diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 371d0616..a20b438e 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -95,6 +95,12 @@ jobs: - megaavr/libraries/ZCD/examples/Interrupt - megaavr/libraries/ZCD/examples/Read_state - megaavr/libraries/ZCD/examples/Simple_ZCD + - megaavr/libraries/PTC/examples/mutualcap + - megaavr/libraries/PTC/examples/mutualcap_low_power + - megaavr/libraries/PTC/examples/self_and_mutual_mix + - megaavr/libraries/PTC/examples/selfcap + - megaavr/libraries/PTC/examples/selfcap_other_nodes_shield + - megaavr/libraries/PTC/examples/selfcap_with_dedicated_shield avr-db-series-sketch-paths: | - megaavr/libraries/Opamp/examples/Inverting_amplifier - megaavr/libraries/Opamp/examples/Inverting_amplifier_with_follower diff --git a/megaavr/libraries/PTC/README.md b/megaavr/libraries/PTC/README.md index 84793f9e..8a96e2ab 100644 --- a/megaavr/libraries/PTC/README.md +++ b/megaavr/libraries/PTC/README.md @@ -13,7 +13,7 @@ An Arduino compatible C library to support the PTC in the AtTiny 1-family as wel For the non-Arduino people: all required files are inside the src folder (No Arduino exclusive functions are used in this library). -## How to use this library +### How to use this library #### Step 1: Memory Initialization @@ -31,7 +31,7 @@ Here are some examples: #### Step 3: Acquisition and Processing -This library requires a regular call to the function `void ptc_process(uint16_t currTime)`. This function handles all background functionality of the library and calls the callback `extern void ptc_event_callback(const uint8_t eventType, cap_sensor_t* node)`, or one of the few sub-callbacks below, when appropriate. First, the function checks if an acquisition was completed (all nodes of the selected type converted). If that's the case, it proceeds to handle the gathered data to handle the respective state machine of each node whose conversion was completed. The more nodes you have, the more time it might take. +This library requires a regular call to the function `void ptc_process(uint16_t currTime)`. This function handles all background functionality of the library and calls the callback `extern void ptc_event_callback(const uint8_t eventType, cap_sensor_t* node)`, or one of the few sub-callbacks below, when appropriate. First, the function checks if an acquisition was completed (all nodes of the selected type converted). If that's the case, it proceeds to handle the gathered data to handle the respective state machine of each node whose conversion was completed. The more nodes you have, the more time it might take. The exact workings of this function will exceed the scope of this document. This function takes an argument, `uint16_t currTime`, to decide when to start the next acquisition. This, compared to having a, as an example, `millis()` inside the library, offers the user significant flexibility on choosing the time source (e.g. TCB.CNT). The Period can be set by `void ptc_set_acqusition_period(uint16_t period)`. Whenever the currTime minus the currTime when the last acquisition trigger happened is greater or equal the period, a new acquisition is started. @@ -60,7 +60,7 @@ There are following events: - `PTC_CB_EVENT_TOUCH_RELEASE`(node: current node): - This event triggers shortly before the state-machine is updated to the "not-touched" state. -You can use a simple switch-case to check for the events in the callback. +You can use a simple switch-case to check for the events in the callback. Another option is to use following "sub-callbacks". As they are weak and aliased with `ptc_event_callback` it is possible to choose between one function to collect all events, or have dedicated function for each event type. - `void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t* node);` - called on `PTC_CB_EVENT_WAKE_*` events @@ -97,7 +97,7 @@ The ptc_ch_bm_t is a typedef that depends on the pincount of the device and rang (Source: [Microchip's PTC Subsystem Firmware User's Guide](https://web.archive.org/web/20221225142000/https://www.mouser.com/pdfdocs/SAMA5D2_PTC_Firmware_UG.pdf)) This schematic was made for a different chip, but it is likely to look similar to this on the AVRs. -Most of the following is a hypothesis based on the publically available documentation and observation. +Most of the following is a hypothesis based on the publicly available documentation and observation. The PTC is using a charge transfer between a variable and a fixed capacitance to measure a difference between those two. The variable capacitance in this case is the sensor electrode and the fixed one is the internal Cc. The neat thing about having a dedicated hardware for this, is that Cc is not really fixed, compared to the Sample-and-Hold capacitor of the normal ADC, it can be calibrated to be about the same as the electrode we want to measure (See below). In order to measure the capacitance of the node, the PTC performs following Steps: @@ -109,32 +109,32 @@ In order to measure the capacitance of the node, the PTC performs following Step The measured voltage depends on what happens around the electrode. In an idle state, 50% of the charge of the node (and thus voltage) is transferred to Cc, however when something conductive is moved towards the electrode, the capacitance will increase slightly. With a higher capacitance, the node will have a higher charge, meaning less then 50% of the charge can be transferred to Cc until they reach an equal Voltage, which means the overall voltage will be higher, which can be measured by the ADC. -If you have trouble understanding: Imagine two equally sized volumes connected trough a valve. You fill the first up with water, open the valve and look on the second, how high the water has risen. After marking the "idle" level, when you put a finger in the water and see the water rise. This new level is the increase in voltage due to an interference. +If you have trouble understanding: Imagine two equally sized volumes connected through a valve. You fill the first up with water, open the valve and look on the second, how high the water has risen. After marking the "idle" level, when you put a finger in the water and see the water rise. This new level is the increase in voltage due to an interference. ### Calibration / Compensation Based on the documentation found online, the PTC has an internal, tunable capacitor connected after the series resistance to ground that is used to compensate the parasitic capacitance of the electrodes. Every node starts with a default compensation value. As soon as the node is enabled, the library attempts to find a compensation setting that will result in an ADC value of about 512 counts (1/2 of ADC resolution). Based on oscilloscope readings, it can also be said that the PTC tries to have a charge of 50% VCC on the electrode when being acquired. This is the also the reason, why the digital input function of the pins is disabled. -The maximum compensation is about 30pF for Mutual-cap and about 50pF for Self-cap. It is possible to get the compensation capacitance with uint16_t ptc_get_node_cc_fempto(cap_sensor_t* node); - however this function has to do a lot of calculations and is thus a bit bloat-y. It will also return the value in fempto farrads, to avoid floats. Read more here: https://www.microchipdeveloper.com/touch:guide-to-interpret-cc-calibration-value +The maximum compensation is about 30pF for Mutual-cap and about 50pF for Self-cap. It is possible to get the compensation capacitance with uint16_t ptc_get_node_cc_femto(cap_sensor_t* node); - however this function has to do a lot of calculations and is thus a bit bloat-y. It will also return the value in femto farrads, to avoid floats. Read more here: https://www.microchipdeveloper.com/touch:guide-to-interpret-cc-calibration-value Different pins have a different parasitic capacitance. I suspect this is depends on the internal circuitry and alternative functions, so it's normal to see some difference with pins next to each other. ### Tuning of nodes In order to ease the use of the PTC module, the ptc_add_* functions will initialize the cap_sensor_t struct with some default values, like the CC value mentioned above. That values can be easily changed and will be applied the next time a conversion of said node starts. Here is a list: - - Analog Gain. Increases the sensitivity of the electrode by adjusting a capacitor on a integrator (I think) (1x Gain) - - Digital Gain. Defines the amount of ADC Oversampling. Will not affect the count value, as it is internally right-shifted. (16x Oversampled) - - Charge Share Delay. Affects the Sample length of the ADC. (0 extra clocks) - - Prescaler. It is possible to slow down the ADC clock by adjusting the Prescaler. (Depends on CPU clock, targeted: 1MHz +/- 25%) - - Serial Resistor. Allows to change the serial resistor between the Cc and the node. Fixed at 100k for Self-Cap. Creates RC-low-pass filter. +- Analog Gain. Increases the sensitivity of the electrode by adjusting a capacitor on a integrator (I think) (1x Gain) +- Digital Gain. Defines the amount of ADC Oversampling. Will not affect the count value, as it is internally right-shifted. (16x Oversampled) +- Charge Share Delay. Affects the Sample length of the ADC. (0 extra clocks) +- Prescaler. It is possible to slow down the ADC clock by adjusting the Prescaler. (Depends on CPU clock, targeted: 1MHz +/- 25%) +- Serial Resistor. Allows to change the serial resistor between the Cc and the node. Fixed at 100k for Self-Cap. Creates RC-low-pass filter. If a node is not sensitive enough, you can increase the Analog Gain (if it becomes too sensitive, an increase of the thresholds might be needed). However it is better to have a bigger node to begin with because the bigger the area, the higher is the capacitance delta. ### Global settings of the State-machine The state-machine, which changes the node's state between Calibration, touch, no touch, etc. uses some variables that are valid for all nodes, those are: - - `uint16_t force_recal_delta`. Each node has a threshold value that is used to calculate the delta. This Threshold value is drifting over time to adjust for environmental changes. If the threshold value drifts 512 +/- this value, a recalibration of CC is performed. Default: 150 - - `uint8_t touched_detect_nom`. Number of consecutive Measurements (Conversions) that are above the touch threshold until the node becomes "touched". Default: 3 - - `uint8_t untouched_detect_nom`. Number of consecutive measurements that are below the no-touch threshold until the node is fully untouched. Default: 3 - - `uint8_t touched_max_nom`. Number of consecutive measurements plus one in the touched state until a recalibration is forced. Can be disabled by writing 255 to it. Default: 200 - - `uint8_t drift_up_nom`. If the delta is higher then the reference, but lower then the threshold, the amount of consecutive measurements plus one, until the reference is incremented. Can be disabled with 255. Default: 20. - - `uint8_t drift_down_nom`. If the delta is below the reference, the amount of consecutive measurements plus one until the reference is decremented. Can be disabled with 255. Default: 20. +- `uint16_t force_recal_delta`. Each node has a threshold value that is used to calculate the delta. This Threshold value is drifting over time to adjust for environmental changes. If the threshold value drifts 512 +/- this value, a recalibration of CC is performed. Default: 150 +- `uint8_t touched_detect_nom`. Number of consecutive Measurements (Conversions) that are above the touch threshold until the node becomes "touched". Default: 3 +- `uint8_t untouched_detect_nom`. Number of consecutive measurements that are below the no-touch threshold until the node is fully untouched. Default: 3 +- `uint8_t touched_max_nom`. Number of consecutive measurements plus one in the touched state until a recalibration is forced. Can be disabled by writing 255 to it. Default: 200 +- `uint8_t drift_up_nom`. If the delta is higher then the reference, but lower then the threshold, the amount of consecutive measurements plus one, until the reference is incremented. Can be disabled with 255. Default: 20. +- `uint8_t drift_down_nom`. If the delta is below the reference, the amount of consecutive measurements plus one until the reference is decremented. Can be disabled with 255. Default: 20. diff --git a/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino b/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino new file mode 100644 index 00000000..777193b5 --- /dev/null +++ b/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino @@ -0,0 +1,60 @@ +#include +/* + * This example creates two different sensing nodes. + * PA4 and PA5 are on the Y-Lines, PA6 is the X-Line. + * PTC_CB_EVENT_TOUCH_DETECT and PTC_CB_EVENT_TOUCH_RELEASE can + * be used for quick actions, like switching a pin or variable, + * but it is recommended to use PTC_CB_EVENT_CONV_MUTUAL_CMPL, as + * otherwise the handling of the successive nodes would be delayed. + */ +#define MySerial Serial + +cap_sensor_t nodes[2]; + +void setup() { + MySerial.begin(115200); + + // this puts the node on the list and initializes to default values + ptc_add_mutualcap_node(&nodes[0], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PA7)); + ptc_add_mutualcap_node(&nodes[1], PIN_TO_PTC(PIN_PA5), PIN_TO_PTC(PIN_PA7)); + + + // Make sure Serial works + MySerial.println("Hello World!"); +} + +void loop() { + ptc_process(millis()); // main ptc task, requires regular calls +} + +// callbacks that are called by ptc_process at different points to ease user interaction +void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node) { + if (PTC_CB_EVENT_TOUCH_DETECT == eventType) { + MySerial.print("node touched:"); + MySerial.println(ptc_get_node_id(node)); + } else if (PTC_CB_EVENT_TOUCH_RELEASE == eventType) { + MySerial.print("node released:"); + MySerial.println(ptc_get_node_id(node)); + } +} + +void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node) { + if (PTC_CB_EVENT_CONV_TYPE_CMPL_MSK & eventType) { + // Do more complex things here + } + (void)node; // remove unused warning +} + +void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node) { + if (PTC_CB_EVENT_ERR_CALIB_LOW == eventType) { + MySerial.print("Calib error, Cc too low."); + } else if (PTC_CB_EVENT_ERR_CALIB_HIGH == eventType) { + MySerial.print("Calib error, Cc too high."); + } else if (PTC_CB_EVENT_ERR_CALIB_TO == eventType) { + MySerial.print("Calib error, calculation timeout."); + } else { + MySerial.print("Calib Successful."); + } + MySerial.print(" Node: "); + MySerial.println(ptc_get_node_id(node)); +} diff --git a/megaavr/libraries/PTC/examples/mutualcap_low_power/mutualcap_low_power.ino b/megaavr/libraries/PTC/examples/mutualcap_low_power/mutualcap_low_power.ino index 5200c04c..90d414a6 100644 --- a/megaavr/libraries/PTC/examples/mutualcap_low_power/mutualcap_low_power.ino +++ b/megaavr/libraries/PTC/examples/mutualcap_low_power/mutualcap_low_power.ino @@ -28,7 +28,7 @@ void setup() { EVSYS.CHANNEL0 = EVSYS_CHANNEL0_RTC_PIT_DIV2048_gc; // The user must select the Event Channel SLPCTRL.CTRLA = SLEEP_MODE_STANDBY | SLPCTRL_SEN_bm; // The deepest we can go is Stand-by - + // Make sure Serial works MySerial.println("Hello World!"); } @@ -43,9 +43,9 @@ void loop() { MySerial.println("Sleepy..."); MySerial.flush(); // wait for transmission to finish - + ptc_lp_init(&lp_node, EVSYS_USER_CHANNEL0_gc); // Will set STARTEI bit to start conversions, needs the desired Event Channel - while (ptc_lp_was_waken() != PTC_LIB_WAS_WAKEN) { + while (ptc_lp_was_waken() != PTC_LIB_WAS_WAKEN) { sleep_cpu(); // Make sure the ADC has woken the CPU, otherwise, go back to sleep. } // The library does not filter wake-ups. After all, something else might have woken the chip. MySerial.println("I'm Awake!"); @@ -55,7 +55,7 @@ void loop() { } // callbacks that are called by ptc_process at different points to ease user interaction -void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node) { uint32_t ms = millis(); if (PTC_CB_EVENT_TOUCH_DETECT == eventType) { // a low-power node, that has waken the chip won't issue a TOUCH_DETECT MySerial.print("node touched:"); @@ -68,14 +68,14 @@ void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { } } -void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_CONV_TYPE_CMPL_MSK & eventType) { // Do more complex things here } (void)node; // remove unused warning } -void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_ERR_CALIB_LOW == eventType) { MySerial.print("Calib error, Cc too low."); } else if (PTC_CB_EVENT_ERR_CALIB_HIGH == eventType) { @@ -90,11 +90,11 @@ void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node MySerial.println(ptc_get_node_id(node)); } -void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_WAKE_TOUCH == eventType) { // True if the node was touched when a wakeup occurred } else if (PTC_CB_EVENT_WAKE_NO_TOUCH == eventType) { // True if the node was no touch when a wakeup occurred } (void)node; // remove unused warning -} \ No newline at end of file +} diff --git a/megaavr/libraries/PTC/examples/self_and_mutual_mix/self_and_mutual_mix.ino b/megaavr/libraries/PTC/examples/self_and_mutual_mix/self_and_mutual_mix.ino index 50fd6a3d..ecf8df29 100644 --- a/megaavr/libraries/PTC/examples/self_and_mutual_mix/self_and_mutual_mix.ino +++ b/megaavr/libraries/PTC/examples/self_and_mutual_mix/self_and_mutual_mix.ino @@ -33,7 +33,7 @@ void loop() { // callbacks that are called by ptc_process at different points to ease user interaction -void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_TOUCH_DETECT == eventType) { MySerial.print("node touched:"); MySerial.println(ptc_get_node_id(node)); @@ -43,7 +43,7 @@ void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { } } -void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_CONV_MUTUAL_CMPL == eventType) { ptc_set_next_conversion_type(NODE_SELFCAP_SHIELD_bm); } else if (PTC_CB_EVENT_CONV_SHIELD_CMPL == eventType) { @@ -52,7 +52,7 @@ void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node) (void)node; // remove unused warning } -void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_ERR_CALIB_LOW == eventType) { MySerial.print("Calib error, Cc too low."); } else if (PTC_CB_EVENT_ERR_CALIB_HIGH == eventType) { diff --git a/megaavr/libraries/PTC/examples/selfcap/selfcap.ino b/megaavr/libraries/PTC/examples/selfcap/selfcap.ino index b5400484..06d90c15 100644 --- a/megaavr/libraries/PTC/examples/selfcap/selfcap.ino +++ b/megaavr/libraries/PTC/examples/selfcap/selfcap.ino @@ -30,7 +30,7 @@ void loop() { // callbacks that are called by ptc_process at different points to ease user interaction -void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_TOUCH_DETECT == eventType) { MySerial.print("node touched:"); MySerial.println(ptc_get_node_id(node)); @@ -40,14 +40,14 @@ void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { } } -void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_CONV_TYPE_CMPL_MSK & eventType) { // Do more complex things here } (void)node; // remove unused warning } -void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_ERR_CALIB_LOW == eventType) { MySerial.print("Calib error, Cc too low."); } else if (PTC_CB_EVENT_ERR_CALIB_HIGH == eventType) { diff --git a/megaavr/libraries/PTC/examples/selfcap_other_nodes_shield/selfcap_other_nodes_shield.ino b/megaavr/libraries/PTC/examples/selfcap_other_nodes_shield/selfcap_other_nodes_shield.ino index 0b2363fc..cefd8c56 100644 --- a/megaavr/libraries/PTC/examples/selfcap_other_nodes_shield/selfcap_other_nodes_shield.ino +++ b/megaavr/libraries/PTC/examples/selfcap_other_nodes_shield/selfcap_other_nodes_shield.ino @@ -31,7 +31,7 @@ void loop() { } // callback that is called by ptc_process at different points to ease user interaction -void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_TOUCH_DETECT == eventType) { MySerial.print("node touched:"); MySerial.println(ptc_get_node_id(node)); diff --git a/megaavr/libraries/PTC/examples/selfcap_with_dedicated_shield/selfcap_with_dedicated_shield.ino b/megaavr/libraries/PTC/examples/selfcap_with_dedicated_shield/selfcap_with_dedicated_shield.ino index 0538d165..e6522c9f 100644 --- a/megaavr/libraries/PTC/examples/selfcap_with_dedicated_shield/selfcap_with_dedicated_shield.ino +++ b/megaavr/libraries/PTC/examples/selfcap_with_dedicated_shield/selfcap_with_dedicated_shield.ino @@ -29,7 +29,7 @@ void loop() { } // callback that is called by ptc_process at different points to ease user interaction -void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_TOUCH_DETECT == eventType) { MySerial.print("node touched:"); MySerial.println(ptc_get_node_id(node)); @@ -39,14 +39,14 @@ void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node) { } } -void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_CONV_TYPE_CMPL_MSK == eventType) { // Do more complex things here } (void)node; // remove unused warning } -void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node) { +void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node) { if (PTC_CB_EVENT_ERR_CALIB_LOW == eventType) { MySerial.print("Calib error, Cc too low."); } else if (PTC_CB_EVENT_ERR_CALIB_HIGH == eventType) { diff --git a/megaavr/libraries/PTC/keywords.txt b/megaavr/libraries/PTC/keywords.txt index 29e70ed2..7babd425 100644 --- a/megaavr/libraries/PTC/keywords.txt +++ b/megaavr/libraries/PTC/keywords.txt @@ -22,6 +22,11 @@ PTC_t KEYWORD1 ptc_process KEYWORD2 ptc_event_callback KEYWORD2 +ptc_event_cb_touch KEYWORD2 +ptc_event_cb_wake KEYWORD2 +ptc_event_cb_conversion KEYWORD2 +ptc_event_cb_calibration KEYWORD2 +ptc_event_cb_error KEYWORD2 ptc_enable_node KEYWORD2 ptc_disable_node KEYWORD2 ptc_set_next_conversion_type KEYWORD2 @@ -33,7 +38,7 @@ ptc_add_selfcap_node KEYWORD2 ptc_add_mutualcap_node KEYWORD2 ptc_suspend KEYWORD2 ptc_resume KEYWORD2 -ptc_get_node_cc_fempto KEYWORD2 +ptc_get_node_cc_femto KEYWORD2 ptc_get_node_xCh_bm KEYWORD2 ptc_get_node_yCh_bm KEYWORD2 ptc_get_node_sensor_value KEYWORD2 @@ -58,18 +63,18 @@ ptc_get_sm_settings KEYWORD2 # Constants (LITERAL1) ####################################### -PTC_CB_EVENT_TOUCH LITERAL1 -PTC_CB_EVENT_WAKE_TOUCH LITERAL1 -PTC_CB_EVENT_WAKE_NO_TOUCH LITERAL1 -PTC_CB_EVENT_CONV_CMPL LITERAL1 +PTC_CB_EVENT_TOUCH LITERAL1 +PTC_CB_EVENT_WAKE_TOUCH LITERAL1 +PTC_CB_EVENT_WAKE_NO_TOUCH LITERAL1 +PTC_CB_EVENT_CONV_CMPL LITERAL1 PTC_CB_EVENT_CONV_MUTUAL_CMPL LITERAL1 -PTC_CB_EVENT_CONV_SELF_CMPL LITERAL1 +PTC_CB_EVENT_CONV_SELF_CMPL LITERAL1 PTC_CB_EVENT_CONV_SHIELD_CMPL LITERAL1 -PTC_CB_EVENT_CONV_CALIB LITERAL1 -PTC_CB_EVENT_ERR_CALIB LITERAL1 -PTC_CB_EVENT_ERR_CALIB_LOW LITERAL1 -PTC_CB_EVENT_ERR_CALIB_HIGH LITERAL1 -PTC_CB_EVENT_ERR_CALIB_TO LITERAL1 +PTC_CB_EVENT_CONV_CALIB LITERAL1 +PTC_CB_EVENT_ERR_CALIB LITERAL1 +PTC_CB_EVENT_ERR_CALIB_LOW LITERAL1 +PTC_CB_EVENT_ERR_CALIB_HIGH LITERAL1 +PTC_CB_EVENT_ERR_CALIB_TO LITERAL1 NODE_TYPE_NOCONV_bm LITERAL1 NODE_MUTUAL_bm LITERAL1 @@ -77,10 +82,3 @@ NODE_SELFCAP_bm LITERAL1 NODE_SHIELD_bm LITERAL1 NODE_SELFCAP_SHIELD_bm LITERAL1 NODE_TYPE_bm LITERAL1 - - - - - - - diff --git a/megaavr/libraries/PTC/library.properties b/megaavr/libraries/PTC/library.properties index 6c6ddc59..2a17413a 100644 --- a/megaavr/libraries/PTC/library.properties +++ b/megaavr/libraries/PTC/library.properties @@ -6,5 +6,5 @@ sentence=This library (experimental) allows a straightforward use of the Periphe paragraph=The PTC contains hardware to assist in touch detection. This library helps taking leverage of this peripheral
Supports only the 1-Series AtTiny chips.
Tested with 1614 so far
feedback appreciated! category=Sensors includes=ptc.h -url=https://github.com/SpenceKonde/DxCore/tree/master/megaavr/libraries/PTC -architectures=megaavr \ No newline at end of file +url=https://github.com/SpenceKonde/DxCore/tree/master/megaavr/libraries +architectures=megaavr diff --git a/megaavr/libraries/PTC/src/ptc.c b/megaavr/libraries/PTC/src/ptc.c index e19cbee7..8254a0d6 100644 --- a/megaavr/libraries/PTC/src/ptc.c +++ b/megaavr/libraries/PTC/src/ptc.c @@ -1,5 +1,5 @@ /* - * Refer to ptc.h file for copyright, changelog, usage and license information + * Refer to ptc.h file for copyright, changelog, usage and license information */ @@ -8,7 +8,7 @@ // The following functions are used internally only. -// get the pointer of the last valid node struct can be "firstNode" +// get the pointer of the last valid node struct can be "firstNode" cap_sensor_t* ptc_get_last_node(void); @@ -54,7 +54,7 @@ volatile ptc_lib_t ptc_lib_state = PTC_LIB_IDLE; /* * Global settings for the state-machine applied on every node. * Pointer to this struct can be retrieved by ptc_get_sm_settings() - * + * */ ptc_lib_sm_set_t ptc_sm_settings = { .force_recal_delta = 150, @@ -73,17 +73,17 @@ ptc_lib_sm_set_t* ptc_get_sm_settings() { #if defined (__PTC_Tiny__) #define PTC_DEFAULT_SC_CC 0x0567 #define PTC_DEFAULT_MC_CC 0x0234 - + const uint8_t ptc_a_gain_lut[] = { - 0x3F, 0x1C, 0x0B, + 0x3F, 0x1C, 0x0B, 0x05, 0x03, 0x01, }; #elif defined (__PTC_DA__) #define PTC_DEFAULT_SC_CC 0x00F0 #define PTC_DEFAULT_MC_CC 0x00A0 - + const uint8_t ptc_a_gain_lut[] = { - 0x1F, 0x0F, 0x07, + 0x1F, 0x0F, 0x07, 0x03, 0x01 }; #endif @@ -110,7 +110,7 @@ __attribute__ ((weak, alias("ptc_event_callback"))) void ptc_event_cb_error(cons // Set the threshold for touch detection and away from touch for a node uint8_t ptc_node_set_thresholds (cap_sensor_t* node, int16_t th_in, int16_t th_out) { - if (NULL == node) + if (NULL == node) return PTC_LIB_BAD_POINTER; node->touch_in_th = th_in; @@ -122,7 +122,7 @@ uint8_t ptc_node_set_thresholds (cap_sensor_t* node, int16_t th_in, int16_t th_o // Change Resistor Setting. Note: Only has an effect on mutual sensors uint8_t ptc_node_set_resistor(cap_sensor_t* node, uint8_t res) { PTC_CHECK_FOR_BAD_POINTER(node); - + if (res > RSEL_MAX) return PTC_LIB_BAD_ARGUMENT; @@ -170,7 +170,7 @@ uint8_t ptc_node_set_gain(cap_sensor_t* node, uint8_t aGain, uint8_t dGain) { #endif if (dGain > 0x06) { - if (__builtin_constant_p(dGain)) + if (__builtin_constant_p(dGain)) badArg("Digital Gain too high. Max Digital Gain Value is 0x06 (equals 64x)"); return PTC_LIB_BAD_ARGUMENT; } @@ -242,12 +242,12 @@ void ptc_init_ADC0(void) { uint8_t ptc_add_node(cap_sensor_t* node, uint8_t* pCh, const uint8_t type) { const uint8_t typesize = sizeof(ptc_ch_arr_t); - + uint8_t hi_node = (uint8_t)((uint16_t)node >> 8); if ((hi_node < (uint8_t)(RAMSTART >> 8)) || (hi_node >= (uint8_t)(RAMEND >> 8))) return PTC_LIB_BAD_POINTER; - - + + uint8_t status = ptc_append_node(node); if (status != PTC_LIB_SUCCESS) return status; @@ -284,12 +284,12 @@ uint8_t ptc_add_node(cap_sensor_t* node, uint8_t* pCh, const uint8_t type) { #endif node->hw_a_d_gain = NODE_GAIN(0, ADC_SAMPNUM_ACC16_gc); - + if (type & NODE_MUTUAL_bm) { node->touch_in_th = 10; node->touch_out_th = 5; node->hw_compCaps = PTC_DEFAULT_MC_CC; /* value from qTouch */ - node->hw_rsel_presc = NODE_RSEL_PRSC(RSEL_VAL_50, PTC_PRESC_DEFAULT); + node->hw_rsel_presc = NODE_RSEL_PRSC(RSEL_VAL_50, PTC_PRESC_DEFAULT); } else { /* Selfcap */ if (yCh == 0) /* not a single pin selected */ return PTC_LIB_BAD_ARGUMENT; @@ -301,7 +301,7 @@ uint8_t ptc_add_node(cap_sensor_t* node, uint8_t* pCh, const uint8_t type) { node->hw_compCaps = PTC_DEFAULT_SC_CC; /* value from qTouch */ node->hw_rsel_presc = NODE_RSEL_PRSC(RSEL_VAL_0, PTC_PRESC_DEFAULT); } - + #if defined(__PTC_Tiny__) PTC.PIN_OVR |= pinmask; uint8_t* pin_lut = (uint8_t* )ptc_ch_to_pin; @@ -345,7 +345,7 @@ uint8_t ptc_add_node(cap_sensor_t* node, uint8_t* pCh, const uint8_t type) { uint8_t ptc_enable_node(cap_sensor_t* node) { PTC_CHECK_FOR_BAD_POINTER(node); - + node->state.disabled = 0; return PTC_LIB_SUCCESS; } @@ -389,7 +389,7 @@ uint8_t ptc_lp_init(cap_sensor_t* node, uint8_t event_ch) { if (NULL != lowPowerNode) return PTC_LIB_WRONG_STATE; /* if called while we are already in low power */ - + #if defined(__PTC_Tiny__) EVSYS.ASYNCUSER1 = event_ch; #elif defined (__PTC_DA__) @@ -434,7 +434,7 @@ uint8_t ptc_lp_was_waken(void) { /* not recommended to use, as the calculation is bloated */ -uint16_t ptc_get_node_cc_fempto (cap_sensor_t* node) { +uint16_t ptc_get_node_cc_femto (cap_sensor_t* node) { if (NULL == node) return 0; @@ -442,7 +442,7 @@ uint16_t ptc_get_node_cc_fempto (cap_sensor_t* node) { uint16_t retVal = 0; uint16_t comp = node->hw_compCaps; for (uint8_t i = 0; i < 3; i++) { - retVal /= 10; /* "skips" last addition */ + retVal /= 10; /* "skips" last addition */ uint8_t temp = comp & 0x0F; retVal += (temp * 675); comp >>= 4; /* select next field */ @@ -658,7 +658,7 @@ void ptc_process_node_sm (cap_sensor_t* node) { uint8_t ptc_process_calibrate (cap_sensor_t* node) { uint16_t rawData = node->sensorData; - + #if defined(__PTC_Tiny__) uint16_t compensation = node->hw_compCaps; uint8_t cc_accurate = compensation & 0x0F; @@ -722,7 +722,7 @@ uint8_t ptc_process_calibrate (cap_sensor_t* node) { } rawData -= 0xCF; } - + while (rawData > 0x0015) { cc_coarse += dir; if (cc_coarse > 0x0F) @@ -738,8 +738,8 @@ uint8_t ptc_process_calibrate (cap_sensor_t* node) { cc_coarse -= dirOvf; } } - - + + while (rawData > 0x0001) { cc_fine += dir; if (cc_fine > 0x0F) @@ -757,7 +757,7 @@ uint8_t ptc_process_calibrate (cap_sensor_t* node) { cc_fine -= dir; if (dir < 0) return PTC_LIB_CALIB_TOO_LOW; - else + else return PTC_LIB_CALIB_TOO_HIGH; } else { cc_coarse -= dirOvf; @@ -802,7 +802,7 @@ uint8_t ptc_process_calibrate (cap_sensor_t* node) { if (delta < 4) return PTC_LIB_CALIB_DONE; - + if (newCC > 0x03FF) { if (newCC > 0x7FFF) { // underflow newCC = 0x00; @@ -824,7 +824,7 @@ void ptc_init_conversion(uint8_t nodeType) { if (ptc_lib_state & (PTC_LIB_CONV_PROG | PTC_LIB_CONV_LP | PTC_LIB_CONV_WCMP | PTC_LIB_SUSPENDED)) return; - + #if defined (__PTC_Tiny__) pPTC->CTRLP = 0x00; if (nodeType & NODE_MUTUAL_bm) { /* NODE_MUTUAL */ @@ -873,8 +873,8 @@ void ptc_init_conversion(uint8_t nodeType) { } else { return; } - - + + uint8_t freq = freq_select; if (nodeType == NODE_MUTUAL_bm) { pPTC->SAMPDLY = 0x00; @@ -889,7 +889,7 @@ void ptc_init_conversion(uint8_t nodeType) { freq = 0x0F; pPTC->SAMPDLY = freq; } - + pPTC->INTFLAGS = ADC_RESRDY_bm | ADC_WCMP_bm; // clear ISR flags, if they were unhandled currConvType = nodeType; @@ -934,7 +934,7 @@ void ptc_set_registers(cap_sensor_t* node) { if (NULL == node) return; - + uint8_t lut_index = 0; if ((node->state.disabled == 0) && (node->stateMachine != PTC_SM_NOINIT_CAL)) { lut_index = node->hw_a_d_gain / 16; // A little workaround as >> 4 is kinda broken sometimes. @@ -953,7 +953,7 @@ void ptc_set_registers(cap_sensor_t* node) { chargeDelay = 0x1F; } pPTC->SAMPCTRL = chargeDelay; - + pPTC->CTRLC = (node->hw_rsel_presc & 0x0F) | ADC_REFSEL_VDDREF_gc; pPTC->CTRLP |= 0x03; @@ -967,7 +967,7 @@ void ptc_set_registers(cap_sensor_t* node) { } else { chargeDelay = 0x1F; } - + pPTC->SAMPCTRL = chargeDelay; pPTC->CTRLC = (node->hw_rsel_presc & 0x0F) | 0x80; @@ -982,7 +982,7 @@ void ptc_set_registers(cap_sensor_t* node) { pPTC->CTRL_SHIELD = 0; } #endif - + pPTC->COMP = node->hw_compCaps; pPTC->AGAIN = analogGain; pPTC->CTRLB = node->hw_a_d_gain & 0x0F; @@ -992,7 +992,7 @@ void ptc_set_registers(cap_sensor_t* node) { if (0 == node->state.low_power) pPTC->COMMAND = 0x01; // Normal operation: Manual Start - else + else pPTC->EVCTRL = 0x01; // Low Power: Start by positive flank on event } @@ -1033,7 +1033,7 @@ uint8_t ptc_append_node(cap_sensor_t* pNewNode) { void ptc_eoc(void) { PTC_t *pPTC; - volatile cap_sensor_t *pCurrentNode; // volatile nedded to pass type check + volatile cap_sensor_t *pCurrentNode; // volatile needed to pass type check _fastPtr_d(pPTC,&PTC); _fastPtr_d(pCurrentNode,currConvNode); @@ -1045,13 +1045,13 @@ void ptc_eoc(void) { uint16_t rawVal = pPTC->RES; // clears ISR flags uint8_t oversampling = pCurrentNode->hw_a_d_gain & 0x0F; pCurrentNode->sensorData = rawVal >> oversampling; - + //currConvNode->sensorData = pPTC->RES_TRUE; pCurrentNode->state.data_ready = 1; if (pCurrentNode->state.low_power) { if (flags & ADC_WCMP_bm) { - pCurrentNode->state.win_comp = 1; + pCurrentNode->state.win_comp = 1; ptc_lib_state = PTC_LIB_CONV_WCMP; } else { pCurrentNode->state.win_comp = 0; @@ -1069,7 +1069,7 @@ ISR(ADC0_RESRDY_vect) { ptc_eoc(); } -ISR(ADC0_WCOMP_vect, ISR_ALIASOF (ADC0_RESRDY_vect)); +ISR(ADC0_WCOMP_vect, ISR_ALIASOF(ADC0_RESRDY_vect)); #elif defined(__PTC_DA__) ISR(PTC_PTC_vect) { diff --git a/megaavr/libraries/PTC/src/ptc.h b/megaavr/libraries/PTC/src/ptc.h index 835ffe7c..9c21fa8a 100644 --- a/megaavr/libraries/PTC/src/ptc.h +++ b/megaavr/libraries/PTC/src/ptc.h @@ -122,7 +122,7 @@ inline uint8_t ptc_add_selfcap_node(cap_sensor_t* node, const ptc_ch_bm_t xCh, c // this places only the significant number of bits on stack. Significantly reduces register pressure on DAs const uint8_t typesize = sizeof(ptc_ch_arr_t); uint8_t pCh[typesize*2]; - + pCh[0] = (uint8_t)(xCh >> 0); pCh[0+typesize] = (uint8_t)(yCh >> 0); @@ -156,7 +156,7 @@ inline uint8_t ptc_add_mutualcap_node(cap_sensor_t* node, const ptc_ch_bm_t xCh, if (xCh == 0) badArg("xCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); if (yCh & xCh) badArg("pin overlap detected"); } - + // this places only the significant number of bits on stack. Significantly reduces register pressure on DAs const uint8_t typesize = sizeof(ptc_ch_arr_t); uint8_t pCh[typesize*2]; @@ -218,12 +218,12 @@ inline uint16_t ptc_get_node_sensor_value(cap_sensor_t* node) { return node->sensorData; } -// returns true, if node is a valid pointer and node is touched, otherwise false. +// returns true, if node is a valid pointer and node is touched, otherwise false. // No other return value so there can be easy checks - not null or null inline uint8_t ptc_get_node_touched(cap_sensor_t* node) { if (node == NULL) return 0x00; - if (node->stateMachine & (PTC_SM_TOUCH_DETECT | PTC_SM_TOUCH_OUT_FLT)) + if (node->stateMachine & (PTC_SM_TOUCH_DETECT | PTC_SM_TOUCH_OUT_FLT)) return 0x01; return 0x00; } @@ -252,7 +252,7 @@ inline uint8_t ptc_get_node_state(cap_sensor_t* node) { inline ptc_id_t ptc_get_node_id(cap_sensor_t* node) { if (node == NULL) return 0xFF; - + return (node->id); } diff --git a/megaavr/libraries/PTC/src/ptc_io.h b/megaavr/libraries/PTC/src/ptc_io.h index 4d09d4f7..f5c1ba35 100644 --- a/megaavr/libraries/PTC/src/ptc_io.h +++ b/megaavr/libraries/PTC/src/ptc_io.h @@ -1,7 +1,7 @@ -/* - * Refer to ptc.h file for copyright, changelog, usage and license information +/* + * Refer to ptc.h file for copyright, changelog, usage and license information */ - + #pragma once #ifndef PTC_IO_H #define PTC_IO_H @@ -82,7 +82,7 @@ typedef struct PTC_struct { register8_t CC_EN; /* 0xCA [0x01] ?? */ register8_t CTRL_BOOST; /* 0xCB [0xFF] set to 0x09 if NODE_SELFCAP_SHIELD_2L */ _DWORDREGISTER(reserved_1); /* 0xCC [0x00] unwritable */ - + register8_t COMMAND; /* 0xD0 [0x01] Command */ register8_t EVCTRL; /* 0xD1 [0x01] Event Control */ register8_t INTCTRL; /* 0xD2 [0x07] Interrupt Control: 0x01 - RESRDY, 0x02 - WINCMP */ @@ -103,7 +103,7 @@ typedef struct PTC_struct { _WORDREGISTER(COMP); /* 0xE4 [0x3FF] Compensation */ register8_t AGAIN; /* 0xE6 [0x1F] Analog Gain */ register8_t reserved_6; /* 0xE7 [0x00] unwritable */ - + register8_t XBM[6]; /* 0xE8 X Channel bitmask */ register8_t reserved_7[2]; /* 0xEE [0x00] unused */ @@ -395,7 +395,7 @@ static const ptc_ch_bm_t digital_pin_to_ptc_bm [] = { // lookup-table to quickly disable input and pull-up. PTC_Tiny only static const uint8_t ptc_ch_to_pin [] = { #if (__PTC_Pincount__ <= 14) - PORTA_ISC(4), + PORTA_ISC(4), PORTA_ISC(5), PORTA_ISC(6), PORTA_ISC(7), @@ -408,7 +408,7 @@ static const uint8_t ptc_ch_to_pin [] = { PORTC_ISC(3), PORTC_ISC(4), // 20 pin parts: writing to this location will have no effect, but likely pre-filtered by PIN_TO_PTC anyway PORTC_ISC(5), // 20 pin parts: writing to this location will have no effect, but likely pre-filtered by PIN_TO_PTC anyway - + PORTB_ISC(5), /* X12 / Y12 */ PORTB_ISC(6), #endif diff --git a/megaavr/platform.txt b/megaavr/platform.txt index 91bc379f..6deb8b1d 100644 --- a/megaavr/platform.txt +++ b/megaavr/platform.txt @@ -41,7 +41,7 @@ compiler.c.elf.flags={compiler.warning_flags} -Os -g -flto -fuse-linker-plugin - compiler.c.elf.cmd=avr-gcc compiler.S.flags=-c -g -x assembler-with-cpp -flto -MMD compiler.cpp.cmd=avr-g++ -compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++17 -ffunction-sections -fdata-sections -fshort-enums -flto -mrelax -MMD -fpermissive -Wno-sized-deallocation -fno-exceptions -fno-threadsafe-statics -Wno-error=narrowing +compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++17 -ffunction-sections -fdata-sections -fshort-enums -flto -mrelax -MMD -fpermissive -Wno-sized-deallocation -fno-exceptions -fno-threadsafe-statics -Wno-error=narrowing compiler.ar.cmd=avr-gcc-ar compiler.ar.flags=rcs compiler.objcopy.cmd=avr-objcopy