diff --git a/ContributionGuidelines.md b/ContributionGuidelines.md index 6f369522..013eb586 100644 --- a/ContributionGuidelines.md +++ b/ContributionGuidelines.md @@ -233,7 +233,7 @@ But it looks like it works as long as the bitwise and is non-zero. There's no re * macros DO NOT CHECK TYPES; your macros that are exposed to user code library code must be hardened to take stupid types passed to them. Remember that while the core uses uint8_t's for all pin identifiers, code in the wild frequently follows the boneheaded lead of the Arduino IDE and uses signed 16-bit integers. A bug discovered in early 2023 involved the fact that if certain pin information macros were called with a signed value, the math where we checked if it was a valid pin could fuck up; we were checking if pin >= NUM_DIGITAL_PINS. When the values are unsigned, this works because -1 = 255 > pin whenever pin is valid - a single test can be used to catch both known NOT_A_PIN and other invalid pins not yet identified as much. But if a signed integer was passed to the macro, the default signed integer type would remain in use for the comparison with 0. So any negative number would pass through the test, and down the line would cause us to read from a memory location that was not part of the table we were looking values up in. If there is anything type sensitive, explicitly cast it. * Because of this, in macros, always use 255 if for some reason you have to write out the value of the NOT_A_whateveritis error values, not -1, even though you know it will end up in a uint8_t. There's no advantage to -1, but though you may know what type it will eventually have, you do not know the path it will take to get there, which may involve multiple data types. -### Performance and binary size matter. +### Performance and binary size matter Particularly within the core API, performance matters. That means that things like division and modulo are bad, especially on large datatypes. Division and modulo both run the same routines. Approx time cost of math (how the variables are initialized may have an impact of +/- 2 x (size of datatype), so this is not gospel. The time required for division shown below is an average, since that routine does not execute in a fixed period of time. Large numerators and small denominators make it worse - as you would expect, computing x / y where x < y is highly favorable. Notice that switching to 64-bit types from 32-bit ones more than doubles the execution time - this is due to register pressure. It is likely to actually have an even larger effect in practice if there are other variables that need to be swapped out of working registers and reloaded.* | Math Operation | uint8_t | uint16_t | uint32_t | uint64_t | float | diff --git a/README.md b/README.md index 8ded849b..111be1eb 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This corrects many of the bugs present in 2.6.x ### ATTN: Linux Users **Only versions of the Arduino IDE downloaded from [arduino.cc](https://arduino.cc) should be used, NEVER from a linux package manager. The package managers often have the Arduino IDE - but have *modified it*. This is despite their knowing nothing about Arduino or embedded development in general, much less what they would need to know to modify it successfully** Those version are notorious for subtle but serious issues caused by these unwise modifications. This core should not be expected to work on such versions, and no modifications will be made for the sake of fixing versions of the IDE that come from package managers for this reason. -### IDE Versions 2.0.x and some 2.1 do not work correctly. +### IDE Versions 2.0.x and some 2.1 do not work correctly This is a bug in the Arduino client. IDE versions between 1.8.13 and 2.x developed significant novel defects. IDE versions 1.8.2 and earlier , however, possess crippling unfixed defects. I believe that they finally have a working version of the IDE out, and I believe that latest is able to install my core correctly. @@ -24,7 +24,7 @@ Prior to megaTinyCore 2.6.0, manual installation of megaTinyCore would cause V1. I buy a lot of electronics stuff on AliExpress. It's a great marketplace for things that are made by Chinese companies and are mostly generic, including loads of components unavailable to individuals in the global West any other way (ex, min order is a reel or something like that - if you can even find a component vendor that works with the no-name chinese chip maker). It is not a great place for the latest semiconductor product lines from major Western manufacturers, especially in the midst of a historic shortage of said chips. The modern AVR devices, when they are available through those channels at all, are **frequently reported to be fake or defective** (like ATtiny412s that think they're 416s and may not correctly execute power on reset). For that matter, **you probably don't want to buy any AVR microcontrollers on AliExpress**... Assembled boards, like Arduino Nano clones, generally work if you avoid the ones with the third party LGT8 chips and watch out for the ones with the ATmega168p instead of the '328p - but there are a lot of reports of bogus microcontrollers when they're sold as bare chips (I have heard of fake ATtiny85s that were actually remarked ATtiny13s; it's not just modern AVRs that get faked). There are a lot of interesting theories for where those bogus chips came from, and Microchip has remained totally silent on the issue. ## Using This Document -This document is best viewed online (as opposed to opening the markdown file in your favorite text editor), so that links are clickable and inline images are shown, and probably most importantly, to make tables render correctly sometimes. Again, this [document can be found on github](https://github.com/SpenceKonde/megaTinyCore](https://github.com/SpenceKonde/megaTinyCore) +This document is best viewed online (as opposed to opening the markdown file in your favorite text editor), so that links are clickable and inline images are shown, and probably most importantly, to make tables render correctly sometimes. Again, this [document can be found on github](]() ## Getting Started ### [Wiring](Wiring.md) diff --git a/megaavr/extras/PlatformIO.md b/megaavr/extras/PlatformIO.md index 6ba18934..427c8a20 100644 --- a/megaavr/extras/PlatformIO.md +++ b/megaavr/extras/PlatformIO.md @@ -227,7 +227,7 @@ Programmer used for uploading. | `powerdebugger_updi` | Power Debugger in UPDI mode | | `serialupdi` | Serial UPDI based on pymcuprog, includes improvements taken from the DxCore implementation | -**Note that this is not an exhaustive list**. +**Note that this is not an exhaustive list**. The full list of supported programmers is accessible via the `avrdude -c ?` command. ### `upload_flags` @@ -243,7 +243,7 @@ upload_flags = ### Using SerialUPDI from platformIO It has been reported that, added to platformio.ini, the following works for making platformIO use SerialUPDI, but requires `python3` in the path. This is also a very aggressive configuration, and you may have better luck if you back off the speed to 115200 baud. UPDI does autobaud - you just want to make sure you pick a speed that the hardware will handle. 115200 works almost always, higher speeds are more demanding of how the UPDI circuit is wired up to meet required rise and fall times. -However, note that this is probably obsolete and in most cases the same functionality can be achieved simply by setting `upload_protocol = serialupdi`, unless you have a specific reason to use this core's implementation. +However, note that this is probably obsolete and in most cases the same functionality can be achieved simply by setting `upload_protocol = serialupdi`, unless you have a specific reason to use this core's implementation. ```ini [env:ATtiny1614] platform = atmelmegaavr @@ -252,18 +252,18 @@ framework = arduino platform_packages = platformio/framework-arduino-megaavr-megatinycore@https://github.com/SpenceKonde/megaTinyCore ; Fill in the following as per your hardware setup -upload_port = +upload_port = upload_speed = 230400 -upload_flags = - --tool - uart - --device - $BOARD - --uart - $UPLOAD_PORT - -b - $UPLOAD_SPEED +upload_flags = + --tool + uart + --device + $BOARD + --uart + $UPLOAD_PORT + -b + $UPLOAD_SPEED upload_protocol = custom upload_command = python3 ${platformio.packages_dir}/framework-arduino-megaavr-megatinycore/tools/prog.py $UPLOAD_FLAGS -f$SOURCE -a write diff --git a/megaavr/extras/Ref_Digital.md b/megaavr/extras/Ref_Digital.md index ceb6b8a6..2d44b906 100644 --- a/megaavr/extras/Ref_Digital.md +++ b/megaavr/extras/Ref_Digital.md @@ -20,7 +20,7 @@ This core includes a number of features to provide more control or performance w ## But first, about the hardware -As there is a good chance you;ve noticed, a lot of hardware is better at driving pins low than high (sinking vs sourcing). Classic AVRs supposedly had symmetric drive - they were very similar in their ability to source and sink current (this varied by part though - for comparison below I used the last classic AVR that came out in it's fully evolved form, the 328PB. But ever since they've been publishing graphs, it's been clear the drive strength is not symmetric. +As there is a good chance you;ve noticed, a lot of hardware is better at driving pins low than high (sinking vs sourcing). Classic AVRs supposedly had symmetric drive - they were very similar in their ability to source and sink current (this varied by part though - for comparison below I used the last classic AVR that came out in it's fully evolved form, the 328PB. But ever since they've been publishing graphs, it's been clear the drive strength is not symmetric. The pretense - in the form of matching specs in the electrical characteristics table - was abandoned on the Dx-series parts, though it mysteriously returned in the Ex-series, though none have gotten graphs published yet... This summarizes the differences in digital I/O capability (not counting pincount and peripheral differences) @@ -32,32 +32,32 @@ The EB specs 0.4V max of drop, symmetric, at 6mA and Vdd = 3.0V. But as with the | Abs max injection | +/-1 ma |+/-15mA |+/-15mA |+/-20 | +/-20 | | .. @ Vdd > 4.9 | (rumor) |+/-1 mA |+/- 1mA | | - | | IOH,max from graph | 20 mA | | 20 mA | 50 mA | tbd**| Sinking Current Current shown for highest voltage shown on the highest operating voltage chart (the most favorable) -| VOH,min from graph | 4.45V | | 4.3V | 3.5 V | tbd**| Voltage shown corresponding to that point. Both are at the "25 C mean" line. +| VOH,min from graph | 4.45V | | 4.3V | 3.5 V | tbd**| Voltage shown corresponding to that point. Both are at the "25 C mean" line. | IOL,max from graph | 20 ma | | 20 mA | 100mA | tbd**| These taken together imply that the tiny2 is about 62% better at sinking current than sourcing it (as measured by voltage drop) | VOL,max from graph | 0.45V | | 0.43 | 1.4V | tbd**| while the Dx is 185% better at sinking current than sourcing it, under extreme conditions. Who the hell tests and gives graphs to 2x the abs max? Love it! -| Rpullup | ~35k | ~35k | ~35k | ~35k | 32k | -| INLVL supported | No | No | No | Yes* | Yes | -| Fully Async Pins | 2, 6 | 2, 6 |2,6/All | All | From the DD onward, all pins are treated as "fully async". It means you can trigger on super short pulses (and noise), and wake on rise/fall, not just change/low. On all pins. +| Rpullup | ~35k | ~35k | ~35k | ~35k | 32k | +| INLVL supported | No | No | No | Yes* | Yes | +| Fully Async Pins | 2, 6 | 2, 6 |2,6/All | All | From the DD onward, all pins are treated as "fully async". It means you can trigger on super short pulses (and noise), and wake on rise/fall, not just change/low. On all pins. | PINCONTROL bulk-set | n/a | No | No | Yes | Yes | The PINCONTROL bulk-PINnCTRL writing registers are only on Dx/Ex, where there may be a lot more unused pins. -| EVGENCTRL | n/a | No | No | DU | Yes | The DU, being rather late (notice how two of the E's cut in front of it), did manage to pick up the EVGENCTRL features (user facing only if not using included Event.h library. It's an improvement, as it makes the event channels, finally, really -| Those special pins | - | - | - | - | - | UPDI and reset have various levels of functionality on different parts. -| UPDI pin | n/a | PA0 | PA0 | PF7* | PF7 | -| Pin for HV pulse | Reset | PA0 | PA0 | PF6* | PF6 | This is the pin that the HV pulse for reprogramming a UPDI pin set to be non UPDI ust be sent to. If it can be used as GPIO, the pin drive is ~1/30th that of other pins. -| Pin PA0 can be GPIO | *** | Hard | Hard | n/a | n/a | Nothing special about PA0 on Dx/Ex, other than that if there's a crystal somewhere, it's likely on those pins. +| EVGENCTRL | n/a | No | No | DU | Yes | The DU, being rather late (notice how two of the E's cut in front of it), did manage to pick up the EVGENCTRL features (user facing only if not using included Event.h library. It's an improvement, as it makes the event channels, finally, really +| Those special pins | - | - | - | - | - | UPDI and reset have various levels of functionality on different parts. +| UPDI pin | n/a | PA0 | PA0 | PF7* | PF7 | +| Pin for HV pulse | Reset | PA0 | PA0 | PF6* | PF6 | This is the pin that the HV pulse for reprogramming a UPDI pin set to be non UPDI ust be sent to. If it can be used as GPIO, the pin drive is ~1/30th that of other pins. +| Pin PA0 can be GPIO | *** | Hard | Hard | n/a | n/a | Nothing special about PA0 on Dx/Ex, other than that if there's a crystal somewhere, it's likely on those pins. | .. can be Reset | n/a | Possible| Yes | n/a | n/a | as above | .. can be UPDI | n/a | Default |Default | n/a | n/a | as above -| Pin PB4 can be RST | n/a | No | Yes | No | No | Note that 14-pin parts don't have a PB4. -| Pin PF6 can be RST | n/a | n/a | n/a | Yes | Yes | -| .. can be GPIO | n/a | n/a | n/a | No | No | On no modern AVR with a PF6 is the pin usable as an output. -| .. can be GPI | n/a | n/a | n/a | see | --> | Yes. And this is the DEFAULT setting according to Microchip, but on these parts, it's a "harmless" fuse that gets set as specified on all updi uploads. +| Pin PB4 can be RST | n/a | No | Yes | No | No | Note that 14-pin parts don't have a PB4. +| Pin PF6 can be RST | n/a | n/a | n/a | Yes | Yes | +| .. can be GPIO | n/a | n/a | n/a | No | No | On no modern AVR with a PF6 is the pin usable as an output. +| .. can be GPI | n/a | n/a | n/a | see | --> | Yes. And this is the DEFAULT setting according to Microchip, but on these parts, it's a "harmless" fuse that gets set as specified on all updi uploads. | Pin PF7 can be UPDI | n/a | n/a | n/a | Default | Default | -| Pin PF7 can be GPIO | n/a | n/a | n/a | Yes* | Yes | New in +| Pin PF7 can be GPIO | n/a | n/a | n/a | Yes* | Yes | New in `*` In the DA-series, there was no INLVL. In the DA and DB-series, although it obviously was PF7, until there was a way to enable it for general I/O in the DDs, they never called it that. On the DA and DB families, PF7 cannot be made GPIO, and it's only ever UPDI. Since the DD that is no longer the case, and seems to be partodf their standard features now -`**` - It remains to be seen what the pin drive strength will be like on the Ex-series or any other future part, and until the IO pin output current is added to the characteristics graphs section of the datasheet, you don't really have much information; preliminary datasheets typically omit this sort of data. Right now in the EA-series datasheets, we still have a specification of Minimum, Typical, and Maximum VOH and VOL of "-", "-", and "-" (though they helpfully note that the test conditions are 6mA at VDD = 3.0V; presumably yet to be conducted). Now, one really has to wonder how it takes them so long to characterize the behavior of an I/O pin. I understand how complex peripherals would be hard to test. And subtle ISA bugs like the cursed write sequence that the compiler would be perfectly within it's rights to emit but never does, nobody noticed that for like 5 years or more. But an I/O pin is pretty simple. You put a controlled load on it, and change the load while measuring the voltage on the pin. It's the sort of thing that I'm sure, nowadays, would be easy for anyone well versed in microcontrollers to automate, especially if they had to do that sort of thing, oh, every time they released a product? I'm sure Microchip has some employees who are quite knowledgable about microcontrollers. I sure hope they do. Like, I'd expect new figures to be automatically generated by the automated testing on each new internal development rev that comes ot of the fab. +`**` - It remains to be seen what the pin drive strength will be like on the Ex-series or any other future part, and until the IO pin output current is added to the characteristics graphs section of the datasheet, you don't really have much information; preliminary datasheets typically omit this sort of data. Right now in the EA-series datasheets, we still have a specification of Minimum, Typical, and Maximum VOH and VOL of "-", "-", and "-" (though they helpfully note that the test conditions are 6mA at VDD = 3.0V; presumably yet to be conducted). Now, one really has to wonder how it takes them so long to characterize the behavior of an I/O pin. I understand how complex peripherals would be hard to test. And subtle ISA bugs like the cursed write sequence that the compiler would be perfectly within it's rights to emit but never does, nobody noticed that for like 5 years or more. But an I/O pin is pretty simple. You put a controlled load on it, and change the load while measuring the voltage on the pin. It's the sort of thing that I'm sure, nowadays, would be easy for anyone well versed in microcontrollers to automate, especially if they had to do that sort of thing, oh, every time they released a product? I'm sure Microchip has some employees who are quite knowledgeable about microcontrollers. I sure hope they do. Like, I'd expect new figures to be automatically generated by the automated testing on each new internal development rev that comes out of the fab. -`***` - On a Tiny85 or Tiny84/841, HVSP really doesn't seem too bad - sure it was way more of a pain than normal ISP, but normal ISP kinda sucks in most ways compared to UPDI. It's similar to a modern AVR with UPDI disabled, and I'd call it "Hard". But only those three part families use HVSP. Everything else takes HVPP to reprogram after reset is disabled, and HVPP requires connection around 20 wires to the target (with 20 chances to make a mistake), most of them can't be loaded so you can just forget about doing it in system. I do not recall reading any account on forums of someone using HVPP outside of work, and those were rare. HVPP is much harder than "Hard". +`***` - On a Tiny85 or Tiny84/841, HVSP really doesn't seem too bad - sure it was way more of a pain than normal ISP, but normal ISP kinda sucks in most ways compared to UPDI. It's similar to a modern AVR with UPDI disabled, and I'd call it "Hard". But only those three part families use HVSP. Everything else takes HVPP to reprogram after reset is disabled, and HVPP requires connection around 20 wires to the target (with 20 chances to make a mistake), most of them can't be loaded so you can just forget about doing it in system. I do not recall reading any account on forums of someone using HVPP outside of work, and those were rare. HVPP is much harder than "Hard". ### The tinies aren't symmettric drive? You might be scurrying to your datasheets thinking "I swear they were symmetric on the tinys!" and if you look at the table - which gives worst case values, they are. But the graphs, which give observed "typical" values, tell a different story - they show a voltage drop when sourcing 20mA on a tiny2 of around 0.7V at room temp, but a voltage drop of 0.4-0.45 on at sinking the same amount of current. The low side drivers are stronger. The Dx-series takes it to a new level: At max opperating voltage (5.5v) they ran it up to the absolute max on the high side (recording a 2 volt drop). On the low side though, at the 50mA absolute max, they were still only seeing 0.6V. So they ran it up to twice the absolute maximum rating, which saw a 1.4V drop. Though it's hardly novel for the graphs in the datasheet to show that that the parts typically blow their rated specs out of the water, at least briefly or in favorable conditions (as in when being pushed very hard) or under normal, mild conditions; no particular AVR example comes to mind, but my preferred 1117-series regulator specs the usual dropout as a maximum. The typical characteristics graphs, in contrast, show dropout on the order of a tenth of a volt at light loads. I appreciate the transparency on behavior in overload conditions. I wish they did that more. @@ -69,10 +69,10 @@ What tinyAVR calls current injection, Dx/Ex-series call "Clamp current"; the lat Anyway, the takeaways I intended from this are: * The I/O pins we have are a hell of a lot better than the ones classic had, and they're getting better -* The I/O pins on the Dx-series in particular are spectacularly strong, and they have greatly improved resiliance to clamp current across the board. -* Pin drive is not symmetric. This has ramifications for the design constraints on an RC filter used to make analog voltages out of PWM. +* The I/O pins on the Dx-series in particular are spectacularly strong, and they have greatly improved resilience to clamp current across the board. +* Pin drive is not symmetric. This has ramifications for the design constraints on an RC filter used to make analog voltages out of PWM. -## Now we can talk about the digital I/O features supplied by the core +## Now we can talk about the digital I/O features supplied by the core (murmurs of annoyance from the audience 'about damn time!') * We supply the classics, which work as you know them to: @@ -81,21 +81,21 @@ Anyway, the takeaways I intended from this are: * digitalRead(uint8_t pin) - returns a uint8_t, not an int. **Does not turn off PWM when used on a pin currently outputting PWM, unlike the default core. Do you really want all the turnOffPWM overhead? Does it even make sense that reading will cancel an effect you enabled with a function ending in "Write"?** * analogWrite(uint8_t pin, uint8_t duty) - *That's not digital* "Well I can't very well talk about the ovewrhead without bringing up PWM, as you'll see." * There has always been an internal function by this name, which does exactly what it sounds like. For reasons unclear to me, it was hidden. - * turnOffPWM(uint8_t pin) - turns off PWM on the pin passed to it, if it's a PWM pin. Otherwise does nothing but burn a lot of clock cycles, especially on Dx-series. Mostly used internally, and you just digitalWrite() the pin for the same effect. + * turnOffPWM(uint8_t pin) - turns off PWM on the pin passed to it, if it's a PWM pin. Otherwise does nothing but burn a lot of clock cycles, especially on Dx-series. Mostly used internally, and you just digitalWrite() the pin for the same effect. * And we have added a bunch of new API extensions... - * openDrain(uint8_t pin, uint8_t state) - State = LOW or FLOATING (HIGH will work, but is not recommended). Leaves the port output value at 0 (and sets it to that if it isn't there already), and just changes the direction. - * digitalWriteFast(uint8_t pin, uint8_t state) - arguments must be compile-time known foldable constants (compile errors saying as much if you try), but if they are, these are extremely fast and lightweight. And let's face it, this covers a lot of real situations. - * digitalReadFast(uint8_t pin) - As above; in a construction like `if(digitalReadFast(PINNUMBER)){...}` may use only a single clock cycle on the test. - * pinModeFast(uint8_t pin, uint8_t mode) - as above. Performance and overhead suffer significantly if mode not constant. - * openDrainFast(uint8_t pin, uint8_t state) - as above. + * openDrain(uint8_t pin, uint8_t state) - State = LOW or FLOATING (HIGH will work, but is not recommended). Leaves the port output value at 0 (and sets it to that if it isn't there already), and just changes the direction. + * digitalWriteFast(uint8_t pin, uint8_t state) - arguments must be compile-time known foldable constants (compile errors saying as much if you try), but if they are, these are extremely fast and lightweight. And let's face it, this covers a lot of real situations. + * digitalReadFast(uint8_t pin) - As above; in a construction like `if(digitalReadFast(PINNUMBER)){...}` may use only a single clock cycle on the test. + * pinModeFast(uint8_t pin, uint8_t mode) - as above. Performance and overhead suffer significantly if mode not constant. + * openDrainFast(uint8_t pin, uint8_t state) - as above. * pinConfigure(uint8_t pin, uint16_t pin_config) - wrapper around all things pin maniupulation related, allowing direction and output value to be set along with all the options in the PINnCFG register. -* We have applied some policies to all of these fnctions - * Things like digitalWrite() or pinMode() that get an argument where only a small number of options are valid, if you pass a compiletime known constant that isn't valid, you'll get a compile error telling you as much. - * All things that take a pin number as an argument, if that argument is compiletime known constant, and is invalid (eg, pin doesn't exist), you'll be told as much through a compile error +* We have applied some policies to all of these functions + * Things like digitalWrite() or pinMode() that get an argument where only a small number of options are valid, if you pass a compile time known constant that isn't valid, you'll get a compile error telling you as much. + * All things that take a pin number as an argument, if that argument is compile time known constant, and is invalid (eg, pin doesn't exist), you'll be told as much through a compile error * This is consistent with my philosophy that "There is no way this line could ever not be a bug" - such as telling a pin to do something that didn't correspond to anything a pin could do is a valid and appropriate reason to generate a compile error. * There are many ways to outfox these "badArg" errors, since they only work on foldable compile-time-known constants. But they help to quickly catch the stupid bugs - bad tab completions, constant pin assignments copy/pasted from code for other modern AVRs, etc -* We discourage referrign to pins by numbers. PIN_PA1, PIN_PB3 is preferred. If they're not defined at compile time, you're compiling for the wrong part or are trying to use ports you don't have. - * It's also a great help when porting between modern AVRs, as the same peripherals are found on the same port pins +* We discourage referrign to pins by numbers. PIN_PA1, PIN_PB3 is preferred. If they're not defined at compile time, you're compiling for the wrong part or are trying to use ports you don't have. + * It's also a great help when porting between modern AVRs, as the same peripherals are found on the same port pins @@ -146,7 +146,7 @@ About 200 bytes of that space is used for lookup tables, not instructions. This is why the fast digital I/O functions exist, and why there are people who habitually do `VPORTA.OUT |= 0x80;` instead of `digitalWrite(PIN_PA7, HIGH);` -It's also why I am not comfortable automatically switching digital I/O to "fast" type when constant arguments are given. The normal functions are just SO SLOW that you're sure to break code that hadn't realized they were depending on the time it took for digital write, even if just for setup or hold time for the thing it's talking to. And yes, I absolutely have seen an example of the code where they used super short delays amongst digitalWrites(), and the result was totally different than what their comments described. In their case, the whole thing was a kludge, so it was compensated for by an arbitrary fudge factor somewhere (IIRC there were several of those - what was it, making a tiny85 or something act like a +It's also why I am not comfortable automatically switching digital I/O to "fast" type when constant arguments are given. The normal functions are just SO SLOW that you're sure to break code that hadn't realized they were depending on the time it took for digital write, even if just for setup or hold time for the thing it's talking to. And yes, I absolutely have seen an example of the code where they used super short delays amongst digitalWrites(), and the result was totally different than what their comments described. In their case, the whole thing was a kludge, so it was compensated for by an arbitrary fudge factor somewhere (IIRC there were several of those - what was it, making a tiny85 or something act like a ## openDrain() It has always been possible to get the benefit of an open drain configuration - you set a pin's output value to 0 and toggle it between input and output. This core provides a slightly smoother (also faster) wrapper around this than using pinmode (since pinMode must also concern itself with configuring the pullup, whether it needs to be changed or not, every time - it's actually significantly slower setting things input vs output. The openDrain() function takes a pin and a value - `LOW`, `FLOATING` (or `HIGH`) or `CHANGE`. `openDrain()` always makes sure the output buffer is not set to drive the pin high; often the other end of a pin you're using in open drain mode may be connected to something running from a lower supply voltage, where setting it OUTPUT with the pin set high could damage the other device. diff --git a/megaavr/extras/Ref_Reset.md b/megaavr/extras/Ref_Reset.md index 44a90531..23ae92cb 100644 --- a/megaavr/extras/Ref_Reset.md +++ b/megaavr/extras/Ref_Reset.md @@ -52,8 +52,8 @@ The reset sources are listed below. Note that for brevity and familiarity to tho | UPDI Reset | RSTCTRL_UPDIRF_bm | 1 << 5 | 0x20 | UPDI programming | POR, BOR (by hw), manually | | Software Reset (SWR) | RSTCTRL_SWRF_bm | 1 << 4 | 0x10 | When SWR used | .. | | Watchdog Reset (WDR) | RSTCTRL_WDRF_bm | 1 << 3 | 0x08 | When WDT expires w/out wdr instruction
wdr executed before window in windowed mode| POR, BOR (by hw), manually | -| External Reset | RSTCTRL_EXTRF_bm | 1 << 2 | 0x04 | When reset pin is brought LOW | POR, BOR, Manually | -| Brownout Reset (BOR) | RSTCTRL_BORF_bm | 1 << 1 | 0x02 | When Vdd lower than BOD threshold and BOD enabled. | POR, manually. resers all flags except POR. +| External Reset | RSTCTRL_EXTRF_bm | 1 << 2 | 0x04 | When reset pin is brought LOW | POR, BOR, Manually | +| Brownout Reset (BOR) | RSTCTRL_BORF_bm | 1 << 1 | 0x02 | When Vdd lower than BOD threshold and BOD enabled. | POR, manually. resers all flags except POR. | Power on reset (POR) | RSTCTRL_PORF_bm | 1 | 0x01 | On power on (Vdd goes above VPOR from below VPORR) | Manually. Resets all other reset flags * All of these resets restore the peripheral registers [(SFRs)](https://github.com/SpenceKonde/AVR-Guidance/blob/master/Glossary.md#sfr) to their power on state - with the exception of the reset flag register, amd a small number of things that ONLY POR resets: @@ -69,42 +69,42 @@ The reset sources are listed below. Note that for brevity and familiarity to tho | WDRF | UPDIRF| SWRF | EXTRF | PORF | BORF | Meaning, Suggested response |------|-------|------|-------|------|------|------------- | 0 | 0 | 0 | 0 | 0 | 0 | (Unless you are reinventing this wheel) You are reading RSTFR, but the core already read that and cleared it; we leave the value in the 6 LSB of GPIOR0. -| 0 | 0 | 0 | 0 | 0 | 0 | (No wheel reinvention, GPIOR0 & 0x3F == 0 at start of setup()) - Should be impossible without disabling reset cause clearing (see "Note"), otherwise create a new issue showing code that reproduces this issue; this should not occur and constitutes a serious bug. -| 0 | 0 | 0 | 0 | 0 | 0 | (Reinventing the reset cause clearing wheel, and found RSTFR = 0 after apparent reset). Reset is dirty, hardware in unknown state, "guaranteed not to work". Fire software reset immediately. +| 0 | 0 | 0 | 0 | 0 | 0 | (No wheel reinvention, GPIOR0 & 0x3F == 0 at start of setup()) - Should be impossible without disabling reset cause clearing (see "Note"), otherwise create a new issue showing code that reproduces this issue; this should not occur and constitutes a serious bug. +| 0 | 0 | 0 | 0 | 0 | 0 | (Reinventing the reset cause clearing wheel, and found RSTFR = 0 after apparent reset). Reset is dirty, hardware in unknown state, "guaranteed not to work". Fire software reset immediately. | X | 1 | X | X | X | X | The most recent reset was caused by exiting UPDI programming mode. All other reset flags should probably be ignored. Start per application requirements as a "hot start" -| 1 | 0 | 0 | 0 | 0 | 0 | Code was previously executing. WDT was enabled, and the timeout expired without wdr being executed (or executed wdr too soon in windowed mode). React per application requirements as a "hot start". -| 0 | 0 | 1 | 0 | 0 | 0 | Code was previously executing, until a software reset was issued. If not expected, likely resulted from dirty reset; investigate as such. If expected, react per application requirements as a "hot start". +| 1 | 0 | 0 | 0 | 0 | 0 | Code was previously executing. WDT was enabled, and the timeout expired without wdr being executed (or executed wdr too soon in windowed mode). React per application requirements as a "hot start". +| 0 | 0 | 1 | 0 | 0 | 0 | Code was previously executing, until a software reset was issued. If not expected, likely resulted from dirty reset; investigate as such. If expected, react per application requirements as a "hot start". | 1 | 0 | 0 | 1 | 0 | 0 | As above. While WDR triggered, reset pin asserted and released. Likely WDR is retriggering repeatedly, and the reset button fixed nothing. If powercycling then fixed, suspect "hot start" issue. | X | 0 | 1 | X | 1 | X | Can't happen unless reset cause clearing has definitely been disabled. See "Note" | X | 0 | 1 | X | 0 | 1 | Can't happen unless reset cause clearing is broken badly. See "Note" | 1 | 0 | X | X | 1 | X | Can't happen unless reset cause clearing has definitely been disabled. See "Note" | 1 | 0 | X | X | 0 | 1 | Can't happen unless reset cause clearing is broken badly. See "Note" -| 1 | 0 | 1 | X | 0 | 0 | Implausible but possible with precise timeing - but more likely reset cause clearing is broken badly. See "Note" -| 0 | 0 | 1 | 1 | 0 | 0 | Implausible but possible with precise timeing - but more likely reset cause clearing is broken badly. See "Note" -| 0 | 0 | 0 | X | X | X | power on, reset pin, or brown out reset. If more than one is set, see the smaller table below. -| X | X | X | X | 1 | X | If POR fired, Vdd must have been below VPOR, fall or VPORR since the last time POR was triggered, then Vdd exceeded VPOR, rise or VPOR). +| 1 | 0 | 1 | X | 0 | 0 | Implausible but possible with precise timing - but more likely reset cause clearing is broken badly. See "Note" +| 0 | 0 | 1 | 1 | 0 | 0 | Implausible but possible with precise timing - but more likely reset cause clearing is broken badly. See "Note" +| 0 | 0 | 0 | X | X | X | power on, reset pin, or brown out reset. If more than one is set, see the smaller table below. +| X | X | X | X | 1 | X | If POR fired, Vdd must have been below VPOR, fall or VPORR since the last time POR was triggered, then Vdd exceeded VPOR, rise or VPOR). | PORF | EXTRF | BORF | Meaning, Suggested Response |------|-------|------|---------- | 1 | 0 | 0 | Vdd ramped quickly, and BOD did not have a chance to engage and react (fast rising power) or BOR not enabled. This is a typical successful startup (or if problems at startup, perhapse you should be using BOD or longer SUT) -| 0 | 0 | 1 | Code was executing, Vdd fell below VBOR, triggering BOR. Vdd stayed above VPOR, fall or VPORR, and later increased to exceed VBOR, and appropriate brownout recovery steps taken, if needed, -| 1 | 0 | 1 | Vdd ramp was slow. Likely while still in SUT, the BOD awoke, saw the still insufficeint voltage (Vdd < VBOR , and asserted a BOR, from which it revived when Vdd exceeded VBOR +| 0 | 0 | 1 | Code was executing, Vdd fell below VBOR, triggering BOR. Vdd stayed above VPOR, fall or VPORR, and later increased to exceed VBOR, and appropriate brownout recovery steps taken, if needed, +| 1 | 0 | 1 | Vdd ramp was slow. Likely while still in SUT, the BOD awoke, saw the still insufficeint voltage (Vdd < VBOR , and asserted a BOR, from which it revived when Vdd exceeded VBOR | 1 | 1 | X | The reset pin voltage rose significantly more slowly than VDD, and hence upon release of POR. EXTR remained engaged for some time longer. Excessive capacitive loading on reset? Insufficient pullup? -Note: These states indicate that you've done something to defang the reset cause detection and dirty reset detection. That is something one does not do by accident, so presumably you have something in mind. Uh, well, whatever it is, if it's supposed to be working, it's not, cause here we are with reset flags that cannot occur unless you're failing to reset the reset flags on startup. Anyway, until you fix that, neither bootloader nor application will be able to accurately determine reset cause and dirty resets will usually hang. +Note: These states indicate that you've done something to defang the reset cause detection and dirty reset detection. That is something one does not do by accident, so presumably you have something in mind. Uh, well, whatever it is, if it's supposed to be working, it's not, cause here we are with reset flags that cannot occur unless you're failing to reset the reset flags on startup. Anyway, until you fix that, neither bootloader nor application will be able to accurately determine reset cause and dirty resets will usually hang. Suggested responses (pick one): -a. Run in circles, scream, and shout. +a. Run in circles, scream, and shout. b. (not recommended) Correct your code to check and clear the reset cause at the earliest point in the boot process, and if none is reported handle the fact that you are in an unknown state, likely by firing a software reset. -c. Come to your senses, accept that the wheel was invented long, with little fundamental improvement, and stick to current version ofDo not override the included reset cause handling. Retrive the reset cause from the 6 LSB of GPIOR0 as documented after which it may be set to 0 if used elsewhere. None of the lines on the table which refer to this note should occur. +c. Come to your senses, accept that the wheel was invented long, with little fundamental improvement, and stick to current version ofDo not override the included reset cause handling. Retrieve the reset cause from the 6 LSB of GPIOR0 as documented after which it may be set to 0 if used elsewhere. None of the lines on the table which refer to this note should occur. -Where I say reset cause detection is "disabled", notice that PORF is set, implyingthat the reset flags were never cleared. +Where I say reset cause detection is "disabled", notice that PORF is set, implyingthat the reset flags were never cleared. -When the combination of reset cause flags is inconsistent with both making no attempt to clear flags - PORF was set at poweron, and is no longer set - but has somehow failed to clear other flags. +When the combination of reset cause flags is inconsistent with both making no attempt to clear flags - PORF was set at poweron, and is no longer set - but has somehow failed to clear other flags. In some cases, with unusually good timing, it is likely possible to trigger two reset causes close enough together that you end up with two flags (for example, code is spamming SWR repeatedly, you could hit reset at the right moment ) -`**` - Few plausible routes to this state exist except for a) wiring fault, intermittent power connection. b) power supply circuit defect or unforseen behavior. c) Power supply circuit expected behavior due to, for example, a device with a battery being used until it stopped working due to the battery voltage sinking below VBOR; depending on the design, it is entirely plausible that the battery voltage will continue to be seen by the microcontroller - so then when it is plugged into a USB power cable to recharge, and the voltage rises above VBOR. This results in it starting up with BORF alone set. (This is a useful thing to check when the system gets into bad states, particularly ones not solved by a reset button press but solved by a power cycle - typically the result of an external device with different reset thresholds, which has not reset, or reset uncleanly (some devices, due to hysteresis in the reset mechanism, have a range of voltages in which they are above the nominal POR/BOR mechanism threshold, but not above the device's power on reset voltage. The AVR's have a VPORR, the voltage t +`**` - Few plausible routes to this state exist except for a) wiring fault, intermittent power connection. b) power supply circuit defect or unforeseen behavior. c) Power supply circuit expected behavior due to, for example, a device with a battery being used until it stopped working due to the battery voltage sinking below VBOR; depending on the design, it is entirely plausible that the battery voltage will continue to be seen by the microcontroller - so then when it is plugged into a USB power cable to recharge, and the voltage rises above VBOR. This results in it starting up with BORF alone set. (This is a useful thing to check when the system gets into bad states, particularly ones not solved by a reset button press but solved by a power cycle - typically the result of an external device with different reset thresholds, which has not reset, or reset uncleanly (some devices, due to hysteresis in the reset mechanism, have a range of voltages in which they are above the nominal POR/BOR mechanism threshold, but not above the device's power on reset voltage. The AVR's have a VPORR, the voltage t `*` Possibly due to loading and insufficient pullup strength on the reset pin (You should immediately suspect that there is an issue with autoreset if you are using that - Perhaps a different (larger) value capacitor was used? Could the pullup be disconnected?). This has the visible consequence that the bootloader runs on startup. Even when a sketch is present and the reset button was never pressed. While ideally it should not occur, it is also not indicative of the sort of "drop everything and find this bug" situation, whereas a starting with no flags set indicates a critical failure occurring, and ought never be tolerated, but rather investigated promptly. diff --git a/megaavr/extras/ci/codespell-ignore-words-list.txt b/megaavr/extras/ci/codespell-ignore-words-list.txt index 7e012dda..fea427de 100644 --- a/megaavr/extras/ci/codespell-ignore-words-list.txt +++ b/megaavr/extras/ci/codespell-ignore-words-list.txt @@ -2,4 +2,4 @@ rcall hart acn combatibility -shiftIn \ No newline at end of file +shiftIn diff --git a/megaavr/libraries/PTC/README.md b/megaavr/libraries/PTC/README.md index 7a72c816..84d9dba2 100644 --- a/megaavr/libraries/PTC/README.md +++ b/megaavr/libraries/PTC/README.md @@ -1,73 +1,78 @@ -# PTC Library -An Arduino compatible C library to support the PTC in the AtTiny 1-Series +# ptc_touch +An Arduino compatible C library to support the PTC in the ATtiny 1-family as well as the DA family. ## Features -As this is library can be considered an Alpha version, things might change in the future, so nothing is set in stone yet. * Ready to use with minimal knowledge of the peripheral, but can still be well configured (options are similar to the "official" library) * Supports Mutual-cap and Self-cap (+Shield) sensing technology -* Supports event-based triggering of conversions, as well as window-compared wake-up from Stand-by sleep -* Can be suspended to allow the polled use of ADC0 (ADC0 ISRs are used by the library) -* Reduced memory footprint due to CPU specific optimizations compared to the "official" implementation (needs about 3.5k Flash) +* Supports event-based triggering of conversions, as well as window-compared wake-up from stand-by sleep +* On ATtiny: Can be suspended to allow the polled use of ADC0's internal channels (ADC0 ISRs are used by the library) +* Reduced memory footprint due to CPU specific optimizations compared to the "official" implementation ## Installation +For the non-Arduino people: all required files are inside the src folder (No Arduino exclusive functions are used in this library). -Not needed, comes with the core. -For the non-Arduino people: all you need is inside the src folder. +### How to use this library -## How to use this library - -### Overview #### Step 1: Memory Initialization Allocate memory for every single sensing node. The easiest way is to have an array with `cap_sensor_t` elements, e.g. `cap_sensor_t nodes[3];`. Even though it allows you to access the data stored in this structures directly, it is not allowed to change the contents directly. Always use the provided setter functions for this. #### Step 2: Node registration -Initialize the node to be either a mutual-cap node with `uint8_t ptc_add_mutualcap_node(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh);` or to a self-cap node with `uint8_t ptc_add_selfcap_node(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh);`. When everything went without problems, the functions will return the enum `PTC_LIB_SUCCESS`. While mutual-cap requires yCh and xCh not to be 0 (otherwise `PTC_LIB_BAD_ARGUMENT` will be returned), the self-cap requires only the yCh not to be 0. If the xCh is zero, the node will be a normal self-cap node, otherwise it will be of the self-cap with shield type. This becomes relevant when you change between the types, but that will be explained later. This function will also disable the pullup and digital input function of the specified pins. The order, in which this function is called determines the conversion order. The nodes will be also numbered for easier identification. Up to 255 nodes are supported for devices with 16 or less PTC pins. 65k for bigger devices. The nodes are enabled by default, so, in case an added node should be disabled, you can call `uint8_t ptc_disable_node(cap_sensor_t* node);`, and for `re-enabling uint8_t ptc_enable_node(cap_sensor_t* node);`. +Initialize the node to be either a mutual-cap node with `uint8_t ptc_add_mutualcap_node(cap_sensor_t* node, ptc_ch_bm_t xCh, ptc_ch_bm_t yCh);` or to a self-cap node with `uint8_t ptc_add_selfcap_node(cap_sensor_t* node, ptc_ch_bm_t xCh, ptc_ch_bm_t yCh);`. When everything went without problems, the functions will return the enum `PTC_LIB_SUCCESS`. While mutual-cap requires yCh and xCh not to be 0 (otherwise `PTC_LIB_BAD_ARGUMENT` will be returned), the self-cap requires only the yCh not to be 0. If the xCh is zero, the node will be a normal self-cap node, otherwise it will be of the self-cap with shield type. This becomes relevant when you change between the types, but that will be explained later. This function will also disable the pullup and digital input function of the specified pins. The order, in which this function is called determines the conversion order. The nodes will be also numbered for easier identification. Up to 255 nodes are supported for devices with 16 or less PTC pins. 65k for bigger devices. The nodes are enabled by default, so, in case an added node should be disabled, you can call `uint8_t ptc_disable_node(cap_sensor_t* node);`, and for re-enabling `uint8_t ptc_enable_node(cap_sensor_t* node);`. Here are some examples: -- `ptc_add_mutualcap_node(&nodes[0], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PA7));` +- `ptc_add_mutualcap_node(&nodes[0], PIN_TO_PTC(PIN_PA7), PIN_TO_PTC(PIN_PA4));` - PA4 will be sensing, PA7 will be driving -- `ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA4), 0);` +- `ptc_add_selfcap_node(&nodes[1], 0, PIN_TO_PTC(PIN_PA4));` - No Driving pin, only sensing on PA4 -- `ptc_add_selfcap_node(&nodes[2], (PIN_TO_PTC(PIN_PA4) | PIN_TO_PTC(PIN_PA5)), PIN_TO_PTC(PIN_PA7));` +- `ptc_add_selfcap_node(&nodes[2], PIN_TO_PTC(PIN_PA7), (PIN_TO_PTC(PIN_PA4) | PIN_TO_PTC(PIN_PA5)));` - useful for wakeups, all "buttons" work as one, no matter on which you press, plus driven shield pin. #### 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)` 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. However, if there was a successful call to `uint8_t ptc_suspend(void)`, only the timestamp is updated, but no acquisition is started. This also applies when the library was put into low-power mode, except that conversions can still be triggered by events. Even though this library can handle three different node types, only one type can be acquired at a time. By default, the type of the first node in the list is used. But the user can use the function `ptc_set_next_conversion_type(uint8_t type)` to change this behavior. #### Step 4: Callback -In order to provide an efficient way of notification, the library provides a couple of extern callbacks, listed below with the type of events that are passed to those functions. In a previous version, all callback-events would have called `void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t* node) {`, however, I realized that a single function would end up too complex. -All (sub-)callbacks are provided with the `weak` and `alias(ptc_event_callback)` attributes, meaning that if you do not provide a definition in your files, the callbacks will be redirected to the standard callback function, but never to both. The standard callback has also a default version that literally does nothing, meaning that your sketch will compile, even if you don't define any of the callbacks (not recommended though). - - -There are following callbacks: -- `ptc_event_cb_touch` With following Events: - - `PTC_CB_EVENT_TOUCH_DETECT` (0x11): Whenever a node changes to the pressed state - - `PTC_CB_EVENT_TOUCH_RELEASE` (0x12): Whenever a node changes to the un-pressed state -- `ptc_event_cb_wake` Only when low-power is enabled, called from ptc_process: - - `PTC_CB_EVENT_WAKE_TOUCH` (0x15): When the Window-Comparator detected a touch - - `PTC_CB_EVENT_WAKE_NO_TOUCH` (0x16): opposite of above -- `ptc_event_cb_conversion`: - - `PTC_CB_EVENT_CONV_CMPL` (0x20): Anytime a node has finished it's conversion and the state-machine was updated - - `PTC_CB_EVENT_CONV_MUTUAL_CMPL` (0x21). Useful to select the next type of conversions, if you have a mix of types - - `PTC_CB_EVENT_CONV_SELF_CMPL` (0x24). You can AND the values with NODE_MUTUAL_bm, NODE_SELFCAP_bm, - - `PTC_CB_EVENT_CONV_SHIELD_CMPL` (0x28). NODE_SELFCAP_SHIELD_bm. - - `PTC_CB_EVENT_CONV_TYPE_CMPL_MSK`(0x0D): Can be used with bitwise AND to differentiate between node and type conversions. -- `ptc_event_cb_calibration`: - - `PTC_CB_EVENT_CONV_CALIB` (0x40): When ever a calibration was finished. - - `PTC_CB_EVENT_ERR_CALIB_MSK` (0x07): Can be used with bitwise AND to to differentiate between success and Error. - - `PTC_CB_EVENT_ERR_CALIB_LOW` (0x41): If the Capacitor compensation ended up too low. - - `PTC_CB_EVENT_ERR_CALIB_HIGH` (0x42): As above, but too high. - - `PTC_CB_EVENT_ERR_CALIB_TO` (0x44): If the calibration timed out. -- `ptc_event_cb_error': - - Currently unused, but might be in the future. -The hexa-decimal values are there for orientation and might change in the future. +In order to understand what happens, there is an extern callback function defined in the h-file. This means that you are required to have a function in your sketch-file that look like this: `void ptc_event_callback(const uint8_t eventType, cap_sensor_t* node) {`. The reason why I used an extern function and not a pointer to a callback is that this allows the compiler to inline optimizations, like optimizing away calls with eventTypes that don't need handling. +There are following events: +- `PTC_CB_EVENT_WAKE_TOUCH` (node: lowPower): + - This event happens in LP mode only when the Window-Comparator was triggered. +- `PTC_CB_EVENT_WAKE_NO_TOUCH` (node: lowPower): + - This event happens in LP mode only when the Window-Comparator was not triggered. +- `PTC_CB_EVENT_CONV_CMPL` (node: current node): + - This event happens whenever ptc_process finished handling said node +- `PTC_CB_EVENT_CONV_MUTUAL_CMPL`, +- `PTC_CB_EVENT_CONV_SELF_CMPL`, +- `PTC_CB_EVENT_CONV_SHIELD_CMPL` (node: last converted node): + - This events happen whenever ptc_process has handled all nodes of said type. +- `PTC_CB_EVENT_CONV_CALIB` (node: current node): + - This event happens, whenever the calibration was successful. +- `PTC_CB_EVENT_ERR_CALIB` (node: current node): + - This event happens, whenever the calibration failed on said node. +- `PTC_CB_EVENT_TOUCH_DETECT`(node: current node): + - This event triggers shortly before the state-machine is updated to the "touched" state. +- `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. +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 +- `void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node);` + - called on `PTC_CB_EVENT_CONV_*_CMPL` events +- `void ptc_event_cb_error(const ptc_cb_event_t eventType, cap_sensor_t* node);` + - called on `PTC_CB_EVENT_CONV_CALIB` events +- `void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node);` + - called on `PTC_CB_EVENT_ERR_CALIB` events +- `void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node);` + - called on `PTC_CB_EVENT_TOUCH_*` events + ### Low Power operation @@ -75,13 +80,13 @@ This library also provides a so-called low power mode. This mode changes the ope In order to initialize the low-power mode, just call `uint8_t ptc_lp_init(cap_sensor_t* node)` (to disable: `uint8_t ptc_lp_disable(void)`) with the node you want to have automatically converted. The node must be enabled and must have been calibrated beforehand to work correctly, meaning it had to have a couple finished conversions. -To check, if the window-comparator was triggered outside of the WAKE events, you can use the function `uint8_t ptc_lp_was_waken(void)` that returns either `PTC_LIB_WAS_WAKEN` or `PTC_LIB_ASLEEP`, at least while the low-power mode is active or use the callback. +To check, if the window-comparator was triggered outside of the WAKE events, you can use the function `uint8_t ptc_lp_was_waken(void)` that returns either `PTC_LIB_WAS_WAKEN` or `PTC_LIB_ASLEEP`, at least while the low-power mode is active. ### Pins and how to use the ptc_add_* functions -The ptc_add_* functions require a bitmap (ptc_ch_bm_t yCh; ptc_ch_bm_t xCh) of pins, that will be connected to the PTC internals upon the beginning of a conversion. The bitposition equals to the PTC pin numbering, that you can find in the PTC column in the datasheet. It is different from the usual numbering, for example PA4 on the 1614 is X0/Y0, resulting in a bitmap of 0x01. +The ptc_add_* functions require a bitmap (ptc_ch_bm_t yCh; ptc_ch_bm_t xCh) of pins, that will be connected to the PTC internals upon the beginning of a conversion. The bit position equals to the PTC pin numbering, that you can find in the PTC column in the datasheet. On Tinies, it is different from the usual numbering, for example PA4 on the 1614 is X0/Y0, resulting in a bitmap of 0x01. On DAs it is more straightforward, PA0 is bitmap 1<<0, PB0 is 1<<8. PORTC is skipped, so PD0 is 1<<16. -However, to make it easier, a compile-time look-up-table with a macro to access it is provided, `PIN_TO_PTC(PIN_Pnx)`. If the pin is valid, the corresponding bit-map is returned, otherwise null. But this requires the megaTinyCore from Spence Konde, as the PIN_Pnx naming is defined there. +However, to make it easier, a compile-time look-up-table with a macro to access it is provided, PIN_TO_PTC(PIN_Pnx). If the pin is valid, the corresponding bit-map is returned, otherwise null. But this requires the megatiny/Dx core from Spence Konde, as the PIN_Pnx naming is defined there. The reason, why a bit-map is used, is to provide a way to connect the electrodes of multiple pins to act as one big electrode. In order to create a node with multiple pins connected, a simple OR operation is enough. @@ -110,20 +115,24 @@ If you have trouble understanding: Imagine two equally sized volumes connected t 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_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 +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: 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. - -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. +- `uint8_t ptc_node_set_gain(cap_sensor_t *node, ptc_gain_t gain)`. Increases the sensitivity of the electrode by adjusting a capacitor on the integrator (I think). Defaults to 1x Gain. + - Valid values are: `PTC_GAIN_1`, `PTC_GAIN_2`, `PTC_GAIN_4`, `PTC_GAIN_8`, `PTC_GAIN_16`, `PTC_GAIN_32` (Tiny only). +- `uint8_t ptc_node_set_oversamples(cap_sensor_t *node, uint8_t ovs)`. Defines the amount of ADC Oversampling. Will not affect the count value, as it is internally right-shifted. Valid values are in the range from 0 to 6 resulting in 1x, 2x, 4x, 8x, 16x, 32x, 64x oversampling. Defaults to 16x. +- `uint8_t ptc_node_set_charge_share_delay(cap_sensor_t *node, uint8_t csd)`. Affects the Sample length of the ADC. This does pretty much the same thing as ADC.SAMPCTRL register. Valid range is from 0 to 31. Defaults to 0 extra clocks. +- `uint8_t ptc_node_set_prescaler(cap_sensor_t *node, ptc_presc_t presc)`. It is possible to slow down the ADC/PTC clock by adjusting the Prescaler. The ADC/PTC Clock should be between 1 and 2 MHz. The library calculates the default based on F_CPU. + - Valid values for Tiny are: `PTC_PRESC_DIV2_gc`, `PTC_PRESC_DIV4_gc`, `PTC_PRESC_DIV8_gc`, `PTC_PRESC_DIV16_gc`, `PTC_PRESC_DIV32_gc`, `PTC_PRESC_DIV64_gc`, `PTC_PRESC_DIV128_gc`, `PTC_PRESC_DIV256_gc`. + - Valid values for DA are: `PTC_PRESC_DIV2_gc`, `PTC_PRESC_DIV4_gc`, `PTC_PRESC_DIV6_gc`, `PTC_PRESC_DIV8_gc`, `PTC_PRESC_DIV10_gc`, `PTC_PRESC_DIV12_gc`, `PTC_PRESC_DIV14_gc`, `PTC_PRESC_DIV16_gc`. +- `uint8_t ptc_node_set_resistor(cap_sensor_t *node, ptc_rsel_t res)`. Allows to change the serial resistor between the Cc and the node. Fixed at 100k for Self-Cap. Defaults to 50k for Mutual-Cap. + - Valid Values are: `RSEL_VAL_0`, `RSEL_VAL_20`, `RSEL_VAL_50`, `RSEL_VAL_70`, `RSEL_VAL_80` (DA only), `RSEL_VAL_100`, `RSEL_VAL_120` (DA only), `RSEL_VAL_200`. + +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 electrode 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: diff --git a/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino b/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino index a17dfa72..90b484cb 100644 --- a/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino +++ b/megaavr/libraries/PTC/examples/mutualcap/mutualcap.ino @@ -8,7 +8,7 @@ * otherwise the handling of the successive nodes would be delayed. */ #define MySerial Serial - +#if !defined(MILLIS_USE_TIMERNONE) cap_sensor_t nodes[2]; void setup() { @@ -28,7 +28,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)); @@ -38,14 +38,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) { @@ -58,3 +58,9 @@ void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node MySerial.print(" Node: "); MySerial.println(ptc_get_node_id(node)); } +#else +void setup() { +} +void loop() { +} +#endif 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 new file mode 100644 index 00000000..7fc99041 --- /dev/null +++ b/megaavr/libraries/PTC/examples/mutualcap_low_power/mutualcap_low_power.ino @@ -0,0 +1,106 @@ +#include +#include +/* + * This example demonstrates how to use the PTC in sleep mode. + * The library changes the triggering to an Event-System based approach. + * However, only one node can be monitored this way. To allow a wake-up, + * all nodes a connected together in the low-power node. + * PA4 is the X-Line, while PA5, PA6 and PA7 act as Y-Line. + */ +#define MySerial Serial +#if !defined(MILLIS_USE_TIMERNONE) +cap_sensor_t nodes[3]; +cap_sensor_t lp_node; // as an example, could also be part of the array above + +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_PA5)); + ptc_add_mutualcap_node(&nodes[1], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PA6)); + ptc_add_mutualcap_node(&nodes[2], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PA7)); + + // make the "low power" node a lumped sensor - this allows to monitor all electrodes at the same time. + // In other words, this creates a single, big button. + ptc_add_mutualcap_node(&lp_node, PIN_TO_PTC(PIN_PA4), (PIN_TO_PTC(PIN_PA5) | PIN_TO_PTC(PIN_PA6) | PIN_TO_PTC(PIN_PA7))); + + RTC.PITCTRLA = RTC_PITEN_bm; // In this example, the PIT is our Event source/timer + 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!"); +} + +uint32_t nextSleep = 10000; // entering sleep after 10 seconds of inactivity + +void loop() { + uint32_t ms = millis(); + ptc_process(ms); // main ptc task, requires regular calls + if (ms > nextSleep) { + nextSleep = ms + 10000; + 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) { + 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!"); + ptc_lp_disable(); // disable the low-power node to restore the usual acquisition process + MySerial.flush(); // wait for finished transmission from Serial + } +} + +// 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) { + 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:"); + MySerial.println(ptc_get_node_id(node)); + nextSleep = ms + 10000; + } else if (PTC_CB_EVENT_TOUCH_RELEASE == eventType) { // however, a TOUCH_RELEASE will be, when the finger is removed again + MySerial.print("node released:"); + MySerial.println(ptc_get_node_id(node)); + nextSleep = ms + 10000; + } +} + +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. Time to fix: "); // Time to fix indicates how long the calibration took. Should be < 6 preferably. + MySerial.print(node->lastStateChange); // Values close to 10 might indicate some problems with noise. + } + MySerial.print(" Node: "); + MySerial.println(ptc_get_node_id(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 +} +#else +void setup() { +} +void loop() { +} +#endif 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 b0c5af75..42d94a12 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 @@ -1,8 +1,8 @@ #include /* * This example creates four different sensing nodes. of two different types. - * PA4 and PA5 are the self-cap lines with PB0 acting as shield pin. - * PA6 and PA7 are the Y-Lines with PB1 acting as the X-line. + * PA4 and PA5 are the self-cap lines with PA0 acting as shield pin. + * PA6 and PA7 are the Y-Lines with PA1 acting as the X-line. * PTC_CB_EVENT_CONV_MUTUAL_CMPL and * PTC_CB_EVENT_CONV_SHIELD_CMPL can be used to change the type that is converted. * This will create an interlaced conversion, but it is not mandatory to do so. @@ -10,16 +10,17 @@ #define MySerial Serial +#if !defined(MILLIS_USE_TIMERNONE) cap_sensor_t nodes[4]; void setup() { // put your setup code here, to run once: - ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PB0)); - ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA5), PIN_TO_PTC(PIN_PB0)); + ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA0), PIN_TO_PTC(PIN_PA4)); + ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA0), PIN_TO_PTC(PIN_PA5)); - ptc_add_mutualcap_node(&nodes[2], PIN_TO_PTC(PIN_PA6), PIN_TO_PTC(PIN_PB1)); - ptc_add_mutualcap_node(&nodes[3], PIN_TO_PTC(PIN_PA7), PIN_TO_PTC(PIN_PB1)); + ptc_add_mutualcap_node(&nodes[2], PIN_TO_PTC(PIN_PA1), PIN_TO_PTC(PIN_PA6)); + ptc_add_mutualcap_node(&nodes[3], PIN_TO_PTC(PIN_PA1), PIN_TO_PTC(PIN_PA7)); MySerial.begin(115200); MySerial.println("Hello World!"); @@ -33,7 +34,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 +44,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 +53,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) { @@ -65,3 +66,9 @@ void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node MySerial.print(" Node: "); MySerial.println(ptc_get_node_id(node)); } +#else +void setup() { +} +void loop() { +} +#endif diff --git a/megaavr/libraries/PTC/examples/selfcap/selfcap.ino b/megaavr/libraries/PTC/examples/selfcap/selfcap.ino index 05e3eea3..d62be568 100644 --- a/megaavr/libraries/PTC/examples/selfcap/selfcap.ino +++ b/megaavr/libraries/PTC/examples/selfcap/selfcap.ino @@ -8,16 +8,16 @@ * otherwise the handling of the successive nodes would be delayed. */ #define MySerial Serial - +#if !defined(MILLIS_USE_TIMERNONE) cap_sensor_t nodes[3]; void setup() { MySerial.begin(115200); // this puts the node on the list and initializes to default values - ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA4), 0); - ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA5), 0); - ptc_add_selfcap_node(&nodes[2], PIN_TO_PTC(PIN_PA6), 0); + ptc_add_selfcap_node(&nodes[0], 0, PIN_TO_PTC(PIN_PA4)); + ptc_add_selfcap_node(&nodes[1], 0, PIN_TO_PTC(PIN_PA5)); + ptc_add_selfcap_node(&nodes[2], 0, PIN_TO_PTC(PIN_PA6)); // Make sure Serial works MySerial.println("Hello World!"); @@ -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) { @@ -60,3 +60,9 @@ void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node MySerial.print(" Node: "); MySerial.println(ptc_get_node_id(node)); } +#else +void setup() { +} +void loop() { +} +#endif 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 de2ea758..13b1e6f7 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 @@ -10,16 +10,16 @@ * as shield. This improves the signal-to-noise ratio. */ #define MySerial Serial - +#if !defined(MILLIS_USE_TIMERNONE) cap_sensor_t nodes[3]; void setup() { MySerial.begin(115200); // this puts the node on the list and initializes to default values - ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PA5) | PIN_TO_PTC(PIN_PA6)); - ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA5), PIN_TO_PTC(PIN_PA4) | PIN_TO_PTC(PIN_PA6)); - ptc_add_selfcap_node(&nodes[2], PIN_TO_PTC(PIN_PA6), PIN_TO_PTC(PIN_PA4) | PIN_TO_PTC(PIN_PA5)); + ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA5) | PIN_TO_PTC(PIN_PA6), PIN_TO_PTC(PIN_PA4)); + ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA4) | PIN_TO_PTC(PIN_PA6), PIN_TO_PTC(PIN_PA5)); + ptc_add_selfcap_node(&nodes[2], PIN_TO_PTC(PIN_PA4) | PIN_TO_PTC(PIN_PA5), PIN_TO_PTC(PIN_PA6)); // Make sure Serial works @@ -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)); @@ -54,3 +54,9 @@ void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t* node) { MySerial.println(ptc_get_node_id(node)); } } +#else +void setup() { +} +void loop() { +} +#endif diff --git a/megaavr/libraries/PTC/examples/selfcap_shield_lowpower/selfcap_shield_lowpower.ino b/megaavr/libraries/PTC/examples/selfcap_shield_lowpower/selfcap_shield_lowpower.ino index f714209e..45676bce 100644 --- a/megaavr/libraries/PTC/examples/selfcap_shield_lowpower/selfcap_shield_lowpower.ino +++ b/megaavr/libraries/PTC/examples/selfcap_shield_lowpower/selfcap_shield_lowpower.ino @@ -74,8 +74,9 @@ void loop() { ptc_lp_init(&lp_node); // Will set STARTEI bit to start conversions 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 the wake-up events. The user must make sure - // there are no detected touches before going back to sleep. + } + // The library does not filter the wake-up events. The user must make sure + // there are no detected touches before going back to sleep. MySerial.println("I'm Awake!"); ptc_lp_disable(); } @@ -113,8 +114,8 @@ void ptc_event_cb_calibration(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 + 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 } 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 e09f7aea..c6711032 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 @@ -10,15 +10,15 @@ * as shield. This improves the signal-to-noise ratio. */ #define MySerial Serial - +#if !defined(MILLIS_USE_TIMERNONE) cap_sensor_t nodes[2]; void setup() { MySerial.begin(115200); // this puts the node on the list and initializes to default values - ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA4), PIN_TO_PTC(PIN_PA6)); - ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA5), PIN_TO_PTC(PIN_PA6)); + ptc_add_selfcap_node(&nodes[0], PIN_TO_PTC(PIN_PA6), PIN_TO_PTC(PIN_PA4)); + ptc_add_selfcap_node(&nodes[1], PIN_TO_PTC(PIN_PA6), PIN_TO_PTC(PIN_PA5)); // Make sure Serial works MySerial.println("Hello World!"); @@ -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) { @@ -59,3 +59,9 @@ void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node MySerial.print(" Node: "); MySerial.println(ptc_get_node_id(node)); } +#else +void setup() { +} +void loop() { +} +#endif diff --git a/megaavr/libraries/PTC/keywords.txt b/megaavr/libraries/PTC/keywords.txt index ea0ca5b6..7babd425 100644 --- a/megaavr/libraries/PTC/keywords.txt +++ b/megaavr/libraries/PTC/keywords.txt @@ -1,5 +1,5 @@ ####################################### -# Syntax Coloring Map For the ptc library +# Syntax Coloring Map For ptc_touch ####################################### ####################################### @@ -12,7 +12,6 @@ ptc_ret_t KEYWORD1 ptc_lib_t KEYWORD1 cap_sensor_t KEYWORD1 ptc_lib_sm_set_t KEYWORD1 -ptc_cb_event_t KEYWORD1 PTC_t KEYWORD1 @@ -20,12 +19,13 @@ PTC_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### + 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_wake KEYWORD2 ptc_event_cb_error KEYWORD2 ptc_enable_node KEYWORD2 ptc_disable_node KEYWORD2 @@ -38,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 @@ -52,11 +52,7 @@ ptc_lp_init KEYWORD2 ptc_lp_disable KEYWORD2 ptc_lp_was_waken KEYWORD2 ptc_get_sm_settings 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 + ####################################### # Instances (KEYWORD2) @@ -66,22 +62,20 @@ ptc_event_cb_error KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### + PTC_CB_EVENT_TOUCH LITERAL1 -PTC_CB_EVENT_TOUCH_DETECT LITERAL1 -PTC_CB_EVENT_TOUCH_RELEASE LITERAL1 -PTC_CB_EVENT_TOUCH_WAKE 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_SHIELD_CMPL LITERAL1 -PTC_CB_EVENT_CONV_TYPE_CMPL_MSK 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_ERR_CALIB_MSK LITERAL1 + NODE_TYPE_NOCONV_bm LITERAL1 NODE_MUTUAL_bm LITERAL1 NODE_SELFCAP_bm LITERAL1 diff --git a/megaavr/libraries/PTC/library.properties b/megaavr/libraries/PTC/library.properties index 52f3c017..921d708f 100644 --- a/megaavr/libraries/PTC/library.properties +++ b/megaavr/libraries/PTC/library.properties @@ -1,10 +1,10 @@ name=PTC -version=1.0.0 +version=1.1.0 author=MX682X maintainer=MX682X sentence=This library (experimental) allows a straightforward use of the Peripheral Touch Controller (PTC) -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! +paragraph=The PTC contains hardware to assist in touch detection. This library helps taking leverage of this peripheral. category=Sensors includes=ptc.h -url=https://github.com/MX682X/ptc_touch +url=https://github.com/SpenceKonde/megaTinyCore/tree/master/megaavr/libraries/PTC architectures=megaavr diff --git a/megaavr/libraries/PTC/src/ptc.c b/megaavr/libraries/PTC/src/ptc.c index 25337796..3c7cb408 100644 --- a/megaavr/libraries/PTC/src/ptc.c +++ b/megaavr/libraries/PTC/src/ptc.c @@ -1,5 +1,5 @@ /* - * Refer to ptc_touch.h file for copyright, changelog, usage and license information + * Refer to ptc.h file for copyright, changelog, usage and license information */ @@ -9,30 +9,27 @@ // The following functions are used internally only. // get the pointer of the last valid node struct can be "firstNode" -cap_sensor_t* ptc_get_last_node(void); +cap_sensor_t *ptc_get_last_node(void); -uint8_t ptc_append_node(cap_sensor_t* pNewNode); +uint8_t ptc_append_node(cap_sensor_t *pNewNode); // Prepares the ADC/PTC registers for the next conversion batch, starts conversion with firstNode void ptc_init_conversion(uint8_t nodeType); -// Strts the conversion of the node that is passed as argument -void ptc_start_conversion (cap_sensor_t* node); +// Starts the conversion of the node that is passed as argument +void ptc_start_conversion(cap_sensor_t *node); // State-Machine Handler, interprets the measured values and decides if key is "pressed" -void ptc_process_node_sm (cap_sensor_t* node); +void ptc_process_node_sm(cap_sensor_t *node); // Helper function -void ptc_process_measurement (cap_sensor_t* node); +void ptc_process_measurement(cap_sensor_t *node); // Handles (initial) calibration -uint8_t ptc_process_calibrate (cap_sensor_t* node); +uint8_t ptc_process_calibrate(cap_sensor_t *node); -// Handles adjustment of the reference value when a button is not pressed -void ptc_process_adjust (); -void ptc_set_registers(cap_sensor_t* node); cap_sensor_t *firstNode = NULL; @@ -65,82 +62,68 @@ ptc_lib_sm_set_t ptc_sm_settings = { .drift_down_nom = 15 }; -ptc_lib_sm_set_t* ptc_get_sm_settings() { +ptc_lib_sm_set_t *ptc_get_sm_settings() { return &ptc_sm_settings; } -// Analog gain Values, extracted with a debugger - -const uint8_t ptc_a_gain_lut[] = { - 0x3F, 0x1C, 0x0B, - 0x05, 0x03, 0x01, -}; - - - - - -//#if (NUM_TOTAL_PINS > 32) -//uint16_t ptc_touch_stack = 0; -//#else -//uint8_t ptc_touch_stack = 0; -//#endif - -// Workarounds to make the code work with DAs... Argh. -#if defined (__PTC_DA__) - #ifndef ADC_SAMPNUM_ACC1_gc - #define ADC_SAMPNUM_ACC1_gc ADC_SAMPNUM_NONE_gc - #endif - #ifndef ADC_REFSEL_VDDREF_gc - #define ADC_REFSEL_VDDREF_gc 0x00 - #endif -#endif #if defined (__PTC_Tiny__) #define PTC_DEFAULT_SC_CC 0x0567 #define PTC_DEFAULT_MC_CC 0x0234 + #define PTC_GAIN_BASE 0x003F #elif defined (__PTC_DA__) #define PTC_DEFAULT_SC_CC 0x00F0 #define PTC_DEFAULT_MC_CC 0x00A0 + #define PTC_GAIN_BASE 0x001F #endif -/* - * Different Functions that change the behaviour of the supplied node - */ -__attribute__ ((weak)) -void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t* node) { + + + +__attribute__((weak)) +void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t *node) { (void)eventType; (void)node; } -__attribute__ ((weak, alias("ptc_event_callback"))) void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node); -__attribute__ ((weak, alias("ptc_event_callback"))) void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t* node); -__attribute__ ((weak, alias("ptc_event_callback"))) void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node); -__attribute__ ((weak, alias("ptc_event_callback"))) void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node); -__attribute__ ((weak, alias("ptc_event_callback"))) void ptc_event_cb_error(const ptc_cb_event_t eventType, cap_sensor_t* node); +__attribute__((weak, alias("ptc_event_callback"))) void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node); +__attribute__((weak, alias("ptc_event_callback"))) void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t *node); +__attribute__((weak, alias("ptc_event_callback"))) void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node); +__attribute__((weak, alias("ptc_event_callback"))) void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node); +__attribute__((weak, alias("ptc_event_callback"))) void ptc_event_cb_error(const ptc_cb_event_t eventType, cap_sensor_t *node); + +/* + * Different Functions that change the behaviour of the supplied node + */ + // 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) +uint8_t ptc_node_set_thresholds(cap_sensor_t *node, int16_t th_in, int16_t th_out) { + if (NULL == node) { return PTC_LIB_BAD_POINTER; - - node->touch_in_th = th_in; - node->touch_out_th = th_out; + } + if (th_in != 0) { + node->touch_in_th = th_in; + } + if (th_out != 0) { + node->touch_out_th = th_out; + } return PTC_LIB_SUCCESS; } // Change Resistor Setting. Note: Only has an effect on mutual sensors -uint8_t ptc_node_set_resistor(cap_sensor_t* node, uint8_t res) { +uint8_t ptc_node_set_resistor(cap_sensor_t *node, ptc_rsel_t res) { PTC_CHECK_FOR_BAD_POINTER(node); - if (res > RSEL_MAX) + if (res > RSEL_MAX) { return PTC_LIB_BAD_ARGUMENT; - - + } + res &= 0x0F; + res <<= 0x04; if (node->type & NODE_MUTUAL_bm) { uint8_t presc = node->hw_rsel_presc & 0x0F; - presc |= ((res & 0x0F) << 4); + presc |= res; node->hw_rsel_presc = presc; return PTC_LIB_SUCCESS; } @@ -149,38 +132,63 @@ uint8_t ptc_node_set_resistor(cap_sensor_t* node, uint8_t res) { } -// Change pre-scaler. Recommended ADC frequency: < 1.5MHz, but max 3 factors below -uint8_t ptc_node_set_prescaler(cap_sensor_t* node, uint8_t presc) { +// Change prescaler. Recommended ADC frequency: < 1.5MHz, but max 3 factors below +uint8_t ptc_node_set_prescaler(cap_sensor_t *node, ptc_presc_t presc) { PTC_CHECK_FOR_BAD_POINTER(node); - if ((presc > (PTC_PRESC_DEFAULT + 2)) || (presc < PTC_PRESC_DEFAULT)) + if ((presc > (PTC_PRESC_DEFAULT + 2)) || (presc < PTC_PRESC_DEFAULT)) { return PTC_LIB_BAD_ARGUMENT; - + } + presc &= 0x0F; uint8_t res = node->hw_rsel_presc & 0xF0; - res |= (presc & 0x0F); + res |= presc; node->hw_rsel_presc = res; return PTC_LIB_SUCCESS; } -uint8_t ptc_node_set_gain(cap_sensor_t* node, uint8_t aGain, uint8_t dGain) { +uint8_t ptc_node_set_gain(cap_sensor_t *node, ptc_gain_t gain) { PTC_CHECK_FOR_BAD_POINTER(node); - if (aGain > 0x05) { - if (__builtin_constant_p(aGain)) - badArg("Analog Gain too high. Max Analog Gain Value is 0x05 (equals 32x)"); + + if (gain >= PTC_GAIN_MAX) { + if (__builtin_constant_p(gain)) { + badArg("Analog Gain too high. Max Analog Gain Value is 0x3F (Tiny) / 0x1F (DA)"); + } return PTC_LIB_BAD_ARGUMENT; } + gain = PTC_GAIN_MAX - gain; + gain <<= 4; + uint8_t ovs = node->hw_gain_ovs & 0x0F; + node->hw_gain_ovs = gain | ovs; + return PTC_LIB_SUCCESS; +} - if (dGain > 0x06) { - if (__builtin_constant_p(dGain)) +uint8_t ptc_node_set_oversamples(cap_sensor_t *node, uint8_t ovs) { + PTC_CHECK_FOR_BAD_POINTER(node); + + if (ovs > 0x06) { + if (__builtin_constant_p(ovs)) { badArg("Digital Gain too high. Max Digital Gain Value is 0x06 (equals 64x)"); + } return PTC_LIB_BAD_ARGUMENT; } - node->hw_a_d_gain = NODE_GAIN(aGain, dGain); + uint8_t gain = node->hw_gain_ovs & 0xF0; + node->hw_gain_ovs = gain | ovs; return PTC_LIB_SUCCESS; } +uint8_t ptc_node_set_charge_share_delay(cap_sensor_t *node, uint8_t csd) { + PTC_CHECK_FOR_BAD_POINTER(node); + if (csd > 15) { + if (__builtin_constant_p(csd)) { + badArg("Charge Share Delay too high, maximum value is 15"); + } + return PTC_LIB_BAD_ARGUMENT; + } + node->hw_csd = csd; + return PTC_LIB_SUCCESS; +} /* * Two functions to suspend and resume of the normal PTC operation, however, @@ -194,9 +202,9 @@ uint8_t ptc_suspend(void) { if (PTC_LIB_IDLE == ptc_lib_state) { // allow disabling only outside conversions ptc_lib_state = PTC_LIB_SUSPENDED; #if defined (__PTC_Tiny__) - PTC.CTRLP = 0x00; + PTC.CTRLP = 0x00; #elif defined (__PTC_DA__) - PTC.CTRLA = 0x00; + PTC.CTRLA = 0x00; #endif return PTC_LIB_SUCCESS; } @@ -213,120 +221,146 @@ void ptc_resume(void) { // own implementation of mTC's initADC0(), as the one that comes with the core does not overwrite // sample number (CTRLB) void ptc_init_ADC0(void) { - PTC_t* pPTC; + PTC_t *pPTC; _fastPtr_d(pPTC, &PTC); #if defined (__PTC_Tiny__) - #if F_CPU > 24000000 - pPTC->CTRLC = ADC_PRESC_DIV32_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; - #elif F_CPU >= 12000000 - pPTC->CTRLC = ADC_PRESC_DIV16_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; - #elif F_CPU >= 6000000 - pPTC->CTRLC = ADC_PRESC_DIV8_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; - #elif F_CPU >= 3000000 - pPTC->CTRLC = ADC_PRESC_DIV4_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; - #else - pPTC->CTRLC = ADC_PRESC_DIV2_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; - #endif - #if (F_CPU == 6000000 || F_CPU == 12000000 || F_CPU == 24000000 || F_CPU ==25000000) - pPTC->SAMPCTRL = (7); - #elif (F_CPU == 5000000 || F_CPU == 10000000 || F_CPU == 20000000) - pPTC->SAMPCTRL = (13); - #else - pPTC->SAMPCTRL = (10); - #endif - pPTC->CTRLD = ADC_INITDLY_DLY16_gc; - pPTC->CTRLB = ADC_SAMPNUM_ACC1_gc; - pPTC->CTRLA = ADC_ENABLE_bm; + #if F_CPU > 24000000 + pPTC->CTRLC = ADC_PRESC_DIV32_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; + #elif F_CPU >= 12000000 + pPTC->CTRLC = ADC_PRESC_DIV16_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; + #elif F_CPU >= 6000000 + pPTC->CTRLC = ADC_PRESC_DIV8_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; + #elif F_CPU >= 3000000 + pPTC->CTRLC = ADC_PRESC_DIV4_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; + #else + pPTC->CTRLC = ADC_PRESC_DIV2_gc | ADC_REFSEL_VDDREF_gc | ADC_SAMPCAP_bm; + #endif + #if (F_CPU == 6000000 || F_CPU == 12000000 || F_CPU == 24000000 || F_CPU ==25000000) + pPTC->SAMPCTRL = (7); + #elif (F_CPU == 5000000 || F_CPU == 10000000 || F_CPU == 20000000) + pPTC->SAMPCTRL = (13); + #else + pPTC->SAMPCTRL = (10); + #endif + pPTC->CTRLD = ADC_INITDLY_DLY16_gc; + pPTC->CTRLB = ADC_SAMPNUM_ACC1_gc; + pPTC->CTRLA = ADC_ENABLE_bm; #elif defined (__PTC_DA__) - + // PTC can't be used to measure for classical ADC readings (I think) #endif } +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); -// a helper function to reduce copy-pasting between self and mutual initialisations -void ptc_add_node_common(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh); - -// if xCh > 0, with shield, otherwise usual selfcap -uint8_t ptc_add_selfcap_node_asserted(cap_sensor_t* node, const ptc_ch_bm_t yCh, const ptc_ch_bm_t xCh) { - PTC_CHECK_POINTER(node, PTC_LIB_BAD_POINTER); // check not in h file as gcc doesn't know about the memory address - - if (ptc_append_node(node) != PTC_LIB_SUCCESS) + 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; - - ptc_add_node_common(node, yCh, xCh); - - if (xCh > 0) node->type = NODE_SELFCAP_SHIELD_bm; - else node->type = NODE_SELFCAP_bm; + } - node->touch_in_th = 20; - node->touch_out_th = 10; - node->hw_compCaps = PTC_DEFAULT_SC_CC; /* value from official library */ - node->hw_rsel_presc = NODE_RSEL_PRSC(RSEL_VAL_0, PTC_PRESC_DEFAULT); + uint8_t status = ptc_append_node(node); + if (status != PTC_LIB_SUCCESS) { + return status; + } - return PTC_LIB_SUCCESS; -} + memcpy(node->hw_xCh_bm, pCh, typesize * 2); // copies x and y channel + node->type = type; -uint8_t ptc_add_mutualcap_node_asserted(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh) { - PTC_CHECK_POINTER(node, PTC_LIB_BAD_POINTER); // check not in h file as gcc doesn't know about the memory address + #if defined(__PTC_Tiny__) + ptc_ch_bm_t xCh = (ptc_ch_bm_t)pCh[0]; + ptc_ch_bm_t yCh = (ptc_ch_bm_t)pCh[typesize]; + if ((xCh & yCh) != 0) { /* overlap */ + return PTC_LIB_BAD_ARGUMENT; + } + ptc_ch_bm_t pinmask = xCh | yCh; - if (ptc_append_node(node) != PTC_LIB_SUCCESS) - return PTC_LIB_BAD_POINTER; + if (pinmask == 0) { /* not a single pin selected */ + return PTC_LIB_BAD_ARGUMENT; + } + #elif defined (__PTC_DA__) + uint8_t xCh = 0; + uint8_t yCh = 0; + for (uint8_t i = 0; i < typesize; i++) { + uint8_t orX = pCh[i]; + uint8_t orY = pCh[i + typesize]; + uint8_t overlap = orX & orY; + if (overlap != 0) { + return PTC_LIB_BAD_ARGUMENT; + } + xCh |= orX; + yCh |= orY; + } + if ((xCh | yCh) == 0) { /* not a single pin selected */ + return PTC_LIB_BAD_ARGUMENT; + } + #endif - ptc_add_node_common(node, yCh, xCh); + node->hw_gain_ovs = NODE_GAIN(0, ADC_SAMPNUM_ACC16_gc); - node->type = NODE_MUTUAL_bm; + 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); + } else { /* Selfcap */ + if (yCh == 0) { /* not a single pin selected */ + return PTC_LIB_BAD_ARGUMENT; + } + if (xCh != 0) { + node->type = NODE_SELFCAP_SHIELD_bm; + } - node->touch_in_th = 10; - node->touch_out_th = 5; - node->hw_compCaps = PTC_DEFAULT_MC_CC; /* value from official library */ - node->hw_rsel_presc = NODE_RSEL_PRSC(RSEL_VAL_100, PTC_PRESC_DEFAULT); + node->touch_in_th = 20; + node->touch_out_th = 10; + 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; + for (uint8_t i = 0; i < sizeof(ptc_ch_to_pin); i++) { /* 6 or 14 iterations */ + uint8_t offset = *(pin_lut++); + uint16_t basePointer = (uint16_t)&PORTA; + uint8_t *portSettings = (uint8_t *)(offset + basePointer); + if ((uint8_t)pinmask & 0x01) { + (*portSettings) = PORT_ISC_INPUT_DISABLE_gc; + } + pinmask >>= 1; + } return PTC_LIB_SUCCESS; -} - -void ptc_add_node_common(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh) { - - node->stateMachine = PTC_SM_NOINIT_CAL; - node->hw_xCh_bm = xCh; - node->hw_yCh_bm = yCh; - node->hw_a_d_gain = NODE_GAIN(ADC_SAMPNUM_ACC1_gc, ADC_SAMPNUM_ACC16_gc); - #if defined (__PTC_Tiny__) - uint8_t* src = (uint8_t*)&PTC.PIN_OVR; // Get "addresses" of relevant elements - uint8_t* orY = (uint8_t*)&yCh; // This allows to split the uint64_t to an access of one byte at a time. - uint8_t* orX = (uint8_t*)&xCh; // on AtTinies, the compiler unrolls the loop to two iterations - uint8_t bit_pos = 0; // counts from 0 to "highest PTC pin number" - for (uint8_t i = 0; i < sizeof(ptc_ch_bm_t); i++) { - // this equals PTC.PIN_OVR |= (xCh | yCh);, but more friendly for CPU-registers on DAs with uint64_t - uint8_t temp_bm = *(orY++) | *(orX++); - *(src) |= temp_bm; - src++; - - for (uint8_t j = 0; j < 8; j++) { - if (temp_bm & 0x01) { - if (bit_pos < sizeof(ptc_ch_to_pin)) { // avoid accesses outside of the array, which can be smaller then the bit-map - uint8_t offset = ptc_ch_to_pin[bit_pos]; - if (offset != 0) { - uint16_t basePointer = (uint16_t)&PORTA; - uint8_t* portSettings = (uint8_t*)(basePointer + offset); - (*portSettings) = PORT_ISC_INPUT_DISABLE_gc; - } - } - } - bit_pos++; - temp_bm >>= 1; - } - } #elif defined (__PTC_DA__) -// Not started yet + /* separate for-loop to avoid changing the PORT settings before checking the complete input */ + uint8_t old_sreg = SREG; + cli(); // disable Interrupts to avoid anyone messing around with PINCONFIG + + PORTA.PINCONFIG = PORT_PULLUPEN_bm | PORT_ISC_INPUT_DISABLE_gc; // this setting is mirrored across all PORTs (guard against ISRs) + uint8_t *pin_upd = (uint8_t *) & (PORTA.PINCTRLUPD); + + uint8_t *end = pCh + typesize; + while (pCh < end) { + if ((uint8_t)(uint16_t) pin_upd != (uint8_t)(uint16_t)&PORTC.PINCTRLUPD) { // double cast to remove warning, skip PORTC + uint8_t pin_bm = *pCh; + pCh += typesize; + pin_bm |= *pCh; + pCh -= (typesize - 1); // decrement by one less to increment to next byte + + *pin_upd = pin_bm; + } + pin_upd += sizeof(PORT_t); + } + SREG = old_sreg; + return PTC_LIB_SUCCESS; #endif } -uint8_t ptc_enable_node(cap_sensor_t* node) { + +uint8_t ptc_enable_node(cap_sensor_t *node) { PTC_CHECK_FOR_BAD_POINTER(node); node->state.disabled = 0; @@ -334,7 +368,7 @@ uint8_t ptc_enable_node(cap_sensor_t* node) { } // Will finish conversion, but not start a new one -uint8_t ptc_disable_node(cap_sensor_t* node) { +uint8_t ptc_disable_node(cap_sensor_t *node) { PTC_CHECK_FOR_BAD_POINTER(node); node->state.disabled = 1; @@ -345,8 +379,9 @@ uint8_t ptc_disable_node(cap_sensor_t* node) { // Make the ADC wake up the CPU after the next conversion to allow a drift uint8_t ptc_lp_force_drift(void) { - if (NULL == lowPowerNode) + if (NULL == lowPowerNode) { return PTC_LIB_WRONG_STATE; + } if (PTC.INTCTRL == ADC_WCMP_bm) { // only do something when we are waiting for a value over the threshold PTC.INTCTRL = (ADC_RESRDY_bm | ADC_WCMP_bm); @@ -366,12 +401,19 @@ void ptc_set_next_conversion_type(ptc_node_type_t type) { // ev_ch: bitmask of required channels to be used for ADC0/PTC, e.g. channel 2 and 5 -> 0x24 // this would allow to periodically start ADC0/PIT by a timer without sleep -uint8_t ptc_lp_init(cap_sensor_t* node) { +// user must tell the function which event channel to connect to. +uint8_t ptc_lp_init(cap_sensor_t *node, uint8_t event_ch) { PTC_CHECK_FOR_BAD_POINTER(node); - if (NULL != lowPowerNode) - return PTC_LIB_WRONG_STATE; + 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__) + EVSYS.USERPTCSTART = event_ch; + #endif node->state.low_power = 1; node->stateMachine = PTC_SM_LOW_POWER; @@ -383,8 +425,9 @@ uint8_t ptc_lp_init(cap_sensor_t* node) { } uint8_t ptc_lp_disable(void) { - if (NULL == lowPowerNode) + if (NULL == lowPowerNode) { return PTC_LIB_WRONG_STATE; + } PTC.INTCTRL = 0; PTC.EVCTRL = 0; @@ -397,13 +440,15 @@ uint8_t ptc_lp_disable(void) { // use this in a conversion complete callback to see if the window comparator was triggered uint8_t ptc_lp_was_waken(void) { - if (NULL == lowPowerNode) + if (NULL == lowPowerNode) { return PTC_LIB_WRONG_STATE; + } - if (1 == lowPowerNode->state.win_comp) + if (1 == lowPowerNode->state.win_comp) { return PTC_LIB_WAS_WAKEN; - else + } else { return PTC_LIB_ASLEEP; + } return PTC_LIB_ERROR; } @@ -411,10 +456,12 @@ uint8_t ptc_lp_was_waken(void) { /* not recommended to use, as the calculation is bloated */ -uint16_t ptc_get_node_cc_femto (cap_sensor_t* node) { - if (NULL == node) +uint16_t ptc_get_node_cc_femto(cap_sensor_t *node) { + if (NULL == node) { return 0; + } + #if defined (__PTC_Tiny__) uint16_t retVal = 0; uint16_t comp = node->hw_compCaps; for (uint8_t i = 0; i < 3; i++) { @@ -427,12 +474,21 @@ uint16_t ptc_get_node_cc_femto (cap_sensor_t* node) { comp >>= 2; retVal += (comp & 0x03) * 6200; return retVal; //max 51000 + #elif defined(__PTC_DA__) + uint16_t comp = node->hw_compCaps + 1; // compCaps max 0x3FF + if (node->type != NODE_MUTUAL_bm) { + comp *= 2; + } + + return comp * 31 + (comp >> 2); // *= 31,25 + #else + #endif } -// pass the time, e.g. return value of millis to the function instead of hving a +// pass the time, e.g. return value of millis or TCB value to the function instead of having a // millis in here. improves portability -void ptc_process (uint16_t currTime) { +void ptc_process(uint16_t currTime) { if ((PTC_LIB_CONV_WCMP | PTC_LIB_CONV_LP) & ptc_lib_state) { if (NULL != lowPowerNode) { if (PTC_LIB_CONV_WCMP == ptc_lib_state) { @@ -451,7 +507,7 @@ void ptc_process (uint16_t currTime) { } } else if (PTC_LIB_CONV_COMPL == ptc_lib_state) { ptc_lib_state = PTC_LIB_IDLE; - for (cap_sensor_t* node = firstNode; node != NULL; node = node->nextNode) { + for (cap_sensor_t *node = firstNode; node != NULL; node = node->nextNode) { ptc_process_measurement(node); } ptc_event_cb_conversion((PTC_CB_EVENT_CONV_CMPL | currConvType), (cap_sensor_t *)currConvNode); @@ -477,9 +533,10 @@ void ptc_process (uint16_t currTime) { /* * Internal functions only below. Not part of the API */ -void ptc_process_measurement (cap_sensor_t* node) { - if (node->state.error) +void ptc_process_measurement(cap_sensor_t *node) { + if (node->state.error) { return; + } if (node->state.data_ready != 0) { node->state.data_ready = 0; @@ -489,12 +546,13 @@ void ptc_process_measurement (cap_sensor_t* node) { } -void ptc_process_node_sm (cap_sensor_t* node) { + +void ptc_process_node_sm(cap_sensor_t *node) { uint16_t nodeData = node->sensorData; uint8_t nodeSM = node->stateMachine; uint8_t lastChange = node->lastStateChange; uint16_t reference = node->reference; - int16_t nodeDelta = nodeData - reference; + int16_t nodeDelta = nodeData - reference; if (nodeSM == PTC_SM_NOINIT_CAL) { /* Beginning here */ uint8_t retVal = ptc_process_calibrate(node); @@ -622,177 +680,192 @@ void ptc_process_node_sm (cap_sensor_t* node) { } -uint8_t ptc_process_calibrate (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; - uint8_t cc_fine = ((uint8_t) compensation >> 4) & 0x0F; - uint8_t cc_coarse = (uint8_t)(compensation >> 8) & 0x0F; - uint8_t cc_add_rough = (uint8_t)(compensation >> 8) & 0xC0; - uint8_t cc_rough = (uint8_t)(compensation >> 12) & 0x03; - - //uint8_t err = 0; - - int8_t dirOvf; - int8_t dir; - if (rawData > 0x0200) { - rawData = rawData - 0x1FF; - if (rawData < 10) { - return PTC_LIB_CALIB_DONE; - } + uint16_t compensation = node->hw_compCaps; + uint8_t cc_accurate = compensation & 0x0F; + uint8_t cc_fine = ((uint8_t) compensation >> 4) & 0x0F; + uint8_t cc_coarse = (uint8_t)(compensation >> 8) & 0x0F; + uint8_t cc_add_rough = (uint8_t)(compensation >> 8) & 0xC0; + uint8_t cc_rough = (uint8_t)(compensation >> 12) & 0x03; + + //uint8_t err = 0; + + int8_t dirOvf; + int8_t dir; + if (rawData > 0x01FF) { + rawData = rawData - 0x1FF; + if (rawData < 4) { + return PTC_LIB_CALIB_DONE; + } - if (node->type & NODE_MUTUAL_bm){ - dir = -1; - dirOvf = -6; - rawData /= 2; - } else { - dir = 1; - dirOvf = 6; - if (rawData > 0x0100) { - if (cc_add_rough <= 0xC0) { - cc_add_rough = (cc_add_rough + 0x40); - rawData -= 0xF0; - } + if (node->type & NODE_MUTUAL_bm) { + dir = -1; + dirOvf = -6; + rawData /= 2; + } else { + dir = 1; + dirOvf = 6; + if (rawData > 0x0100) { + if (cc_add_rough <= 0xC0) { + cc_add_rough = (cc_add_rough + 0x40); + rawData -= 0xF0; } } + } + } else { + rawData = 0x1FF - rawData; + if (rawData < 4) { + return PTC_LIB_CALIB_DONE; + } + + if (node->type & NODE_MUTUAL_bm) { + dir = 1; + dirOvf = 6; + rawData /= 2; } else { - rawData = 0x1FF - rawData; - if (rawData < 10) { - return PTC_LIB_CALIB_DONE; + dir = -1; + dirOvf = -6; + if (rawData > 0x0100) { + if (cc_add_rough >= 0x40) { + cc_add_rough = (cc_add_rough - 0x40); + rawData -= 0xF0; + } + } + } + } + + while (rawData > 0x0001) { + while (rawData > 0x00CF) { + cc_rough += dir; // this algorithm takes advantage of integer underflow + if (cc_rough > 0x03) { // by checking against >0x03, we can also check for 0xFF aka -1 + cc_rough -= dir; // thus saving some flash. +/-1 can be made in one insn + break; // by using sub reg with 0xFF or 0x01 in a reg respectively } + rawData -= 0xCF; + } - if (node->type & NODE_MUTUAL_bm){ - dir = 1; - dirOvf = 6; - rawData /= 2; + while (rawData > 0x0015) { + cc_coarse += dir; + if (cc_coarse > 0x0F) { + break; + } + rawData -= 0x15; + } + if (cc_coarse > 0x0F) { + cc_rough += dir; + if (cc_rough > 0x03) { + cc_rough -= dir; + cc_coarse -= dir; } else { - dir = -1; - dirOvf = -6; - if (rawData > 0x0100) { - if (cc_add_rough >= 0x40) { - cc_add_rough = (cc_add_rough - 0x40); - rawData -= 0xF0; - } - } + cc_coarse -= dirOvf; } } + while (rawData > 0x0001) { - while (rawData > 0x00CF) { - cc_rough += dir; // this algorithm takes advantage of integer underflow - if (cc_rough > 0x03) { // by checking against >0x03, we can also check for 0xFF aka -1 - cc_rough -= dir; // thus saving some flash. +/-1 can be made in one insn - break; // by using sub reg with 0xFF or 0x01 in a reg respectively - } - rawData -= 0xCF; + cc_fine += dir; + if (cc_fine > 0x0F) { + break; } + rawData -= 0x02; + } - while (rawData > 0x0015) { - cc_coarse += dir; - if (cc_coarse > 0x0F) - break; - rawData -= 0x15; - } + if (cc_fine > 0x0F) { + cc_coarse += dir; if (cc_coarse > 0x0F) { cc_rough += dir; if (cc_rough > 0x03) { cc_rough -= dir; cc_coarse -= dir; - } else { - cc_coarse -= dirOvf; - } - } - - - while (rawData > 0x0001) { - cc_fine += dir; - if (cc_fine > 0x0F) - break; - rawData -= 0x02; - } - - if (cc_fine > 0x0F) { - cc_coarse += dir; - if (cc_coarse > 0x0F) { - cc_rough += dir; - if (cc_rough > 0x03) { - cc_rough -= dir; - cc_coarse -= dir; - cc_fine -= dir; - if (dir < 0) - return PTC_LIB_CALIB_TOO_LOW; - else - return PTC_LIB_CALIB_TOO_HIGH; + cc_fine -= dir; + if (dir < 0) { + return PTC_LIB_CALIB_TOO_LOW; } else { - cc_coarse -= dirOvf; + return PTC_LIB_CALIB_TOO_HIGH; } } else { - cc_fine -= dirOvf; + cc_coarse -= dirOvf; } - } /* if (cc_fine > 0x0F) */ - } /* (rawData > 0x0001) */ + } else { + cc_fine -= dirOvf; + } + } /* if (cc_fine > 0x0F) */ + } /* (rawData > 0x0001) */ - cc_fine <<= 4; - cc_rough <<= 4; - cc_rough |= cc_add_rough; - cc_rough |= cc_coarse; - cc_fine |= cc_accurate; - node->hw_compCaps = (uint16_t)((cc_rough << 8) | cc_fine); - return PTC_LIB_SUCCESS; + cc_fine <<= 4; + cc_rough <<= 4; + cc_rough |= cc_add_rough; + cc_rough |= cc_coarse; + cc_fine |= cc_accurate; + node->hw_compCaps = (uint16_t)((cc_rough << 8) | cc_fine); + return PTC_LIB_SUCCESS; #elif defined (__PTC_DA__) - int8_t dir; - if (rawData > 0x0200) { - rawData = rawData - 0x1FF; - if (rawData < 5) { - return PTC_LIB_CALIB_DONE; - } + uint16_t delta; + uint16_t newCC = node->hw_compCaps; + uint8_t ret_val = PTC_LIB_SUCCESS; - if (node->type & NODE_MUTUAL_bm) { - dir = -1; - rawData /= 2; - } else { - dir = 1; - } + if (node->type & NODE_MUTUAL_bm) { + if (rawData > 0x1FF) { + delta = (rawData - 0x1FF) >> 2; + newCC -= delta; } else { - rawData = 0x1FF - rawData; - if (rawData < 5) { - return PTC_LIB_CALIB_DONE; - } + delta = (0x1FF - rawData) >> 2; + newCC += delta; + } + } else { + if (rawData > 0x1FF) { + delta = (rawData - 0x1FF) >> 1; + newCC += delta; + } else { + delta = (0x1FF - rawData) >> 1; + newCC -= delta; + } + } - if (node->type & NODE_MUTUAL_bm) { - dir = 1; - rawData /= 2; - } else { - dir = -1; - } + if (delta < 4) { + return PTC_LIB_CALIB_DONE; + } + + if (newCC > 0x03FF) { + if (newCC > 0x7FFF) { // underflow + newCC = 0x00; + ret_val = PTC_LIB_CALIB_TOO_LOW; + } else { // overflow + newCC = 0x3FF; + ret_val = PTC_LIB_CALIB_TOO_HIGH; } - node->hw_compCaps += (int16_t)(dir * rawData); + } + node->hw_compCaps = newCC; + return ret_val; #endif } void ptc_init_conversion(uint8_t nodeType) { PTC_t *pPTC; - _fastPtr_d(pPTC,&PTC); + _fastPtr_d(pPTC, &PTC); - if (ptc_lib_state != PTC_LIB_IDLE) + if (ptc_lib_state & (PTC_LIB_CONV_PROG | PTC_LIB_CONV_LP | PTC_LIB_CONV_WCMP | PTC_LIB_SUSPENDED)) { return; + } -#if defined (__PTC_Tiny__) + #if defined (__PTC_Tiny__) pPTC->CTRLP = 0x00; - if (nodeType == NODE_MUTUAL_bm) { /* NODE_MUTUAL */ + if (nodeType & NODE_MUTUAL_bm) { /* NODE_MUTUAL */ pPTC->CTRLP = 0xC0 | 0x20; pPTC->SHIELD = 0x00; - } else if (nodeType == NODE_SELFCAP_bm){ /* NODE_SELFCAP */ - pPTC->CTRLP = 0x28; - pPTC->SHIELD = 0x00; - } else if (nodeType == NODE_SELFCAP_SHIELD_bm) { + } else if (nodeType & NODE_SHIELD_bm) { /* NODE_SELFCAP */ pPTC->CTRLP = 0x28; pPTC->SHIELD = 0x86; + } else if (nodeType & NODE_SELFCAP_bm) { + pPTC->CTRLP = 0x28; + pPTC->SHIELD = 0x00; } else { return; } @@ -800,39 +873,35 @@ void ptc_init_conversion(uint8_t nodeType) { pPTC->CTRLA = 0x00; uint8_t freq = freq_select; - if (freq > 0x0F) /* FREQ_SELECT_15 */ + if (freq > 0x0F) { /* FREQ_SELECT_15 */ freq = ADC_ASDV_bm; + } + pPTC->CTRLD = freq; pPTC->INTFLAGS = ADC_RESRDY_bm | ADC_WCMP_bm; // clear ISR flags, if there were unhandled currConvType = nodeType; - if (NULL == lowPowerNode) { - pPTC->INTCTRL = ADC_RESRDY_bm; - pPTC->CTRLE = ADC_WINCM_NONE_gc; - ptc_lib_state = PTC_LIB_CONV_PROG; - ptc_start_conversion(firstNode); - } else { + if (NULL != lowPowerNode) { pPTC->INTCTRL = ADC_WCMP_bm; // Wakeup only above of window pPTC->CTRLE = ADC_WINCM_ABOVE_gc; - pPTC->WINHT = (lowPowerNode->reference + lowPowerNode->touch_in_th) << (lowPowerNode->hw_a_d_gain & 0x0F); + pPTC->WINHT = (lowPowerNode->reference + lowPowerNode->touch_in_th) << (lowPowerNode->hw_gain_ovs & 0x0F); ptc_lib_state = PTC_LIB_EVENT; ptc_start_conversion(lowPowerNode); + } else { + pPTC->INTCTRL = ADC_RESRDY_bm; + pPTC->CTRLE = ADC_WINCM_NONE_gc; + ptc_lib_state = PTC_LIB_CONV_PROG; + ptc_start_conversion(firstNode); } -#elif defined (__PTC_DA__) -// The following code is a placeholder, DA not really started yet + #elif defined (__PTC_DA__) + if (nodeType == NODE_MUTUAL_bm) { pPTC->CTRL_SC = 0x00; - //PTC.CTRL_BOOST = 0x00; pPTC->CTRLA = 0x00; - } else if (nodeType == NODE_SELFCAP_bm || nodeType == NODE_SELFCAP_SHIELD_bm) { + } else if (nodeType & NODE_SELFCAP_bm) { pPTC->CTRL_SC = 0x01; - //PTC.CTRL_BOOST = 0x00; pPTC->CTRLA = 0x00; - //} else if (nodeType == 0x82) { // unused - // PTC.CTRL_SC = 0x01; - // PTC.CTRL_BOOST = 0x09; - // PTC.CTRLA = 0x00; } else { return; } @@ -841,25 +910,27 @@ void ptc_init_conversion(uint8_t nodeType) { uint8_t freq = freq_select; if (nodeType == NODE_MUTUAL_bm) { pPTC->SAMPDLY = 0x00; - if (freq < 0x10) /* freq is 0 - 0x0F, 0x10 is auto variation */ + if (freq < 0x10) { /* freq is 0 - 0x0F, 0x10 is auto variation */ freq |= ADC_INITDLY_DLY16_gc; - else + } else { freq = ADC_INITDLY_DLY16_gc | ADC_SAMPDLY_DLY15_gc; + } pPTC->CTRLD = freq; } else { pPTC->CTRLD = ADC_INITDLY_DLY16_gc; - if (freq > 0x0F) + if (freq > 0x0F) { freq = 0x0F; + } pPTC->SAMPDLY = freq; } - pPTC->INTFLAGS = ADC_RESRDY_bm | ADC_WCMP_bm; // clear ISR flags, if there were unhandled + pPTC->INTFLAGS = ADC_RESRDY_bm | ADC_WCMP_bm; // clear ISR flags, if they were unhandled currConvType = nodeType; if (NULL != lowPowerNode) { pPTC->INTCTRL = ADC_WCMP_bm; // Wakeup only above of window pPTC->CTRLE = ADC_WINCM_ABOVE_gc; - pPTC->WINHT = (lowPowerNode->reference + lowPowerNode->touch_in_th) << (lowPowerNode->hw_a_d_gain & 0x0F); + pPTC->WINHT = (lowPowerNode->reference + lowPowerNode->touch_in_th) << (lowPowerNode->hw_gain_ovs & 0x0F); ptc_lib_state = PTC_LIB_EVENT; ptc_start_conversion(lowPowerNode); } else { @@ -868,10 +939,10 @@ void ptc_init_conversion(uint8_t nodeType) { ptc_lib_state = PTC_LIB_CONV_PROG; ptc_start_conversion(firstNode); } -#endif + #endif } -void ptc_start_conversion (cap_sensor_t* node) { +void ptc_start_conversion(cap_sensor_t *node) { while (1) { if (NULL == node) { PTC.INTCTRL = 0; // disable ISR for ADC reuse @@ -887,105 +958,108 @@ void ptc_start_conversion (cap_sensor_t* node) { currConvNode = node; - ptc_set_registers(node); -} - -void ptc_set_registers(cap_sensor_t* node) { PTC_t *pPTC; - _fastPtr_d(node,node); // Sometimes it takes the compiler a bit more of convincing... - _fastPtr_d(pPTC,&PTC); - - if (NULL == node) - return; - - uint8_t analogGain = 0x3F; - if ((node->state.disabled == 0) && (node->stateMachine != PTC_SM_NOINIT_CAL)) { - uint8_t lut_index = node->hw_a_d_gain / 16; // A little workaround as >> 4 is kinda broken sometimes. - analogGain = ptc_a_gain_lut[lut_index]; + _fastPtr_d(pPTC, &PTC); + _fastPtr_d(node, node); + uint8_t analogGain = PTC_GAIN_MAX; + if (node->stateMachine != PTC_SM_NOINIT_CAL) { + analogGain = node->hw_gain_ovs / 16; // A little workaround as >> 4 is kinda broken sometimes. } uint8_t chargeDelay = node->hw_csd; + + #if defined(__PTC_Tiny__) + pPTC->XBM = node->hw_xCh_bm[0]; + pPTC->YBM = node->hw_yCh_bm[0]; + if (chargeDelay < 0x1B) { chargeDelay += 4; } else { chargeDelay = 0x1F; } - - #if defined (__PTC_Tiny__) - pPTC->YBM = node->hw_yCh_bm; - pPTC->XBM = node->hw_xCh_bm; - pPTC->COMP = node->hw_compCaps; - pPTC->AGAIN = analogGain; - pPTC->SAMPCTRL = chargeDelay; - pPTC->CTRLB = node->hw_a_d_gain & 0x0F; - pPTC->RES = node->hw_rsel_presc / 16; - pPTC->CTRLC = (node->hw_rsel_presc & 0x0F) | ADC_REFSEL_VDDREF_gc; - pPTC->CTRLP |= 0x03; - pPTC->CTRLA = ADC_RUNSTBY_bm | ADC_ENABLE_bm; /* 0x81 */ - - if (0 == node->state.low_power) - pPTC->COMMAND = 0x01; // Normal operation: Manual Start - else - pPTC->EVCTRL = 0x01; // Low Power: Start by positive Flank on Event - #elif defined (__PTC_DA__) - + pPTC->SAMPCTRL = chargeDelay; + + + pPTC->CTRLC = (node->hw_rsel_presc & 0x0F) | ADC_REFSEL_VDDREF_gc; + pPTC->CTRLP |= 0x03; + + #elif defined(__PTC_DA__) + ((uint8_t *)&pPTC->XBM)[0] = node->hw_xCh_bm[0]; // avoid memcpy to reduce register pressure + ((uint8_t *)&pPTC->XBM)[1] = node->hw_xCh_bm[1]; // avoiding memcpy means we can put &PTC + ((uint8_t *)&pPTC->XBM)[2] = node->hw_xCh_bm[2]; // and node in the Z/Y-Registers and just use + ((uint8_t *)&pPTC->XBM)[3] = node->hw_xCh_bm[3]; // the fast and memory efficient std/ldd instructions + ((uint8_t *)&pPTC->XBM)[4] = node->hw_xCh_bm[4]; + + ((uint8_t *)&pPTC->YBM)[0] = node->hw_yCh_bm[0]; + ((uint8_t *)&pPTC->YBM)[1] = node->hw_yCh_bm[1]; + ((uint8_t *)&pPTC->YBM)[2] = node->hw_yCh_bm[2]; + ((uint8_t *)&pPTC->YBM)[3] = node->hw_yCh_bm[3]; + ((uint8_t *)&pPTC->YBM)[4] = node->hw_yCh_bm[4]; + #if __PTC_Pincount__ >= 40 + ((uint8_t *)&pPTC->XBM)[5] = node->hw_xCh_bm[5]; + ((uint8_t *)&pPTC->YBM)[5] = node->hw_yCh_bm[5]; #endif -} -void ptc_eoc(void) { - PTC_t *pPTC; - volatile cap_sensor_t *pCurrentNode; // volatile needed to pass type check - _fastPtr_d(pPTC,&PTC); - _fastPtr_d(pCurrentNode,currConvNode); + if (chargeDelay < 0x1B) { + chargeDelay += 4; + } else { + chargeDelay = 0x1F; + } - if (NULL == pCurrentNode) - return; + pPTC->SAMPCTRL = chargeDelay; + pPTC->CTRLC = (node->hw_rsel_presc & 0x0F) | 0x80; - pPTC->CTRLA = 0x00; - uint8_t flags = pPTC->INTFLAGS; // save the flags before they get cleared by RES read - uint16_t rawVal = pPTC->RES; // clears ISR flags - uint8_t oversampling = pCurrentNode->hw_a_d_gain & 0x0F; - pCurrentNode->sensorData = rawVal >> oversampling; + if (node->type & NODE_SELFCAP_bm) { + pPTC->CTRL_SC = 0x01; + } else { + pPTC->CTRL_SC = 0x00; + } + if (node->type & NODE_SHIELD_bm) { + pPTC->CTRL_SHIELD = 1; + } else { + pPTC->CTRL_SHIELD = 0; + } + #endif - //currConvNode->sensorData = pPTC->RES_TRUE; - pCurrentNode->state.data_ready = 1; + pPTC->COMP = node->hw_compCaps; + pPTC->AGAIN = analogGain; + pPTC->CTRLB = node->hw_gain_ovs & 0x0F; + pPTC->RSEL = node->hw_rsel_presc / 16; - if (pCurrentNode->state.low_power) { - if (flags & ADC_WCMP_bm) { - pCurrentNode->state.win_comp = 1; - ptc_lib_state = PTC_LIB_CONV_WCMP; - } else { - pCurrentNode->state.win_comp = 0; - ptc_lib_state = PTC_LIB_CONV_LP; - } + pPTC->CTRLA = ADC_RUNSTBY_bm | ADC_ENABLE_bm; /* 0x81 */ + if (0 == node->state.low_power) { + pPTC->COMMAND = 0x01; // Normal operation: Manual Start } else { - ptc_start_conversion(pCurrentNode->nextNode); + pPTC->EVCTRL = 0x01; // Low Power: Start by positive flank on event } } + // returns the last node in the list, or NULL if list empty. -cap_sensor_t* ptc_get_last_node (void) { - if (firstNode == NULL) +cap_sensor_t *ptc_get_last_node(void) { + if (firstNode == NULL) { return NULL; + } cap_sensor_t *node = firstNode; while (1) { cap_sensor_t *nextNode = node->nextNode; - if (nextNode == NULL) + if (nextNode == NULL) { return node; - else + } else { node = nextNode; + } } } // puts the node to the back of the single-linked list. -// returns NULL if list was empty, otherwise the previous last node -uint8_t ptc_append_node(cap_sensor_t* pNewNode) { - cap_sensor_t* lastNode = ptc_get_last_node(); +uint8_t ptc_append_node(cap_sensor_t *pNewNode) { + + cap_sensor_t *lastNode = ptc_get_last_node(); if (lastNode == NULL) { firstNode = pNewNode; pNewNode->id = 0; @@ -998,15 +1072,47 @@ uint8_t ptc_append_node(cap_sensor_t* pNewNode) { return PTC_LIB_SUCCESS; } +void ptc_eoc(void) { + PTC_t *pPTC; + volatile cap_sensor_t *pCurrentNode; // volatile needed to pass type check + _fastPtr_d(pPTC, &PTC); + _fastPtr_d(pCurrentNode, currConvNode); + + if (NULL == pCurrentNode) { + return; + } + + pPTC->CTRLA = 0x00; + uint8_t flags = pPTC->INTFLAGS; // save the flags before they get cleared by RES read + uint16_t rawVal = pPTC->RES; // clears ISR flags + uint8_t oversampling = pCurrentNode->hw_gain_ovs & 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; + ptc_lib_state = PTC_LIB_CONV_WCMP; + } else { + pCurrentNode->state.win_comp = 0; + ptc_lib_state = PTC_LIB_CONV_LP; + } + + } else { + ptc_start_conversion(pCurrentNode->nextNode); + } +} + #if defined(__PTC_Tiny__) ISR(ADC0_RESRDY_vect) { ptc_eoc(); } -ISR(ADC0_WCOMP_vect) { - ptc_eoc(); -} +ISR(ADC0_WCOMP_vect, ISR_ALIASOF(ADC0_RESRDY_vect)); + #elif defined(__PTC_DA__) ISR(PTC_PTC_vect) { ptc_eoc(); diff --git a/megaavr/libraries/PTC/src/ptc.h b/megaavr/libraries/PTC/src/ptc.h index c074f62d..3854be43 100644 --- a/megaavr/libraries/PTC/src/ptc.h +++ b/megaavr/libraries/PTC/src/ptc.h @@ -1,5 +1,5 @@ /* - ptc library to use the PTC module in AVR devices + library to use the PTC module in AVR devices Copyright (c) 2023, MX682X This library is free software; you can redistribute it and/or @@ -17,14 +17,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA This license applies to all files that are part of this library - (ptc.c, ptc.h, ptc.h, ptc.h) + (ptc.c, ptc.h, ptc_io.h, ptc_types.h) */ #pragma once -#ifndef PTC_TOUCH_H -#define PTC_TOUCH_H +#ifndef PTC_H +#define PTC_H #if defined(MEGATINYCORE) || defined(DXCORE) #include @@ -35,9 +35,8 @@ #endif #endif -#include "ptc_io.h" #include "ptc_types.h" - +#include "ptc_io.h" #ifndef NULL @@ -56,18 +55,8 @@ extern "C" { -// Paranoid Pointer validation -// all attinies have a RAM address where the higher byte starts with 0x38. -// Devices with smaller memories start at 0x3E or 0x3F, but both have the same bits set as 0x38. -// if it's Flash, it will be 0x80, as signed it means negative. -// if it's anything else (I/O, EEPROM, etc.), p will be <= 14 -// thus making a signed check will make pretty sure the pointer points to a RAM address -// Disadvantage: GCC can not validate this at compile time as it does not know the variable's -// address at compile-time. -#define PTC_CHECK_POINTER(__p__, __ret__) \ - if (((int8_t)((uint16_t)__p__ >> 8)) < 0x38) { \ - return __ret__; \ - } + + #define PTC_CHECK_FOR_BAD_POINTER(__p__) \ if (NULL == __p__) { \ @@ -78,27 +67,28 @@ extern "C" { } +void badArg(const char *) __attribute__((weak, error(""))); +void badCall(const char *) __attribute__((weak, error(""))); -//extern void ptc_conversion_complete(uint8_t type); -//extern void ptc_error_callback(uint8_t source, cap_sensor_t* node); -extern void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t* node); - -extern void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t* node); -extern void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t* node); -extern void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t* node); -extern void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t* node); -extern void ptc_event_cb_error(const ptc_cb_event_t eventType, cap_sensor_t* node); +//extern void ptc_conversion_complete(uint8_t type); +//extern void ptc_error_callback(uint8_t source, cap_sensor_t *node); +extern void ptc_event_callback(const ptc_cb_event_t eventType, cap_sensor_t *node); +extern void ptc_event_cb_touch(const ptc_cb_event_t eventType, cap_sensor_t *node); +extern void ptc_event_cb_wake(const ptc_cb_event_t eventType, cap_sensor_t *node); +extern void ptc_event_cb_conversion(const ptc_cb_event_t eventType, cap_sensor_t *node); +extern void ptc_event_cb_calibration(const ptc_cb_event_t eventType, cap_sensor_t *node); +extern void ptc_event_cb_error(const ptc_cb_event_t eventType, cap_sensor_t *node); // Enables the node. Can be called while an acquisition is in progress. -uint8_t ptc_enable_node(cap_sensor_t* node); +uint8_t ptc_enable_node(cap_sensor_t *node); // Disables a node. If the conversion is started, it will be finished -uint8_t ptc_disable_node(cap_sensor_t* node); +uint8_t ptc_disable_node(cap_sensor_t *node); // Can be used outside an acquisition process to select ADC/SELFCAP/MUTUAL/SHIELD void ptc_set_next_conversion_type(ptc_node_type_t type); @@ -107,41 +97,112 @@ void ptc_set_next_conversion_type(ptc_node_type_t type); void ptc_process(uint16_t currTime); -// 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); +// Set the threshold for touch detection and away from touch for a node. +// a "0" will be interpreted as don't change +uint8_t ptc_node_set_thresholds(cap_sensor_t *node, int16_t th_in, int16_t th_out); // Change Resistor Setting. Note: Only has an effect on mutual sensors -uint8_t ptc_node_set_resistor(cap_sensor_t* node, uint8_t res); +uint8_t ptc_node_set_resistor(cap_sensor_t *node, ptc_rsel_t res); + +// Change prescaler. +uint8_t ptc_node_set_prescaler(cap_sensor_t *node, ptc_presc_t presc); + +// Sets the gain through adjusting the charge integrator (increases the sensitivity (and noise)) +uint8_t ptc_node_set_gain(cap_sensor_t *node, ptc_gain_t gain); -// Change preschaler. -uint8_t ptc_node_set_prescaler(cap_sensor_t* node, uint8_t presc); +// Sets the number of oversamples. (the value is right-shifted automatically (reduces noise)) +uint8_t ptc_node_set_oversamples(cap_sensor_t *node, uint8_t ovs); -uint8_t ptc_node_set_gain(cap_sensor_t* node, uint8_t aGain, uint8_t dGain); +// Sets the number of additional PTC Clocks for sampling a node. See also: ADC.SAMPCTRL +uint8_t ptc_node_set_charge_share_delay(cap_sensor_t *node, uint8_t csd); -// this two functions assume that yCh and xCh have validated values. Don't call these directly -uint8_t ptc_add_selfcap_node_asserted(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh); -uint8_t ptc_add_mutualcap_node_asserted(cap_sensor_t* node, ptc_ch_bm_t yCh, ptc_ch_bm_t xCh); +// this is an internal function, there is no sense in calling it directly +uint8_t ptc_add_node(cap_sensor_t *node, uint8_t *pCh, const uint8_t type); -inline uint8_t ptc_add_selfcap_node(cap_sensor_t* node, const ptc_ch_bm_t yCh, const ptc_ch_bm_t xCh) { - if(__builtin_constant_p(yCh)) { - if (yCh == 0) badArg("yCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); - if (yCh & xCh) badArg("pin bitmap overlap detected"); - } else if ((yCh == 0) || ((yCh & xCh) != 0)) { - return PTC_LIB_BAD_ARGUMENT; // We need at least one pin to connect to + +inline uint8_t ptc_add_selfcap_node(cap_sensor_t *node, const ptc_ch_bm_t xCh, const ptc_ch_bm_t yCh) { + if (__builtin_constant_p(yCh)) { + if (yCh == 0) { + badArg("yCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); + } + if (yCh & xCh) { + badArg("pin bitmap overlap detected"); + } } - return ptc_add_selfcap_node_asserted(node, yCh, xCh); + + // this places only the significant number of bits on stack. Significantly reduces register pressure on DAs + const uint8_t tsize = sizeof(ptc_ch_arr_t); + uint8_t pCh[tsize * 2]; + + pCh[0] = (uint8_t)(xCh >> 0); + pCh[0 + tsize] = (uint8_t)(yCh >> 0); + + #if __PTC_Pincount__ >= 8 + pCh[1] = (uint8_t)(xCh >> 8); + pCh[1 + tsize] = (uint8_t)(yCh >> 8); + + #if __PTC_Pincount__ >= 16 + pCh[2] = (uint8_t)(xCh >> 16); + pCh[2 + tsize] = (uint8_t)(yCh >> 16); + pCh[3] = (uint8_t)(xCh >> 24); + pCh[3 + tsize] = (uint8_t)(yCh >> 24); + pCh[4] = (uint8_t)(xCh >> 32); + pCh[4 + tsize] = (uint8_t)(yCh >> 32); + + #if __PTC_Pincount__ >= 40 + pCh[5] = (uint8_t)(xCh >> 40); + pCh[5 + tsize] = (uint8_t)(yCh >> 40); + + #endif + #endif + #endif + + return ptc_add_node(node, pCh, NODE_SELFCAP_bm); }; -inline uint8_t ptc_add_mutualcap_node(cap_sensor_t* node, const ptc_ch_bm_t yCh, const ptc_ch_bm_t xCh) { - if(__builtin_constant_p(yCh) && __builtin_constant_p(xCh)) { - if (yCh == 0) badArg("yCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); - if (xCh == 0) badArg("xCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); - if (yCh & xCh) badArg("pin overlap detected"); - } else if ((yCh == 0 || xCh == 0) || ((yCh & xCh) != 0)) { - return PTC_LIB_BAD_ARGUMENT; // mutual requires at least one pin on y-Channel and x-Channel + +inline uint8_t ptc_add_mutualcap_node(cap_sensor_t *node, const ptc_ch_bm_t xCh, const ptc_ch_bm_t yCh) { + if (__builtin_constant_p(yCh) && __builtin_constant_p(xCh)) { + if (yCh == 0) { + badArg("yCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); + } + if (xCh == 0) { + badArg("xCh bitmap mustn't be 0 (Pin_Pxn is not a PTC pin)"); + } + if (yCh & xCh) { + badArg("pin overlap detected"); + } } - return ptc_add_mutualcap_node_asserted(node, yCh, xCh); + + // this places only the significant number of bits on stack. Significantly reduces register pressure on DAs + const uint8_t tsize = sizeof(ptc_ch_arr_t); + uint8_t pCh[tsize * 2]; + + pCh[0] = (uint8_t)(xCh >> 0); + pCh[0 + tsize] = (uint8_t)(yCh >> 0); + + #if __PTC_Pincount__ >= 8 + pCh[1] = (uint8_t)(xCh >> 8); + pCh[1 + tsize] = (uint8_t)(yCh >> 8); + + #if __PTC_Pincount__ >= 16 + pCh[2] = (uint8_t)(xCh >> 16); + pCh[2 + tsize] = (uint8_t)(yCh >> 16); + pCh[3] = (uint8_t)(xCh >> 24); + pCh[3 + tsize] = (uint8_t)(yCh >> 24); + pCh[4] = (uint8_t)(xCh >> 32); + pCh[4 + tsize] = (uint8_t)(yCh >> 32); + + #if __PTC_Pincount__ >= 40 + pCh[5] = (uint8_t)(xCh >> 40); + pCh[5 + tsize] = (uint8_t)(yCh >> 40); + + #endif + #endif + #endif + + return ptc_add_node(node, pCh, NODE_MUTUAL_bm); }; @@ -149,68 +210,81 @@ inline uint8_t ptc_add_mutualcap_node(cap_sensor_t* node, const ptc_ch_bm_t yCh, uint8_t ptc_suspend(void); void ptc_resume(void); -// If you want to know the compensation capacitance in femto Farrad -uint16_t ptc_get_node_cc_femto(cap_sensor_t* node); +// If you want to know the compensation capacitance in femto Farad +uint16_t ptc_get_node_cc_fempto(cap_sensor_t *node); -ptc_lib_sm_set_t* ptc_get_sm_settings(); +ptc_lib_sm_set_t *ptc_get_sm_settings(); // X and Y channel bitmasks -inline ptc_ch_bm_t ptc_get_node_xCh_bm(cap_sensor_t* node) { - if (node == NULL) return 0x00; - return node->hw_xCh_bm; +inline ptc_ch_bm_t ptc_get_node_xCh_bm(cap_sensor_t *node) { + if (node == NULL) { + return 0x00; + } + ptc_ch_bm_t retval = 0; + memcpy(&retval, node->hw_xCh_bm, sizeof(node->hw_xCh_bm)); + return retval; } -inline ptc_ch_bm_t ptc_get_node_yCh_bm(cap_sensor_t* node) { - if (node == NULL) return 0x00; - return node->hw_yCh_bm; +inline ptc_ch_bm_t ptc_get_node_yCh_bm(cap_sensor_t *node) { + if (node == NULL) { + return 0x00; + } + ptc_ch_bm_t retval = 0; + memcpy(&retval, node->hw_yCh_bm, sizeof(node->hw_yCh_bm)); + return retval; } // the measured PTC value. 512 is 0. 0x00 means BAD_POINTER -inline uint16_t ptc_get_node_sensor_value(cap_sensor_t* node) { - if (node == NULL) return 0x00; +inline uint16_t ptc_get_node_sensor_value(cap_sensor_t *node) { + if (node == NULL) { + return 0x00; + } return node->sensorData; } // 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; +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; } -inline uint8_t ptc_get_node_sm(cap_sensor_t* node) { +inline uint8_t ptc_get_node_sm(cap_sensor_t *node) { PTC_CHECK_FOR_BAD_POINTER(node); return node->stateMachine; } -inline int16_t ptc_get_node_delta(cap_sensor_t* node) { - if (node == NULL) +inline int16_t ptc_get_node_delta(cap_sensor_t *node) { + if (node == NULL) { return 0x8000; //-32k - impossible value for normal operation - + } return (node->sensorData - node->reference); } -inline uint8_t ptc_get_node_state(cap_sensor_t* node) { - if (node == NULL) +inline uint8_t ptc_get_node_state(cap_sensor_t *node) { + if (node == NULL) { return 0xFF; - + } return (node->stateMachine); } -inline ptc_id_t ptc_get_node_id(cap_sensor_t* node) { - if (node == NULL) +inline ptc_id_t ptc_get_node_id(cap_sensor_t *node) { + if (node == NULL) { return 0xFF; - + } return (node->id); } -inline uint8_t ptc_node_request_recal(cap_sensor_t* node) { +inline uint8_t ptc_node_request_recal(cap_sensor_t *node) { PTC_CHECK_FOR_BAD_POINTER(node); node->stateMachine = PTC_SM_NOINIT_CAL; @@ -220,7 +294,7 @@ inline uint8_t ptc_node_request_recal(cap_sensor_t* node) { void ptc_init_ADC0(void); -uint8_t ptc_lp_init(cap_sensor_t* node); +uint8_t ptc_lp_init(cap_sensor_t *node, uint8_t event_ch); uint8_t ptc_lp_disable(void); uint8_t ptc_lp_was_waken(void); diff --git a/megaavr/libraries/PTC/src/ptc_io.h b/megaavr/libraries/PTC/src/ptc_io.h index a80f24a4..854a7f5d 100644 --- a/megaavr/libraries/PTC/src/ptc_io.h +++ b/megaavr/libraries/PTC/src/ptc_io.h @@ -3,8 +3,8 @@ */ #pragma once -#ifndef PTC_TOUCH_IO_H -#define PTC_TOUCH_IO_H +#ifndef PTC_IO_H +#define PTC_IO_H #ifdef __cplusplus extern "C" { @@ -35,7 +35,6 @@ typedef struct PTC_struct { _WORDREGISTER(WINHT); /* Window comparator high threshold */ register8_t CALIB; /* Calibration */ register8_t reserved_2; /* +0x17 */ - /* Any register Name below this point is based on speculation */ register8_t CTRLP; /* +0x18 OR with 0x03 to enable, written 0x28 for Selfcap */ register8_t RSEL; /* +0x19 */ _WORDREGISTER(COMP); /* +0x1A */ @@ -46,398 +45,374 @@ typedef struct PTC_struct { _WORDREGISTER(RES_PTC); /* +0x20 Some Result, written by PTC. Seems to be RES, but left-shifted, or result from PTC accumulated by ADC */ _WORDREGISTER(PIN_OVR); /* +0x22 all X and Y pins OR'd together at init. Pin Function Overwrite Probably*/ _WORDREGISTER(reserved_5); /* +0x24 */ - _WORDREGISTER(XBM); /* +0x26 amount of writeable bits depends on chip-die family */ + _WORDREGISTER(XBM); /* +0x26 amount of writeable bits depends on chip family */ _WORDREGISTER(reserved_6); /* +0x28 */ _WORDREGISTER(YBM); /* +0x2A e.g. 0x3FFF (15 pins) for 1614 with only 6 PTC pins */ _WORDREGISTER(reserved_7);; /* +0x2C */ } PTC_t; #define PTC (*(PTC_t *) 0x0600) /* Analog to Digital Converter */ +#define RSEL_MAX RSEL_VAL_200 +#define PRSC_MAX ADC_PRESC_DIV256_gc + +#if F_CPU >= 12000000 // 16 MHz / 16 = 1.0 MHz, 20 MHz / 16 = 1.25 MHz +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV16_gc +#elif F_CPU >= 6000000 // 8 MHz / 8 = 1.0 MHz, 10 MHz / 8 = 1.25 MHz +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV8_gc +#elif F_CPU >= 3000000 // 4 MHz / 4 = 1.0 MHz, 5 MHz / 4 = 1.25 MHz +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV4_gc +#else // 1 MHz / 2 = 500 kHz - the lowest setting +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV2_gc +#endif #elif defined (__PTC_DA__) typedef struct PTC_struct { - /* Any register Name below this point is based on speculation */ - register8_t CTRLA; /* 0xC0 [0x81] Control A (RUNSTDBY, ENABLE)*/ + register8_t CTRLA; /* 0xC0 [0x81] Control A (RUNSTDBY, ENABLE (0x81))*/ register8_t CTRL_SC; /* 0xC1 [0x07] 0x01: Selfcap_EN, */ - register8_t CTRLC; /* 0xC2 [0x87] Control C (Prescaler | 0x80 (SampCap?))*/ + register8_t CTRLC; /* 0xC2 [0x87] Control C ((node_rsel_prsc & 0x0F) | 0x80 (SampCap? VREF?))*/ register8_t CTRLD; /* 0xC3 [0x6F] Control D (Max: 0x6F) Is always OR'd with 0x20 or limited to 0x2F */ register8_t SAMPCTRL; /* 0xC4 [0x7F] SAMPCTRL (Max: 0x7F) */ - register8_t SAMPDLY; /* 0xC5 [0x7F] 0~15 Inserted ADC clocks */ + register8_t SAMPDLY; /* 0xC5 [0x7F] 0~15 Inserted ADC clocks (freq_option_select) */ register8_t reserved_0; /* 0xC6 [0xFF] unused */ - register8_t CTRLB; /* 0xC7 [0x07] SAMPNUM */ + register8_t CTRLB; /* 0xC7 [0x07] SAMPNUM (node_oversampling) */ + register8_t CTRLE; /* 0xC8 [0x07] WINCM probably */ - register8_t RSEL; /* 0xC9 [0x77] Resistor Setting */ - register8_t CC_EN; /* 0xCA [0x01] Enable Compensation Caps? */ + register8_t RSEL; /* 0xC9 [0x77] Resistor Setting (node_rsel_prsc >> 4) */ + 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 */ + register8_t INTCTRL; /* 0xD2 [0x07] Interrupt Control: 0x01 - RESRDY, 0x02 - WINCMP */ register8_t INTFLAGS; /* 0xD3 [0x07] Interrupt Flags */ register8_t reserved_2; /* 0xD4 [0x02] unknown */ register8_t DEBUGCTRL; /* 0xD5 [0x01] DBGRUN */ - _WORDREGISTER(reserved_3); /* 0xD6 [0x00] unwritable */ + register8_t reserved_3; /* 0xD6 [0x00] unwritable */ + register8_t STATUS; /* 0xD7 [0x00] 0x20 during operation, unwritable */ + _WORDREGISTER(reserved_4); /* 0xD8 [0x00] some other result */ _WORDREGISTER(RES); /* 0xDA [0x00] ADC Result register */ _WORDREGISTER(WINLT); /* 0xDC [0xFF] Window comparator low threshold */ _WORDREGISTER(WINHT); /* 0xDD [0xFF] Window comparator high threshold */ + register8_t CTRL_SHIELD; /* 0xE0 [0x00] Strobing Register, changes between Shield and Mutual ?*/ register8_t SOME_RES; /* 0xE1 [0x81] some unknown result value */ _WORDREGISTER(reserved_5); /* 0xE2 [0x00] unwritable */ _WORDREGISTER(COMP); /* 0xE4 [0x3FF] Compensation */ register8_t AGAIN; /* 0xE6 [0x1F] Analog Gain */ register8_t reserved_6; /* 0xE7 [0x00] unwritable */ - register8_t XBM[8]; /* 0xE8 X Channel bitmask */ - register8_t YBM[8]; /* 0xF0 Y Channel bitmask */ - register8_t XBM_4P[8]; /* 0xF8 X Channel bm when using Boost mode, unused */ + + register8_t XBM[6]; /* 0xE8 X Channel bitmask */ + register8_t reserved_7[2]; /* 0xEE [0x00] unused */ + + register8_t YBM[6]; /* 0xF0 Y Channel bitmask */ + register8_t reserved_8[2]; /* 0xF6 [0x00] unused */ + + register8_t XBM_4P[6]; /* 0xF8 X Channel bm when using Boost mode, unused */ + register8_t reserved_9[2]; /* 0xFE [0x00] unused */ } PTC_t; #define PTC (*(PTC_t *) 0x07C0) /* Analog to Digital Converter */ - +#define RSEL_MAX RSEL_VAL_200 +#define PRSC_MAX PTC_PRESC_DIV16_gc + +#if F_CPU <= 400000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV2_gc +#elif F_CPU <= 800000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV4_gc +#elif F_CPU <= 1200000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV6_gc +#elif F_CPU <= 1600000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV8_gc +#elif F_CPU <= 2000000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV10_gc +#elif F_CPU <= 2400000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV12_gc +#elif F_CPU <= 2800000 +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV14_gc #else - #warning "Neither __PTC_Tiny__ nor __PTC_DA__ defined" +#define PTC_PRESC_DEFAULT PTC_PRESC_DIV16_gc +#endif +#else +#warning "Neither __PTC_Tiny__ nor __PTC_DA__ defined" #endif -#define RSEL_MAX RSEL_VAL_200 -#define PRSC_MAX ADC_PRESC_DIV256_gc - -#if F_CPU >= 12000000 // 16 MHz / 16 = 1.0 MHz, 20 MHz / 16 = 1.25 MHz - #define PTC_PRESC_DEFAULT ADC_PRESC_DIV16_gc -#elif F_CPU >= 6000000 // 8 MHz / 8 = 1.0 MHz, 10 MHz / 8 = 1.25 MHz - #define PTC_PRESC_DEFAULT ADC_PRESC_DIV8_gc -#elif F_CPU >= 3000000 // 4 MHz / 4 = 1.0 MHz, 5 MHz / 4 = 1.25 MHz - #define PTC_PRESC_DEFAULT ADC_PRESC_DIV4_gc -#else // 1 MHz / 2 = 500 kHz - the lowest setting - #define PTC_PRESC_DEFAULT ADC_PRESC_DIV2_gc -#endif #define PIN_TO_PTC(__pin__) (((__pin__) < NUM_TOTAL_PINS ) ? digital_pin_to_ptc_bm[__pin__] : 0x00) -const ptc_ch_bm_t digital_pin_to_ptc_bm [] = { -#if (__PTC_Pincount__ == 6) +static const ptc_ch_bm_t digital_pin_to_ptc_bm [] = { + #if (__PTC_Pincount__ == 6) 0x01 << 0, //PA4 - 0x01 << 1, //PA5 - 0x01 << 2, //PA6 - 0x01 << 3, //PA7 - 0x00, //PB3 - 0x00, //PB2 - 0x01 << 4, //PB1 - 0x01 << 5, //PB0 - 0x00, //PA1 - 0x00, //PA2 - 0x00, //PA3 - 0x00 //PA0 -#elif (__PTC_Pincount__ == 12) + 0x01 << 1, //PA5 + 0x01 << 2, //PA6 + 0x01 << 3, //PA7 + 0x00, //PB3 + 0x00, //PB2 + 0x01 << 4, //PB1 + 0x01 << 5, //PB0 + 0x00, //PA1 + 0x00, //PA2 + 0x00, //PA3 + 0x00 //PA0 + #elif (__PTC_Pincount__ == 12) 0x01 << 0, // 0 PA4 - 0x01 << 1, // 1 PA5 - 0x01 << 2, // 2 PA6 - 0x01 << 3, // 3 PA7 - 0x01 << 12, // 4 PB5 - 0x01 << 13, // 5 PB4 - 0x00, // 6 PB3 - 0x00, // 7 PB2 - 0x01 << 4, // 8 PB1 - // Right side, bottom to top - 0x01 << 5, // 9 PB0 - 0x01 << 6, // 10 PC0 - 0x01 << 7, // 11 PC1 - 0x01 << 8, // 12 PC2 - 0x01 << 9, // 13 PC3 - 0x00, // 14 PA1 - 0x00, // 15 PA2 - 0x00, // 16 PA3 - 0x00 // 17 PA0 -#elif (__PTC_Pincount__ == 14) + 0x01 << 1, // 1 PA5 + 0x01 << 2, // 2 PA6 + 0x01 << 3, // 3 PA7 + 0x01 << 12, // 4 PB5 + 0x01 << 13, // 5 PB4 + 0x00, // 6 PB3 + 0x00, // 7 PB2 + 0x01 << 4, // 8 PB1 + // Right side, bottom to top + 0x01 << 5, // 9 PB0 + 0x01 << 6, // 10 PC0 + 0x01 << 7, // 11 PC1 + 0x01 << 8, // 12 PC2 + 0x01 << 9, // 13 PC3 + 0x00, // 14 PA1 + 0x00, // 15 PA2 + 0x00, // 16 PA3 + 0x00 // 17 PA0 + #elif (__PTC_Pincount__ == 14) 0x01 << 0, // 0 PA4 - 0x01 << 1, // 1 PA5 - 0x01 << 2, // 2 PA6 - 0x01 << 3, // 3 PA7 - 0x00, // 4 PB7 - 0x00, // 5 PB6 - 0x01 << 12, // 6 PB5 - 0x01 << 13, // 7 PB4 - 0x00, // 8 PB3 - 0x00, // 9 PB2 - 0x01 << 4, // 10 PB1 - // Right side, bottom to top - 0x01 << 5, // 11 PB0 - 0x01 << 6, // 12 PC0 - 0x01 << 7, // 13 PC1 - 0x01 << 8, // 14 PC2 - 0x01 << 9, // 15 PC3 - 0x01 << 10, // 16 PC4 - 0x01 << 11, // 17 PC5 - 0x00, // 18 PA1 - 0x00, // 19 PA2 - 0x00, // 20 PA3 - 0x00 // 21 PA0 -#elif (__PTC_Pincount__ == 17) // DA (ToDo) + 0x01 << 1, // 1 PA5 + 0x01 << 2, // 2 PA6 + 0x01 << 3, // 3 PA7 + 0x00, // 4 PB7 + 0x00, // 5 PB6 + 0x01 << 12, // 6 PB5 + 0x01 << 13, // 7 PB4 + 0x00, // 8 PB3 + 0x00, // 9 PB2 + 0x01 << 4, // 10 PB1 + // Right side, bottom to top + 0x01 << 5, // 11 PB0 + 0x01 << 6, // 12 PC0 + 0x01 << 7, // 13 PC1 + 0x01 << 8, // 14 PC2 + 0x01 << 9, // 15 PC3 + 0x01 << 10, // 16 PC4 + 0x01 << 11, // 17 PC5 + 0x00, // 18 PA1 + 0x00, // 19 PA2 + 0x00, // 20 PA3 + 0x00 // 21 PA0 + #elif (__PTC_Pincount__ == 18) // DA (ToDo) 0x01ULL << 0, // 0 PA0 - 0x01ULL << 1, // 1 PA1 - 0x01ULL << 2, // 2 PA2/SDA - 0x01ULL << 3, // 3 PA3/SCL - 0x01ULL << 4, // 4 PA4/MOSI - 0x01ULL << 5, // 5 PA5/MISO - 0x01ULL << 6, // 6 PA6/SCK - 0x01ULL << 7, // 7 PA7/SS/CLKOUT - 0x00ULL, // 8 PC0/USART1_Tx - 0x00ULL, // 9 PC1/USART1_Rx - 0x00ULL, // 10 PC2 - 0x00ULL, // 11 PC3 - 0x01ULL << 16, // 12 PD0/AIN0 - 0x01ULL << 17, // 13 PD1/AIN1 - 0x01ULL << 18, // 14 PD2/AIN2 - 0x01ULL << 19, // 15 PD3/AIN3 - 0x01ULL << 20, // 16 PD4/AIN4 - 0x01ULL << 21, // 17 PD5/AIN5 - 0x01ULL << 22, // 18 PD6/AIN6 - 0x01ULL << 23, // 19 PD7/AIN7/AREF - 0x01ULL << 32, // 20 PF0/USART2_Tx/TOSC1 - 0x01ULL << 33, // 21 PF1/USART2_Rx/TOSC2 -#elif (__PTC_Pincount__ == 21) + 0x01ULL << 1, // 1 PA1 + 0x01ULL << 2, // 2 PA2/SDA + 0x01ULL << 3, // 3 PA3/SCL + 0x01ULL << 4, // 4 PA4/MOSI + 0x01ULL << 5, // 5 PA5/MISO + 0x01ULL << 6, // 6 PA6/SCK + 0x01ULL << 7, // 7 PA7/SS/CLKOUT + 0x00ULL, // 8 PC0/USART1_Tx + 0x00ULL, // 9 PC1/USART1_Rx + 0x00ULL, // 10 PC2 + 0x00ULL, // 11 PC3 + 0x01ULL << 16, // 12 PD0/AIN0 + 0x01ULL << 17, // 13 PD1/AIN1 + 0x01ULL << 18, // 14 PD2/AIN2 + 0x01ULL << 19, // 15 PD3/AIN3 + 0x01ULL << 20, // 16 PD4/AIN4 + 0x01ULL << 21, // 17 PD5/AIN5 + 0x01ULL << 22, // 18 PD6/AIN6 + 0x01ULL << 23, // 19 PD7/AIN7/AREF + 0x01ULL << 32, // 20 PF0/USART2_Tx/TOSC1 + 0x01ULL << 33, // 21 PF1/USART2_Rx/TOSC2 + #elif (__PTC_Pincount__ == 22) 0x01ULL << 0, // 0 PA0 - 0x01ULL << 1, // 1 PA1 - 0x01ULL << 2, // 2 PA2/SDA - 0x01ULL << 3, // 3 PA3/SCL - 0x01ULL << 4, // 4 PA4/MOSI - 0x01ULL << 5, // 5 PA5/MISO - 0x01ULL << 6, // 6 PA6/SCK - 0x01ULL << 7, // 7 PA7/SS/CLKOUT - 0x00ULL, // 8 PC0/USART1_Tx - 0x00ULL, // 9 PC1/USART1_Rx - 0x00ULL, // 10 PC2 - 0x00ULL, // 11 PC3 - 0x01ULL << 16, // 12 PD0/AIN0 - 0x01ULL << 17, // 13 PD1/AIN1 - 0x01ULL << 18, // 14 PD2/AIN2 - 0x01ULL << 19, // 15 PD3/AIN3 - 0x01ULL << 20, // 16 PD4/AIN4 - 0x01ULL << 21, // 17 PD5/AIN5 - 0x01ULL << 22, // 18 PD6/AIN6 - 0x01ULL << 23, // 19 PD7/AIN7/AREF - 0x01ULL << 32, // 20 PF0/USART2_Tx/TOSC1 - 0x01ULL << 33, // 21 PF1/USART2_Rx/TOSC2 - 0x01ULL << 34, // 22 PF2/AIN12 - 0x01ULL << 35, // 23 PF3/AIN13 - 0x01ULL << 36, // 24 PF4/AIN14/TCB0 PWM - 0x01ULL << 37, // 25 PF5/AIN15/TCB1 PWM -#elif (__PTC_Pincount__ == 31) + 0x01ULL << 1, // 1 PA1 + 0x01ULL << 2, // 2 PA2/SDA + 0x01ULL << 3, // 3 PA3/SCL + 0x01ULL << 4, // 4 PA4/MOSI + 0x01ULL << 5, // 5 PA5/MISO + 0x01ULL << 6, // 6 PA6/SCK + 0x01ULL << 7, // 7 PA7/SS/CLKOUT + 0x00ULL, // 8 PC0/USART1_Tx + 0x00ULL, // 9 PC1/USART1_Rx + 0x00ULL, // 10 PC2 + 0x00ULL, // 11 PC3 + 0x01ULL << 16, // 12 PD0/AIN0 + 0x01ULL << 17, // 13 PD1/AIN1 + 0x01ULL << 18, // 14 PD2/AIN2 + 0x01ULL << 19, // 15 PD3/AIN3 + 0x01ULL << 20, // 16 PD4/AIN4 + 0x01ULL << 21, // 17 PD5/AIN5 + 0x01ULL << 22, // 18 PD6/AIN6 + 0x01ULL << 23, // 19 PD7/AIN7/AREF + 0x01ULL << 32, // 20 PF0/USART2_Tx/TOSC1 + 0x01ULL << 33, // 21 PF1/USART2_Rx/TOSC2 + 0x01ULL << 34, // 22 PF2/AIN12 + 0x01ULL << 35, // 23 PF3/AIN13 + 0x01ULL << 36, // 24 PF4/AIN14/TCB0 PWM + 0x01ULL << 37, // 25 PF5/AIN15/TCB1 PWM + #elif (__PTC_Pincount__ == 32) 0x01ULL << 0, // 0 PA0 - 0x01ULL << 1, // 1 PA1 - 0x01ULL << 2, // 2 PA2/SDA - 0x01ULL << 3, // 3 PA3/SCL - 0x01ULL << 4, // 4 PA4/MOSI - 0x01ULL << 5, // 5 PA5/MISO - 0x01ULL << 6, // 6 PA6/SCK - 0x01ULL << 7, // 7 PA7/SS/CLKOUT/LED_BUILTIN - 0x01ULL << 8, // 8 PB0/USART3_Tx - 0x01ULL << 9, // 9 PB1/USART3_Rx - 0x01ULL << 10, // 10 PB2 - 0x01ULL << 11, // 11 PB3 - 0x01ULL << 12, // 12 PB4/(TCB2 PWM) - 0x01ULL << 13, // 13 PB5 - 0x00ULL, // 14 PC0/USART1_Tx - 0x00ULL, // 15 PC1/USART1_Rx - 0x00ULL, // 16 PC2 - 0x00ULL, // 17 PC3 - 0x00ULL, // 18 PC4 - 0x00ULL, // 19 PC5 - 0x00ULL, // 20 PC6 - 0x00ULL, // 21 PC7 - 0x01ULL << 16, // 22 PD0/AIN0 - 0x01ULL << 17, // 23 PD1/AIN1 - 0x01ULL << 18, // 24 PD2/AIN2 - 0x01ULL << 19, // 25 PD3/AIN3 - 0x01ULL << 20, // 26 PD4/AIN4 - 0x01ULL << 21, // 27 PD5/AIN5 - 0x01ULL << 22, // 28 PD6/AIN6 - 0x01ULL << 23, // 29 PD7/AIN7/AREF - 0x01ULL << 24, // 30 PE0/AIN8 - 0x01ULL << 25, // 31 PE1/AIN9 - 0x01ULL << 26, // 32 PE2/AIN10 - 0x01ULL << 27, // 33 PE3/AIN11 - 0x01ULL << 32, // 34 PF0/USART2_Tx/TOSC1 - 0x01ULL << 33, // 35 PF1/USART2_Rx/TOSC2 - 0x01ULL << 34, // 36 PF2/AIN12 - 0x01ULL << 35, // 37 PF3/AIN13 - 0x01ULL << 36, // 38 PF4/AIN14 - 0x01ULL << 37, // 39 PF5/AIN15 -#elif (__PTC_Pincount__ == 47) + 0x01ULL << 1, // 1 PA1 + 0x01ULL << 2, // 2 PA2/SDA + 0x01ULL << 3, // 3 PA3/SCL + 0x01ULL << 4, // 4 PA4/MOSI + 0x01ULL << 5, // 5 PA5/MISO + 0x01ULL << 6, // 6 PA6/SCK + 0x01ULL << 7, // 7 PA7/SS/CLKOUT/LED_BUILTIN + 0x01ULL << 8, // 8 PB0/USART3_Tx + 0x01ULL << 9, // 9 PB1/USART3_Rx + 0x01ULL << 10, // 10 PB2 + 0x01ULL << 11, // 11 PB3 + 0x01ULL << 12, // 12 PB4/(TCB2 PWM) + 0x01ULL << 13, // 13 PB5 + 0x00ULL, // 14 PC0/USART1_Tx + 0x00ULL, // 15 PC1/USART1_Rx + 0x00ULL, // 16 PC2 + 0x00ULL, // 17 PC3 + 0x00ULL, // 18 PC4 + 0x00ULL, // 19 PC5 + 0x00ULL, // 20 PC6 + 0x00ULL, // 21 PC7 + 0x01ULL << 16, // 22 PD0/AIN0 + 0x01ULL << 17, // 23 PD1/AIN1 + 0x01ULL << 18, // 24 PD2/AIN2 + 0x01ULL << 19, // 25 PD3/AIN3 + 0x01ULL << 20, // 26 PD4/AIN4 + 0x01ULL << 21, // 27 PD5/AIN5 + 0x01ULL << 22, // 28 PD6/AIN6 + 0x01ULL << 23, // 29 PD7/AIN7/AREF + 0x01ULL << 24, // 30 PE0/AIN8 + 0x01ULL << 25, // 31 PE1/AIN9 + 0x01ULL << 26, // 32 PE2/AIN10 + 0x01ULL << 27, // 33 PE3/AIN11 + 0x01ULL << 32, // 34 PF0/USART2_Tx/TOSC1 + 0x01ULL << 33, // 35 PF1/USART2_Rx/TOSC2 + 0x01ULL << 34, // 36 PF2/AIN12 + 0x01ULL << 35, // 37 PF3/AIN13 + 0x01ULL << 36, // 38 PF4/AIN14 + 0x01ULL << 37, // 39 PF5/AIN15 + #elif (__PTC_Pincount__ == 46) 0x01ULL << 0, // 0 PA0 - 0x01ULL << 1, // 1 PA1 - 0x01ULL << 2, // 2 PA2/SDA - 0x01ULL << 3, // 3 PA3/SCL - 0x01ULL << 4, // 4 PA4/MOSI - 0x01ULL << 5, // 5 PA5/MISO - 0x01ULL << 6, // 6 PA6/SCK - 0x01ULL << 7, // 7 PA7/SS/CLKOUT/LED_BUILTIN - 0x01ULL << 8, // 8 PB0/USART3_Tx - 0x01ULL << 9, // 9 PB1/USART3_Rx - 0x01ULL << 10, // 10 PB2 - 0x01ULL << 11, // 11 PB3 - 0x01ULL << 12, // 12 PB4/(TCB2 PWM) - 0x01ULL << 13, // 13 PB5 - 0x01ULL << 14, // 14 PB6 - 0x01ULL << 15, // 15 PB7 - 0x00ULL, // 16 PC0/USART1_Tx - 0x00ULL, // 17 PC1/USART1_Rx - 0x00ULL, // 18 PC2 - 0x00ULL, // 19 PC3 - 0x00ULL, // 20 PC4 - 0x00ULL, // 21 PC5 - 0x00ULL, // 22 PC6 - 0x00ULL, // 23 PC7 - 0x01ULL << 16, // 24 PD0/AIN0 - 0x01ULL << 17, // 25 PD1/AIN1 - 0x01ULL << 18, // 26 PD2/AIN2 - 0x01ULL << 19, // 27 PD3/AIN3 - 0x01ULL << 20, // 28 PD4/AIN4 - 0x01ULL << 21, // 29 PD5/AIN5 - 0x01ULL << 22, // 30 PD6/AIN6 - 0x01ULL << 23, // 31 PD7/AIN7/AREF - 0x01ULL << 24, // 32 PE0/AIN8 - 0x01ULL << 25, // 33 PE1/AIN9 - 0x01ULL << 26, // 34 PE2/AIN10 - 0x01ULL << 27, // 35 PE3/AIN11 - 0x01ULL << 28, // 36 PE4 - 0x01ULL << 29, // 37 PE5 - 0x01ULL << 30, // 38 PE6 - 0x01ULL << 31, // 39 PE7 - 0x01ULL << 32, // 40 PF0/USART2_Tx/TOSC1 - 0x01ULL << 33, // 41 PF1/USART2_Rx/TOSC2 - 0x01ULL << 34, // 42 PF2/AIN12 - 0x01ULL << 35, // 43 PF3/AIN13 - 0x01ULL << 36, // 44 PF4/AIN14 - 0x01ULL << 37, // 45 PF5/AIN15 - 0x01ULL << 40, // 46 PG0 - 0x01ULL << 41, // 47 PG1 - 0x01ULL << 42, // 48 PG2 - 0x01ULL << 43, // 49 PG3 - 0x01ULL << 44, // 50 PG4 - 0x01ULL << 45, // 51 PG5 - 0x01ULL << 46, // 52 PG6 - 0x01ULL << 47, // 53 PG7 -#endif + 0x01ULL << 1, // 1 PA1 + 0x01ULL << 2, // 2 PA2/SDA + 0x01ULL << 3, // 3 PA3/SCL + 0x01ULL << 4, // 4 PA4/MOSI + 0x01ULL << 5, // 5 PA5/MISO + 0x01ULL << 6, // 6 PA6/SCK + 0x01ULL << 7, // 7 PA7/SS/CLKOUT/LED_BUILTIN + 0x01ULL << 8, // 8 PB0/USART3_Tx + 0x01ULL << 9, // 9 PB1/USART3_Rx + 0x01ULL << 10, // 10 PB2 + 0x01ULL << 11, // 11 PB3 + 0x01ULL << 12, // 12 PB4/(TCB2 PWM) + 0x01ULL << 13, // 13 PB5 + 0x01ULL << 14, // 14 PB6 + 0x01ULL << 15, // 15 PB7 + 0x00ULL, // 16 PC0/USART1_Tx + 0x00ULL, // 17 PC1/USART1_Rx + 0x00ULL, // 18 PC2 + 0x00ULL, // 19 PC3 + 0x00ULL, // 20 PC4 + 0x00ULL, // 21 PC5 + 0x00ULL, // 22 PC6 + 0x00ULL, // 23 PC7 + 0x01ULL << 16, // 24 PD0/AIN0 + 0x01ULL << 17, // 25 PD1/AIN1 + 0x01ULL << 18, // 26 PD2/AIN2 + 0x01ULL << 19, // 27 PD3/AIN3 + 0x01ULL << 20, // 28 PD4/AIN4 + 0x01ULL << 21, // 29 PD5/AIN5 + 0x01ULL << 22, // 30 PD6/AIN6 + 0x01ULL << 23, // 31 PD7/AIN7/AREF + 0x01ULL << 24, // 32 PE0/AIN8 + 0x01ULL << 25, // 33 PE1/AIN9 + 0x01ULL << 26, // 34 PE2/AIN10 + 0x01ULL << 27, // 35 PE3/AIN11 + 0x01ULL << 28, // 36 PE4 + 0x01ULL << 29, // 37 PE5 + 0x01ULL << 30, // 38 PE6 + 0x01ULL << 31, // 39 PE7 + 0x01ULL << 32, // 40 PF0/USART2_Tx/TOSC1 + 0x01ULL << 33, // 41 PF1/USART2_Rx/TOSC2 + 0x01ULL << 34, // 42 PF2/AIN12 + 0x01ULL << 35, // 43 PF3/AIN13 + 0x01ULL << 36, // 44 PF4/AIN14 + 0x01ULL << 37, // 45 PF5/AIN15 + 0x01ULL << 40, // 46 PG0 + 0x01ULL << 41, // 47 PG1 + 0x01ULL << 42, // 48 PG2 + 0x01ULL << 43, // 49 PG3 + 0x01ULL << 44, // 50 PG4 + 0x01ULL << 45, // 51 PG5 + 0x01ULL << 46, // 52 PG6 + 0x01ULL << 47, // 53 PG7 + #endif }; #if defined (PORTA) - #define PORTA_ISC(_pin_) ((0x20 * 0) + 0x10 + _pin_) +#define PORTA_ISC(_pin_) ((0x20 * 0) + 0x10 + _pin_) #else - #define PORTA_ISC(_pin_) 0x00 +#define PORTA_ISC(_pin_) 0x00 #endif #if defined (PORTB) - #define PORTB_ISC(_pin_) ((0x20 * 1) + 0x10 + _pin_) +#define PORTB_ISC(_pin_) ((0x20 * 1) + 0x10 + _pin_) #else - #define PORTB_ISC(_pin_) 0x00 +#define PORTB_ISC(_pin_) 0x00 #endif #if defined (PORTC) - #define PORTC_ISC(_pin_) ((0x20 * 2) + 0x10 + _pin_) +#define PORTC_ISC(_pin_) ((0x20 * 2) + 0x10 + _pin_) #else - #define PORTC_ISC(_pin_) 0x00 +#define PORTC_ISC(_pin_) 0x00 #endif #if defined (PORTD) - #define PORTD_ISC(_pin_) ((0x20 * 3) + 0x10 + _pin_) +#define PORTD_ISC(_pin_) ((0x20 * 3) + 0x10 + _pin_) #else - #define PORTD_ISC(_pin_) 0x00 +#define PORTD_ISC(_pin_) 0x00 #endif #if defined (PORTE) - #define PORTE_ISC(_pin_) ((0x20 * 4) + 0x10 + _pin_) +#define PORTE_ISC(_pin_) ((0x20 * 4) + 0x10 + _pin_) #else - #define PORTE_ISC(_pin_) 0x00 +#define PORTE_ISC(_pin_) 0x00 #endif #if defined (PORTF) - #define PORTF_ISC(_pin_) ((0x20 * 5) + 0x10 + _pin_) +#define PORTF_ISC(_pin_) ((0x20 * 5) + 0x10 + _pin_) #else - #define PORTF_ISC(_pin_) 0x00 +#define PORTF_ISC(_pin_) 0x00 #endif #if defined (PORTG) - #define PORTG_ISC(_pin_) ((0x20 * 6) + 0x10 + _pin_) +#define PORTG_ISC(_pin_) ((0x20 * 6) + 0x10 + _pin_) #else - #define PORTG_ISC(_pin_) 0x00 +#define PORTG_ISC(_pin_) 0x00 #endif -// lookup-table to quickly disable input and pull-up -const uint8_t ptc_ch_to_pin [] = { -#if (__PTC_Pincount__ <= 14) +// 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(5), PORTA_ISC(6), PORTA_ISC(7), PORTB_ISC(1), /* X4 / Y4 */ - PORTA_ISC(0), -#if (__PTC_Pincount__ == 12 || __PTC_Pincount__ == 14) + PORTB_ISC(0), + #if (__PTC_Pincount__ == 12 || __PTC_Pincount__ == 14) PORTC_ISC(0), PORTC_ISC(1), PORTC_ISC(2), /* X8 / Y8 */ 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 -#else - PORTA_ISC(0), - PORTA_ISC(1), - PORTA_ISC(2), - PORTA_ISC(3), - PORTA_ISC(4), - PORTA_ISC(5), - PORTA_ISC(6), - PORTA_ISC(7), // X7 - PORTB_ISC(0), - PORTB_ISC(1), - PORTB_ISC(2), - PORTB_ISC(3), - PORTB_ISC(4), - PORTB_ISC(5), + PORTB_ISC(5), /* X12 / Y12 */ PORTB_ISC(6), - PORTB_ISC(7), // X15 - - PORTD_ISC(0), - PORTD_ISC(1), - PORTD_ISC(2), - PORTD_ISC(3), - PORTD_ISC(4), - PORTD_ISC(5), - PORTD_ISC(6), - PORTD_ISC(7), // X23 - - PORTE_ISC(0), - PORTE_ISC(1), - PORTE_ISC(2), - PORTE_ISC(3), - PORTE_ISC(4), - PORTE_ISC(5), - PORTE_ISC(6), - PORTE_ISC(7), // X31 - - PORTF_ISC(0), - PORTF_ISC(1), - PORTF_ISC(2), - PORTF_ISC(3), - PORTF_ISC(4), - PORTF_ISC(5), // X37 - 0x00, - 0x00, - - PORTG_ISC(0), - PORTG_ISC(1), - PORTG_ISC(2), - PORTG_ISC(3), - PORTG_ISC(4), - PORTG_ISC(5), - PORTG_ISC(6), - PORTG_ISC(7), // X47 -#endif + #endif + #endif }; diff --git a/megaavr/libraries/PTC/src/ptc_types.h b/megaavr/libraries/PTC/src/ptc_types.h index 8dcb5797..b5832ab3 100644 --- a/megaavr/libraries/PTC/src/ptc_types.h +++ b/megaavr/libraries/PTC/src/ptc_types.h @@ -1,11 +1,11 @@ /* - * Refer to ptc_touch.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_TOUCH_TYPES_H -#define PTC_TOUCH_TYPES_H +#ifndef PTC_TYPES_H +#define PTC_TYPES_H #ifdef __cplusplus extern "C" { @@ -14,65 +14,51 @@ extern "C" { #include #if (defined(__AVR_ATtiny814__) || defined(__AVR_ATtiny1614__) || defined(__AVR_ATtiny3214__)) - typedef uint8_t ptc_id_t; - typedef uint8_t ptc_ch_bm_t; - #define __PTC_Tiny__ - #define __PTC_Pincount__ 6 +typedef uint8_t ptc_id_t; +typedef uint8_t ptc_ch_bm_t; +typedef uint8_t ptc_ch_arr_t[1]; +#define __PTC_Tiny__ +#define __PTC_Pincount__ 6 #elif (defined(__AVR_ATtiny816__) || defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__)) - typedef uint8_t ptc_id_t; - typedef uint16_t ptc_ch_bm_t; - #define __PTC_Tiny__ - #define __PTC_Pincount__ 12 +typedef uint8_t ptc_id_t; +typedef uint16_t ptc_ch_bm_t; +typedef uint16_t ptc_ch_arr_t[1]; +#define __PTC_Tiny__ +#define __PTC_Pincount__ 12 #elif (defined(__AVR_ATtiny817__) || defined(__AVR_ATtiny1617__) || defined(__AVR_ATtiny3217__)) - typedef uint8_t ptc_id_t; - typedef uint16_t ptc_ch_bm_t; - #define __PTC_Tiny__ - #define __PTC_Pincount__ 14 +typedef uint8_t ptc_id_t; +typedef uint16_t ptc_ch_bm_t; +typedef uint16_t ptc_ch_arr_t[1]; +#define __PTC_Tiny__ +#define __PTC_Pincount__ 14 #elif (defined(__AVR_AVR32DA28__) || defined(__AVR_AVR64DA28__) || defined(__AVR_AVR128DA28__)) - typedef uint16_t ptc_id_t; - typedef uint64_t ptc_ch_bm_t; - #define __PTC_DA__ - #define __PTC_Pincount__ 17 +typedef uint16_t ptc_id_t; +typedef uint64_t ptc_ch_bm_t; +typedef uint8_t ptc_ch_arr_t[5]; +#define __PTC_DA__ +#define __PTC_Pincount__ 18 #elif (defined(__AVR_AVR32DA32__) || defined(__AVR_AVR64DA32__) || defined(__AVR_AVR128DA32__)) - typedef uint16_t ptc_id_t; - typedef uint64_t ptc_ch_bm_t; - #define __PTC_DA__ - #define __PTC_Pincount__ 21 +typedef uint16_t ptc_id_t; +typedef uint64_t ptc_ch_bm_t; +typedef uint8_t ptc_ch_arr_t[5]; +#define __PTC_DA__ +#define __PTC_Pincount__ 22 #elif (defined(__AVR_AVR32DA48__) || defined(__AVR_AVR64DA48__) || defined(__AVR_AVR128DA48__)) - typedef uint16_t ptc_id_t; - typedef uint64_t ptc_ch_bm_t; - #define __PTC_DA__ - #define __PTC_Pincount__ 31 +typedef uint16_t ptc_id_t; +typedef uint64_t ptc_ch_bm_t; +typedef uint8_t ptc_ch_arr_t[5]; +#define __PTC_DA__ +#define __PTC_Pincount__ 32 #elif (defined(__AVR_AVR32DA64__) || defined(__AVR_AVR64DA64__) || defined(__AVR_AVR128DA64__)) - typedef uint16_t ptc_id_t; - typedef uint64_t ptc_ch_bm_t; - #define __PTC_DA__ - #define __PTC_Pincount__ 47 +typedef uint16_t ptc_id_t; +typedef uint64_t ptc_ch_bm_t; +typedef uint8_t ptc_ch_arr_t[6]; +#define __PTC_DA__ +#define __PTC_Pincount__ 46 #else - #error "PTC not supported by this chip" +#error "PTC not supported by this chip" #endif -/** - * PTC series resistor setting. For Mutual cap mode, this series - * resistor is switched internally on the Y-pin. For Self cap mode, - * the series resistor is switched internally on the Sensor pin. - * - * Example: - * RSEL_VAL_0 sets internal series resistor to 0ohms. - * RSEL_VAL_20 sets internal series resistor to 20Kohms. - * RSEL_VAL_50 sets internal series resistor to 50Kohms. - * RSEL_VAL_70 sets internal series resistor to 70Kohms. - * RSEL_VAL_100 sets internal series resistor to 100Kohms. - * RSEL_VAL_200 sets internal series resistor to 200Kohms. - */ -typedef enum PTC_RSEL_enum { - RSEL_VAL_0, - RSEL_VAL_20, - RSEL_VAL_50, - RSEL_VAL_70, - RSEL_VAL_100, - RSEL_VAL_200 -} PTC_RSEL_t; typedef enum ptc_freq_enum { FREQ_SEL_0, @@ -95,27 +81,76 @@ typedef enum ptc_freq_enum { } ptc_freq_t; +#if defined (__PTC_Tiny__) +typedef enum PTC_PRESC_enum { + PTC_PRESC_DIV2_gc = (0x00 << 0), /* CLK_PER divided by 2 */ + PTC_PRESC_DIV4_gc = (0x01 << 0), /* CLK_PER divided by 4 */ + PTC_PRESC_DIV8_gc = (0x02 << 0), /* CLK_PER divided by 8 */ + PTC_PRESC_DIV16_gc = (0x03 << 0), /* CLK_PER divided by 16 */ + PTC_PRESC_DIV32_gc = (0x04 << 0), /* CLK_PER divided by 32 */ + PTC_PRESC_DIV64_gc = (0x05 << 0), /* CLK_PER divided by 64 */ + PTC_PRESC_DIV128_gc = (0x06 << 0), /* CLK_PER divided by 128 */ + PTC_PRESC_DIV256_gc = (0x07 << 0), /* CLK_PER divided by 256 */ +} ptc_presc_t; -typedef enum PTC_PRESC_enum -{ - PTC_PRESC_DIV2_gc = (0x00<<0), /* CLK_PER divided by 2 */ - PTC_PRESC_DIV4_gc = (0x01<<0), /* CLK_PER divided by 4 */ - PTC_PRESC_DIV8_gc = (0x02<<0), /* CLK_PER divided by 8 */ - PTC_PRESC_DIV16_gc = (0x03<<0), /* CLK_PER divided by 16 */ - PTC_PRESC_DIV32_gc = (0x04<<0), /* CLK_PER divided by 32 */ - PTC_PRESC_DIV64_gc = (0x05<<0), /* CLK_PER divided by 64 */ - PTC_PRESC_DIV128_gc = (0x06<<0), /* CLK_PER divided by 128 */ - PTC_PRESC_DIV256_gc = (0x07<<0) /* CLK_PER divided by 256 */ -} PTC_PRESC_t; - - +typedef enum PTC_RSEL_enum { + RSEL_VAL_0, + RSEL_VAL_20, + RSEL_VAL_50, + RSEL_VAL_70, + RSEL_VAL_100, + RSEL_VAL_200 +} ptc_rsel_t; + +typedef enum ptc_gain_enum { + PTC_GAIN_1 = 0x00, + PTC_GAIN_2 = 0x23, + PTC_GAIN_4 = 0x34, + PTC_GAIN_8 = 0x3A, + PTC_GAIN_16 = 0x3C, + PTC_GAIN_32 = 0x3E, + PTC_GAIN_MAX = 0x3F, +} ptc_gain_t; + +#elif defined (__PTC_DA__) +typedef enum PTC_PRESC_enum { + PTC_PRESC_DIV2_gc = (0x00 << 0), /* CLK_PER divided by 2 */ + PTC_PRESC_DIV4_gc = (0x01 << 0), /* CLK_PER divided by 4 */ + PTC_PRESC_DIV6_gc = (0x02 << 0), /* CLK_PER divided by 2 */ + PTC_PRESC_DIV8_gc = (0x03 << 0), /* CLK_PER divided by 8 */ + PTC_PRESC_DIV10_gc = (0x04 << 0), /* CLK_PER divided by 8 */ + PTC_PRESC_DIV12_gc = (0x05 << 0), /* CLK_PER divided by 8 */ + PTC_PRESC_DIV14_gc = (0x06 << 0), /* CLK_PER divided by 8 */ + PTC_PRESC_DIV16_gc = (0x07 << 0), /* CLK_PER divided by 16 */ +} ptc_presc_t; + +typedef enum tag_rsel_val_t { + RSEL_VAL_0, + RSEL_VAL_20, + RSEL_VAL_50, + RSEL_VAL_70, + RSEL_VAL_80, + RSEL_VAL_100, + RSEL_VAL_120, + RSEL_VAL_200 +} ptc_rsel_t; + +typedef enum ptc_gain_enum { + PTC_GAIN_1 = 0x00, + PTC_GAIN_2 = 0x10, + PTC_GAIN_4 = 0x18, + PTC_GAIN_8 = 0x1C, + PTC_GAIN_16 = 0x1E, + PTC_GAIN_MAX = 0x1F, +} ptc_gain_t; +#endif typedef struct ptc_node_state_type { - uint8_t error:1; - uint8_t win_comp:1; - uint8_t low_power:1; - uint8_t data_ready:1; - uint8_t disabled:1; + uint8_t error: 1; + uint8_t win_comp: 1; + uint8_t low_power: 1; + uint8_t data_ready: 1; + uint8_t disabled: 1; } ptc_node_state_t; @@ -197,15 +232,15 @@ typedef enum ptc_lib_enum { } ptc_lib_t; typedef struct cap_sensor_type { - struct cap_sensor_type* nextNode; + struct cap_sensor_type *nextNode; ptc_node_type_t type; ptc_id_t id; // number for easier identification in the callback - ptc_ch_bm_t hw_xCh_bm; - ptc_ch_bm_t hw_yCh_bm; - uint16_t hw_compCaps; // [13:12] rough; [11:8] course; [7:4] fine; [3:0] accurate + ptc_ch_arr_t hw_xCh_bm; // the code relies on this bitmaps to be together, + ptc_ch_arr_t hw_yCh_bm; // do not separate them or change order. + uint16_t hw_compCaps; // [13:12] rough; [11:8] course; [7:4] fine; [3:0] accurate (on Tinies only) uint8_t hw_rsel_presc; // [7:4] RSEL, [3:0] PRESC - uint8_t hw_a_d_gain; // [7:4] Analog Gain, [3:0] Digital Gain /* PTC_AGAIN / CTRLB.SAMPNUM */ + uint8_t hw_gain_ovs; // [7:4] Analog Gain, [3:0] Oversampling /* PTC_AGAIN / CTRLB.SAMPNUM */ uint8_t hw_csd; // [4:0] Charge Share Delay /* SAMPLEN in SAMPCTRL */ ptc_node_state_t state; diff --git a/megaavr/libraries/SPI/library.properties b/megaavr/libraries/SPI/library.properties index 8a8bac65..bae585ac 100644 --- a/megaavr/libraries/SPI/library.properties +++ b/megaavr/libraries/SPI/library.properties @@ -5,5 +5,5 @@ maintainer=Spence Konde sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. paragraph=SPI is a synchronous serial data protocol used by microcontrollers for communicating with one or more peripheral devices quickly over short distances. It uses three lines common to all devices (MISO, MOSI and SCK) and one specific for each device. This version has been modified, first to support pinswap on the megaAVR 0-series parts (by @MCUDude) and further by @SpenceKonde to do so on tinyAVR 0-series and 1-series for megaTinyCore, and later to ensure it plays nicely with SPI1, which supports the second SPI port on megaAVR 0-series and AVR-DA series parts, and with the new attachInterrupt code in 2.5.x. of megaTinyCore and 1.4.x of DxCore. 1.1.2 corrects a DxCore-specific typo and corrects styling of code in several places 1.1.1 corrects a further bug relating to startTransaction enabling slave mode and is distributed as part of megaTinyCore 2.5.12. This version is distributed as part of megaTinyCore, see https://github.com/SpenceKonde/megaTinyCore for more information. category=Communication -url=https://www.arduino.cc/reference/en/language/functions/communication/spi/ +url=https://reference.arduino.cc/reference/en/language/functions/communication/spi/ architectures=megaavr diff --git a/megaavr/libraries/Wire/src/twi_pins.c b/megaavr/libraries/Wire/src/twi_pins.c index c01879d9..a873f889 100644 --- a/megaavr/libraries/Wire/src/twi_pins.c +++ b/megaavr/libraries/Wire/src/twi_pins.c @@ -744,4 +744,4 @@ uint8_t TWI1_setConfig(bool smbuslvl, bool longsetup, uint8_t sda_hold, bool smb #endif /* defined(TWI1) */ -#endif /* TWI_PINS_H */ \ No newline at end of file +#endif /* TWI_PINS_H */