From 58580c2fa77d1fa8d65d88d90c15adcf893e978b Mon Sep 17 00:00:00 2001 From: Spence Konde Date: Tue, 3 Oct 2023 00:38:42 -0400 Subject: [PATCH] L I N T LINT LINT --- ChangeLog.md | 2 +- ContributionGuidelines.md | 2 +- README.md | 31 ++++++++++---- megaavr/bootloaders/hex/README.md | 40 +++++++++++++++--- megaavr/bootloaders/hex/mega0.tar | Bin 158208 -> 0 bytes megaavr/bootloaders/hex/mega0.tar.bz2 | Bin 0 -> 5213 bytes megaavr/cores/megatinycore/Arduino.h | 2 +- megaavr/cores/megatinycore/HardwareSerial.h | 2 +- megaavr/cores/megatinycore/WInterrupts.c | 2 +- megaavr/cores/megatinycore/WInterrupts_PA.c | 4 +- megaavr/cores/megatinycore/core_devices.h | 2 +- megaavr/cores/megatinycore/dirty_tricks.h | 4 +- megaavr/cores/megatinycore/wiring_analog.c | 2 +- megaavr/extras/Ref_BestPractices.md | 2 +- megaavr/extras/Ref_Defines.md | 2 +- megaavr/extras/Ref_Differences.md | 19 ++++++--- megaavr/extras/Ref_DirectPortManipulation.md | 32 +++++++------- megaavr/extras/Ref_Functions.md | 2 +- megaavr/extras/Ref_Interrupts.md | 2 +- megaavr/extras/Ref_Microchip.md | 5 ++- megaavr/extras/Ref_Printf.md | 2 +- megaavr/extras/Ref_Reset.md | 2 +- megaavr/extras/Ref_Robust.md | 8 ++-- megaavr/extras/Ref_TCD.md | 2 +- megaavr/extras/Ref_Timers.md | 4 +- megaavr/extras/TakingOverTCA0.md | 4 +- megaavr/libraries/Comparator/README.md | 6 +-- megaavr/libraries/Event/src/Event_parts.h | 2 +- megaavr/libraries/Logic/README.md | 4 +- megaavr/libraries/Wire/src/Wire.cpp | 2 +- .../examples/ClockDiagnose/ClockDiagnose.ino | 2 +- .../examples/TCA0Demo3/TCA0Demo3.ino | 2 +- .../examples/TuningSource/TuningSource.ino | 2 +- 33 files changed, 127 insertions(+), 72 deletions(-) delete mode 100644 megaavr/bootloaders/hex/mega0.tar create mode 100644 megaavr/bootloaders/hex/mega0.tar.bz2 diff --git a/ChangeLog.md b/ChangeLog.md index ac08880a..808d7fc0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -83,7 +83,7 @@ Changes listed here are checked in to GitHub ("master" branch unless specificall * Performance enhancement: From @MX682X, the discovery that an appropriately timed kick to the compiler's groin would cause it to place pointers into base registers when it otherwise would not. Doing this increases efficiency dramatically in some cases, allowing the use of load and store with displacement in several places where it had previously used a truckload of LDS and/or STS instructions. This resulted in smaller, faster binaries due to more efficient initialiaation code for a number of peripherals (which impacted even users who weren't doing anything special, since the biggest beneficiaries were the ADC and timer initialization code, which happen as long as main() is not overridden). This also includes the addition of a number of macros to implement such kicks to permit this method to be readily generalized if other areas of the core are found which could benefit from it. * Organizational enhancement: Move all of the dirty performance enhancement macros like the ones above from Arduino.h to newly added dirty_tricks.h. Change names of many macros to make them easier to type. Document these functions in [the dirty_tricks reference](megaavr/extras/Ref_dirty_tricks.md). * Docs: General day-to-day maintenance -* Add what little support was needed for analogWrite() to work if there were tools submenu to use differerent subsets of pins to wiring_analog.c. Varaiant files however still require significant work, however! +* Add what little support was needed for analogWrite() to work if there were tools submenu to use differerent subsets of pins to wiring_analog.c. Variant files however still require significant work, however! #### Known issues * Optimization level menu is not displayed. diff --git a/ContributionGuidelines.md b/ContributionGuidelines.md index f661319f..19d3bbea 100644 --- a/ContributionGuidelines.md +++ b/ContributionGuidelines.md @@ -55,7 +55,7 @@ Some general themes of these decisions others may disagree with: ## Azduino Code Style We have a largely self-consistent code style used throughout the core and associated files based on a number of principles. -1. Readbility is paramount, and if adhering to particular code styling guidelines would result in hideous looking unreadable code, don't do it. +1. Readability is paramount, and if adhering to particular code styling guidelines would result in hideous looking unreadable code, don't do it. a. astyle is only run in libraries and variants, not the core itself because it would need to be disabled on all files anyway. 2. Always use the integer types that explicitly specify the datatype size. That means, avoid 'int' 'byte' 'char' 'long' etc. Types should be things like: diff --git a/README.md b/README.md index b0a3a913..874e6351 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,6 @@ I sell breakout boards with regulator, UPDI header, and Serial header in my tind ### [ATtiny1614/814/414/1604/804/404 bare board](https://www.tindie.com/products/17748/) ### [ATtiny412 assembled](https://www.tindie.com/products/17685/) ### [ATtiny412/212/402/202 bare board](https://www.tindie.com/products/17749/) -### [ATtiny3227, 3217, 3226, 1616, 1626, 3224, 1624, 412, and even the AVR32DA20 ULTRAMINI](https://www.tindie.com/products/17749/) The Ultramini parts use the tiny QFN (or TSSOP-14) package on a board designed to fit in a standard DIP socket! (24-pin parts use a 28-pin socket, as two middle pins are blocked). ## Notable Hardware Features @@ -337,7 +336,7 @@ This core *always* uses Link Time Optimization to reduce flash usage - all versi These parts all have a large number of analog inputs - DA and DB-series have up to 22 analog inputs, while the DD-series has analog input on every pin that is not used to drive the HF crystal (though the pins on PORTC are only supported when MVIO is turned off). They can be read with `analogRead()` like on a normal AVR, and we default to 10-bit resolution; you can change to the full 12-bit with `analogReadResolution()`, and use the enhanced analogRead functions to take automatically oversampled, decimated readings for higher resolution and to take differential measurements. There are 4 internal voltage references in 1.024, 2.048, 4.096 and 2.5V, plus support for external reference voltage (and Vdd of course). ADC readings are taken 3 times faster than an classic AVR, and that speed can be doubled again if what you are measuring is low impedance, or extend the sampling time by a factor greatly for reading very high impedance sources. This is detailed in the analog reference. ### DAC Support -The Dx-series parts have a 10-bit DAC which can generate a real analog voltage (note that this provides low current and can only be used as a voltage reference or control voltage, it cannot be used to power other devices). This generates voltages between 0 and the selected `VREF` (unlike the tinyAVR 1-series, this can be Vcc!). Set the DAC reference voltage via the DACR`eference()` function - pass it any of the ADC reference options listed under the ADC section above (including VDD!). Call `analogWrite()` on the DAC pin (PD6) to set the voltage to be output by the DAC (this uses it in 8-bit mode). To turn off the DAC output, call `digitalWrite()` or `turnOffPWM()` on that pin. +The Dx-series parts have a 10-bit DAC which can generate a real analog voltage (note that this provides low current and can only be used as a voltage reference or control voltage, it cannot be used to power other devices). This generates voltages between 0 and the selected `VREF` (unlike the tinyAVR 1-series, this can be Vcc!). Set the DAC reference voltage via the DACR`reference()` function - pass it any of the ADC reference options listed under the ADC section above (including VDD!). Call `analogWrite()` on the DAC pin (PD6) to set the voltage to be output by the DAC (this uses it in 8-bit mode). To turn off the DAC output, call `digitalWrite()` or `turnOffPWM()` on that pin. There may be additional options to configure the DAC on the EA-series. @@ -452,7 +451,7 @@ From cplusplus.com: >The length sub-specifier modifies the length of the data type. This is a chart showing the types used to interpret the corresponding arguments with and without length specifier ~(if a different type is used, the proper type promotion or conversion is performed, if allowed)~: Strikethrough mine 'cause that don't work here (and it's not my fault nor under my control - it's supplied with avrlibc, and I suspect that it's because the overhead of implementing it on an 8-bit AVR is too large). When incorrect length specifiers are given (including none when one should be used) surprising things happen. It looks to me like all the arguments get smushed together into a group of bytes. Then it reads the format string, and when it gets to a format specifier for an N byte datatype, it grabs N bytes from the argument array, formats them and prints them to whatever you're printing to, proceeding until the end of the format string. Thus, failing to match the format specifiers' length modifiers with the arguments will result in printing wrong data, for that substitution **and all subsequent ones** in that call to `printf()`. -The table below comprises the relevant lines from that table - many standard types are not a thing in Arduino. +The table below comprises the relevant lines from that table - many standard types are not a thing in Arduino (their original was several times longer, but including that mess would just complicate this discussion. | length | d i | u o x X | f F e E g G a A | c | s | p | n | |--------|-----|---------|-----------------|-----|--------|------|----------| @@ -460,13 +459,26 @@ The table below comprises the relevant lines from that table - many standard typ | hh |int8 | uint8 | | | | | char* | | l |int32| uint32 | | | | | int32_t* | -Notice that there is no line for 64 bit types in the table above; these are not supported (support for 64-bit types is pretty spotty, which is not surprising. Variables of that size are hard to work with on an 8-bit microcontroller with just 32 working registers). This applies to all versions of `printf()` - the capability is not supplied by avr-libc. +Notice that there is no line for 64 bit types in the table above; these are not supported (support for 64-bit types is pretty spotty, which is not surprising. Variables of that size are hard to work with on an 8-bit microcontroller with just 32 working registers), and using uint64's is something you should try to avoid, similar to driving on the wrong side of the road, flying kites during thunder storms, or drinking bleach. While all have been suggested (Europe is really persistent about the side of the road; As far as I'm concerned, it comes down to physics; mirror image symmetry i. This applies to all versions of `printf()` - the capability is not supplied by avr-libc. -There are reports of memory corruption with printf, I suspect it is misunderstandign of above that is actually at hand here. +There are reports of memory corruption with printf, I suspect it is misunderstanding of above that is actually at hand here. #### Selectable `printf()` Implementation A Tools submenu lets you choose from three levels of `printf()`: full `printf()` with all features, the default one that drops float support to save 1k of flash, and the minimal one drops almost everything and for another 450 bytes flash saving (will be a big deal on the 16k and 8k parts. Less so on 128k ones). Note that selecting any non-default option here *will cause it to be included in the binary even if it's never called* - and if it's never called, it normally wouldn't be included. So an empty sketch will take more space with minimal `printf()` selected than with the default, while a sketch that uses `printf()` will take less space with minimal `printf()` vs default. +So: +| Menu selection | printf() or similar used? | Overhead | +|----------------|---------------------------|----------| +| Default | No | 0 by definition | +| Default | Yes | apx 1500 | +| Minimal | No | apx 1000 | +| Minimal | Yes | apx 1100 | +| Full | No | apx 2800 | +| Full | Yes | apx 3000 | + +Notice how when not using printf or similar functions, you are far better off leaving it on the default, as opposed to switching to minimal thinking you'll save flash, because you you'll use more flash not less. + + ### Interrupts From Pins and in General All pins can be used with `attachInterrupt()` and `detachInterrupt()`, on `RISING`, `FALLING`, `CHANGE`, or `LOW`. All pins can wake the chip from sleep on `CHANGE` or `LOW`. Pins marked as Async Interrupt pins on the megaTinyCore pinout charts (pins 2 and 6 within each port) can be used to wake from sleep on `RISING` and `FALLING` edges as well. Those pins are termed "fully asynchronous pins" in the datasheet. @@ -560,10 +572,12 @@ In general you should expect the following about library compatibility: * They do not make assumptions about what pins are associated with a peripheral they are using (though, note that for 14-24 pin modern tinyAVR parts, and for all modern non-tinyAVR parts, there is an incredible level of consistency with pin assignments, but only if the PIN_Pxn macros are used will this help. For the most common peripherals, there are standard constants #defined that specify the pins used for, eg, SPI, I2C, and so on. * Libraries that make use of only Arduino API calls without assumptions about the underlying hardware are almost guaranteed to work. -The amount of effort required to port a given library will vary widely depending on the library. Some are almost trivial for someone familiar with these parts, while others require only a little work, but very deep knowledge of the parts, and only rarely does a library require a long and painful porting process. The fact that many libraries can be ported without much effort again underlines **the need for reports from users about incompatible libraries** as well as compatible ones notlisted on the table linked below. Usually reports of non-working libraries to add to the table result in the library getting fixed, and the fixed library being added to the table; Almost all of the fruit is low hanging and ripe for harvest, if you tell me where the tree is. Many libraries that were initially incompatible were fixed up in under 10 minutes. Porting typical libraries from classic AVRs requires a fraction of the effort that the "tar pit" libraries included with this core take to port to new modern AVR families (these are Comparator, Logic, Event (because they keep fiddling with EVSYS), +The amount of effort required to port a given library will vary widely depending on the library. Some are straightforward for anyone reasonably familiar with these parts and what to generally expect and approach it. Any library associated with some peripheral that both classic and modern had, it's probably going to be a straightforward change if you just need to swap out the classic peripheral for the modern one - Yes, every bitfield will be named differently, but only rarely did a modern AVR's peripheral lack a feature the classic version had. The USART on classic AVR has whack stuff like MPCM, and the 9 bit mode - sorry, modes. Even the layout of some of the registers is similar - the parts aren't as different as they appear at first. Another type is the "bitbanger", where they're using direct port writes; the solution to this is cookbook - switch to using the relevant PORT or VPORT registers. Input capture is a little more complicated because you have to set up the event channel, and figure out how to offer that in a library (that is the hard part). But the consistent factor is that generally, none of these things are long slow painful slogs. And as noted above, many libraries will work out of the box, or have already been adapted. + +The fact that many libraries can be ported no or little underlines **the need for reports from users about incompatible libraries** as well as compatible ones not listed on the table linked below. Usually *reports of non-working libraries* to add to the table *result in the library getting fixed*, and the fixed library being added to the table; Almost all of the fruit here low hanging. So when you come upon incompatible libraries report it to me! Many libraries that were initially incompatible were fixed up in under 10 minutes. Porting typical libraries from classic AVRs requires a fraction of the effort that the "tar pit" libraries included with this core take to port to new modern AVR families (these are Comparator, Logic, Event: Logic and Event are both, on their own, large, complicated, "system-like" peripherals. The CCL is just complex in general, and has seen relatively modest changest between families, except for the t0/1. Event is simple in theory and much more complicated in practice, in no small part because the implementation on the 0-series, 1-series, mega0, 2-series/DA/DB/DD, EA and the EB are each different. And a single library has to support all of them with a consistent interface and paper over all the differences. ### [List of Known Working / Not-working Libraries](https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/LibraryCompatibility.md) -I know lots of people use libraries that aren't on that list, and I fully expect that there is a great number of libraries that work and are not listed, and I'd live to hear about them. Use the "discussions" or email me, or even submit a PR to add a line to the table. I want to hear about working libraries so others will know they work and not hesitate, and I'm even more interested in ones that *don't* work so they can be fixed - or determined to be unfixable) +I know lots of people use libraries that aren't on that list, and I fully expect that there is a great number of libraries that work and are not listed, and I'd love to hear about them. Use the "discussions" or email me, or even submit a PR to add a line to the table. I want to hear about working libraries so others will know they work and not hesitate, and I'm even more interested in ones that *don't* work so they can be fixed - or determined to be unfixable) ## WDT and Resets For more information on resetting from software, using the Watchdog Timer, the causes of unexpected resets and how to prevent them, and generally all things reset-related, see the [Reset Guide](https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/Ref_Reset.md). @@ -573,7 +587,8 @@ It is often useful to identify what options are selected on the menus from withi There are a great number of `#define`s provided to get information about the hardware in-use, in order to write portable and flexible code in your sketch or, especially, library code. -**Note**: You cannot distinguish an extended temperature range part from a normal one from software. +**Note**: You cannot distinguish an extended temperature range part from a normal one from software. For 0/1-series, most packages mark the temperature grade. **this is no longer true on the 2-series, nor on any part released after the 1-series** - So better make sure you mark those parts if you unpack them, because the only alternative is to give the lot number to Microchip support, and they'll tell you if it's an F, a U, or an N (FUN letters - but notice that you can't turn any letter into any other letter without both erasing and adding lines. The same is true of the different set of letters they used on automotive parts - BMZ or something - less FUN, but they had the same "modification resistance" (hey, on at least one occasion, a quantity of t13'd had the markings polished off and were remarked as tiny85's and sold as such on aliexpress and ebay - *that* was worth doing to some criminal in China! +Unethical behavior is of course the norm for companies everywhere, but in the US, criminality *of* the company (as opposed to rogue employees) is not pervasive. When it rises above that, low end of chinese industry - ex, virtually all PVC wire is 2-8 AWG smaller than what is printed on the wire; same with silicone wire (but FEP insulated wire is always spot on, cause it's not at the low end ya see), one has to assume that (well, if they still marked the parts) someone has taken a bunch of parts marked I (vertical line), added 3 horizontal lines to each one (One imagines, with the same sort of automated chip marking method that would be used for putting any other pattern, except here it would just be the missing parts of an E. The consistency of the location of markings on packages is remarkably consistent specimen to specimen, such that you might be able to target by position and get it close enough to be convincing, and with just 3 small marks and no grinding, and significant price difference between I and E spec parts for certain parts (oddly, not for most tinies). Of course when they adopted the I and E when they stopped marking parts at all, so this is academic. But can you seriously imagine anyone inspecting 200 boards and writing down every lot number he saw, and emailing the list to Microchip and asking for confirmation that they're all E's as he ordered?). ## Bootloader (Optiboot) Support A new version of Optiboot (Optiboot_x) now runs on the tinyAVR 0-Series, 1-Series and 2-Series chips. It's under 512 bytes, and works on all parts supported by this core, allowing for a convenient workflow with the same serial connections used for both uploading code and debugging (like a normal Arduino Pro Mini). Note the exception about not having autoreset unless you disable UPDI (except for the 20 and 24-pin 2-Series parts which can put reset on PB4 instead), which is a bit of a bummer. diff --git a/megaavr/bootloaders/hex/README.md b/megaavr/bootloaders/hex/README.md index 46a8319a..2b90da6c 100644 --- a/megaavr/bootloaders/hex/README.md +++ b/megaavr/bootloaders/hex/README.md @@ -1,13 +1,43 @@ -# Here live the bootloader hex files. +# Here live the bootloader hex files ## .lst and .elf files - -Additionally, there is a tar'ed BZip'ed copy of all the assembly listings and elf files produced compiling all of these bootloaders, these are in listings.tar.bz2 and elves.tar.bz2. +We include .lst files, and the .elf files in case you for some strange reason need them, these are in listings.tar.bz2 and elves.tar.bz2. ## megaAVR 0-series parts - There is a tar'ed bzipped archive containing bootloaders for the mega0-series built to the same specifications ## Build logs +Build logs are now included - both normal output and error output. + +## Filename encoding +The filenames consist of three or four sections, separated by an _ + +* "optiboot" - identifies the bootloader +* A string identifying the chip or board: + * "txyz": Any 0/1-series tinyAVR except the 8-pin ones. + * "txy2": Any 0/1-series tinyAVR with 8 pins. + * "tx2z": Any 2-series tinyAVR +* If it is set to use anything other than USART0 on default pins, a third parameter appears here: + * (omitted): USART0, default mapping. + * "alt": USART0, alternate mapping. + * "u1alt": USART1, alternate mapping. + * There is no option to use USART1 on default mapping, because the default USART1 mapping is the same as the alternate USART0 mapping. +* Finally the entry conditions are described: + * "extr": Upon external, software, or UPDI reset (UPDI reset means you just uploaded the bootloader (most likely) so you'd like to see the bootloader run so you know it's there). + * "extr8sec": As above, but wait 8 seconds for upload. + * "extronly": Only upon external or UPDI reset. Note that this requires that you configure fuses to have a reset pin or it won't be much fun. + * "poronly8sec: POR or UPDI reset only, and wait 8 seconds (for plug/unplug to enter bootloader) + * "all8sec": Maximum ease of bootloader entry. Enter bootloader on any reset, except for a WDR (which is impossible for technical reasons), or a BOR (indicative of an unstable power supply. That's not when you want to be writing to flash is it?), 8 second wait. + * "swronly8sec": Only upon a software or UPDI reset, 8 second timeout. Note that this fails to adhere to the prime directive. An application which does not fire a software reset will not enter the bootloader after the first time it was programmed, however, there are a surprising number of people who are doing weird stuff like that, and are okay with the possible need to take drastic actions (some of them have failsafes in their sketch) + + +## What is that technical reason? +Optiboot exits using the WDR to make sure everything is cleanly reset for app - since only the RSTRF persists across reset and we can only clear the bits never set them, there is no way to tell if a WDRF came from itself or the app, and so, since assuming it was from the app would result in the bootloader never exiting. + +## Related to above +Since a startup *without* a hardware reset is guaranteed not to work (GNTW), and means that the prime directive of a bootloader is violated ("Survive any behavior that the application could manifest, no matter how perverse."), because an application that smashes the stack, or fires an interrupt that doesn't exist, or manually jumps to 0 would end up at 0, and the bootloader (in order to fit in flash) assumes the chip is in reset state (ie, that it is clocked at 3.33 MHz or 2.66 MHz, and that the USART is not currently enabled and so on). Hence we have no choice but to ensure that we can detect that case and fire an SWR to reset cleanly (likely into the bootloader) when we start up with no reset flags. Jumping to 0x0000 is prohibited. -Build logs are now included of both normal output and error output. +The minimum needed to avoid GNTW behavior without removing any functionality is what we implement: +* Either in the bootloader or if none is used, the start of the application, we must read the reset flags. +* If they are zero, we must issue a software reset. +* If it's not an entry condition, we clear the flags, write the value they held to GPIOR0 (so the app can see them without hving to fish something our of r2, which cannot be guaranteed to even be possible for any given sketch; this method minimizes the cost, and since we never *read* GPIO0, you may freely trash the value in it if you don't need the reset cause flags.) diff --git a/megaavr/bootloaders/hex/mega0.tar b/megaavr/bootloaders/hex/mega0.tar deleted file mode 100644 index 112066ba298547f7ca9e65132eec99573e00c36d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158208 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 C3EHm! diff --git a/megaavr/bootloaders/hex/mega0.tar.bz2 b/megaavr/bootloaders/hex/mega0.tar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..61d45cc748822d170b4050f0e19dd7066a108364 GIT binary patch literal 5213 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 A!T.> */ -/* External defintitions */ +/* External definitions */ /* Actual implementation is in wiring_extra.c (or .cpp, if I find that I'm not able tomake it work with .c) * Because of the incrutable rules of C++ scoping, you can define an inline function or a template function in a header.... * and not in the body of a separate file, while the opposite is true for ANY OTHER KIND OF FUNCTION. */ diff --git a/megaavr/cores/megatinycore/HardwareSerial.h b/megaavr/cores/megatinycore/HardwareSerial.h index ab6beedd..046fa183 100644 --- a/megaavr/cores/megatinycore/HardwareSerial.h +++ b/megaavr/cores/megatinycore/HardwareSerial.h @@ -56,7 +56,7 @@ * is atomic, but significantly hurts performance. (theoretical worst case * is 94 clocks, real-world is usually far less, but I'll only say "less" * The functions in question have considerable register pressure). But, - * it unquestionably would impact USART performance at high speeeds. + * it unquestionably would impact USART performance at high speeds. * * * The USE_ASM_* options can be disabled by defining them as 0 either in variant pins_arduino.h * The buffer sizes can be overridden in by defining SERIAL_TX_BUFFER either in variant file ( diff --git a/megaavr/cores/megatinycore/WInterrupts.c b/megaavr/cores/megatinycore/WInterrupts.c index 23be1d95..7e84952a 100644 --- a/megaavr/cores/megatinycore/WInterrupts.c +++ b/megaavr/cores/megatinycore/WInterrupts.c @@ -320,7 +320,7 @@ * For each of the ones that we do have a flag for, we load that pointer into Z with postincrement, subtract 0 from it and look at zero flag to make sure it's not null. * assuming it's not, we fire icall to call the user function. Either way we then repeat the loop until out of flags. * which at latest will happen when we're also at end of the ports intfunc array.... - * Then, with the initial flags still in 15 and the the VPORT adderess in r16 copy that once more to a pointer register, 0 the high byte, and store the flags value we read to clear it. + * Then, with the initial flags still in 15 and the the VPORT address in r16 copy that once more to a pointer register, 0 the high byte, and store the flags value we read to clear it. * then it's just a matter of making sure we pop everything we pushed onto the stack in the reverse order, including r16 followed by the reti to exit the interrupt.. */ diff --git a/megaavr/cores/megatinycore/WInterrupts_PA.c b/megaavr/cores/megatinycore/WInterrupts_PA.c index b4ac957d..cb95748d 100644 --- a/megaavr/cores/megatinycore/WInterrupts_PA.c +++ b/megaavr/cores/megatinycore/WInterrupts_PA.c @@ -13,10 +13,10 @@ intFunc[0] = intFunc_A; } /* It is the act of referencing attachPortAEnable() in this file that the compiler to also include the ISR. - * (ISRs can't be asssigned at runtime - the only way things like attachInterruprt look like they can is + * (ISRs can't be assigned at runtime - the only way things like attachInterruprt look like they can is * by having the ISR call a function pointer (ie, icall). After saving every call used plus SREG and RAMPZ. * Beyond being slow, these lengthy prologues and epilogs slow take up flash. This can be like 80+ bytes - * per vector... That's ugly, particlarly on small flash parts. See the discussion in WInterrupts.c about + * per vector... That's ugly, particularly on small flash parts. See the discussion in WInterrupts.c about * what this actually does. */ ISR(PORTA_PORT_vect, ISR_NAKED) { diff --git a/megaavr/cores/megatinycore/core_devices.h b/megaavr/cores/megatinycore/core_devices.h index e356373c..35fd4018 100644 --- a/megaavr/cores/megatinycore/core_devices.h +++ b/megaavr/cores/megatinycore/core_devices.h @@ -355,7 +355,7 @@ * Provide a define indicating which revision of EVSYS this is. 1 and 2 differ only in naming of strobe register. * 3 separates the decision of which pin(s) within a port will be used as event input and which of those to use * with the former being configured with PORTx.EVGENCTRL. This allows the number of generators to drop from 8/port to 2/port, and the number of RTC generators to likewise drop to 2 from 16 with 8 available per channel - * In exchange for this, we achieve our longtime dream: Equality between all generator channels, because the redused number of + * In exchange for this, we achieve our longtime dream: Equality between all generator channels, because the reduced number of * generators allows them to add both options for all ports and both RTC options to all generator channels * Too bad they released so many parts with the other versions :-/ */ diff --git a/megaavr/cores/megatinycore/dirty_tricks.h b/megaavr/cores/megatinycore/dirty_tricks.h index 70de589d..0125bdde 100644 --- a/megaavr/cores/megatinycore/dirty_tricks.h +++ b/megaavr/cores/megatinycore/dirty_tricks.h @@ -58,7 +58,7 @@ * * Many of these hinge upon bullying the compiler into assigning registers less stupidly. These do not at all prevent the register allocator from * smacking down the whole thing with a gigantic wad of fail if it thinks it has to (ie, when you're out of pointer registers). These should be used only with one eye - * on the compiler listings to spot this, because at that point they are usually causeing harm instead of good. + * on the compiler listings to spot this, because at that point they are usually causing harm instead of good. * * List of dirty trick macros * Not dirty - cycle counting macros: @@ -306,7 +306,7 @@ Not enabled. Ugly ways to get delays at very small flash cost. * * Very similar to the above. Passed a pointer as two bytes, the high byte constant and the low byte not, this finds use in the same sort * of situatios as high/low math. See the I/O headers: each class of peripherals often has the same high byte for all addresses. Ports (0x0400, 0x0420, 0x0440 - * and so on. The most freqently used functions get special attention paid to this as a small gain adds up for the most commonly called functions + * and so on. The most frequently used functions get special attention paid to this as a small gain adds up for the most commonly called functions * usage is typically sdmething like * lowbyte = (_SWAP(usartnbr)); // passed from,elsewhere io the code, which must be free of bugs! * lowbyte <<= 1; diff --git a/megaavr/cores/megatinycore/wiring_analog.c b/megaavr/cores/megatinycore/wiring_analog.c index 8d8cd62e..7d47caa9 100644 --- a/megaavr/cores/megatinycore/wiring_analog.c +++ b/megaavr/cores/megatinycore/wiring_analog.c @@ -213,7 +213,7 @@ void DACReference(__attribute__ ((unused))uint8_t mode) { * uint8_t gain=0) * Enhanced analogRead(). Still single-ended, res is resolution in bits, * which range from 8 to the largest value that can be obtained from using - * the accumlation feature and a single "start" command to oversample x4 + * the accumulation feature and a single "start" command to oversample x4 * per extra bit, followed by decimation/2 per extra bit (the math is * described in Microchip/Atmel app notes). This maximum is 13 bits for * 0/1-series parts, and 17 bits for 2-series parts. diff --git a/megaavr/extras/Ref_BestPractices.md b/megaavr/extras/Ref_BestPractices.md index 5e0b675d..09f43d07 100644 --- a/megaavr/extras/Ref_BestPractices.md +++ b/megaavr/extras/Ref_BestPractices.md @@ -10,7 +10,7 @@ Before deployment, be sure to run down this checklist for any circuit you design * Assuming this is a custom PCB, fill both layers with ground pours. Tie the two ground pours together with vias. * To avoid delays, make sure you can get all the parts you're using. Order the parts you need ASAP if they seem to be in short supply (almost everything is now) * Read the bloody errata, especially if on a DA, DB, or tiny 0/1. Those things have many relevant errata. -* Choose pins wisely. Make a list of how many pins you need with what functionality. Depending on your application pins 2 and 6 in each port may need to be valued differently, as they can respond to very brief interrupts and wake the chip on rising/failling because they are "fully async" - however, they are more vulnerable to electromagnetic noise for the same reason. +* Choose pins wisely. Make a list of how many pins you need with what functionality. Depending on your application pins 2 and 6 in each port may need to be valued differently, as they can respond to very brief interrupts and wake the chip on rising/failing because they are "fully async" - however, they are more vulnerable to electromagnetic noise for the same reason. * Determining the crystal loading a priori can be very difficult - you need to knowe stray capacitances (which you almost certainly don't have a tool precise enough to measure) and the whole thing is full of black magic. On the classic AVRs, the crystals were power hogs, but it wasn't hard to make them work. Now, they may not be power hogs, but short of very expensive, high precision lab equipment (you need a FET probe for your scope, which I was told cost in the area of 1 grand, and even the soldering to get the adjustable caps on insstead of normal ones can be a job. People argue over what the rule of thumb is (if you ask around, you'll get three different answers, the largest at least 4x the smallest). I have been unable to determine why a given capacitor value (which by all accounts should not have worked, did, while all the other values (which were closer to what the formulas said) wouldn't oscillate). The only thing I can suggest is to get an assortment of small caps in the appropriate range of values, and then use trial and error to find a good working value (unless you have an incredibly well outfitted lab). * If a voltage in excess of Vdd or below ground might ever be applied to any pin, you MUST take measures to limit the current and ensure that it stays within the range given in the datasheet. Often this can be achieved with a diff --git a/megaavr/extras/Ref_Defines.md b/megaavr/extras/Ref_Defines.md index 155a878a..321ab034 100644 --- a/megaavr/extras/Ref_Defines.md +++ b/megaavr/extras/Ref_Defines.md @@ -86,7 +86,7 @@ If your sketch requires a certain timer NOT be used for millis, or that a certai ``` ### Testing if millis enabled -The names of these two functions are #defined as themselves if the function can be used. On DxCore, `millis` is never defined if `micros` is not, since there are no timekeeping options which suppory millis but not micros. On megaTinyCore, if the RTC is used, millis, but not micros, will be available, so `defined(micros)` is false but `defined(millis)` is true. +The names of these two functions are #defined as themselves if the function can be used. On DxCore, `millis` is never defined if `micros` is not, since there are no timekeeping options which support millis but not micros. On megaTinyCore, if the RTC is used, millis, but not micros, will be available, so `defined(micros)` is false but `defined(millis)` is true. ## Identifying part family within sketch When writing code that may be compiled for a variety of target chips, it is often useful to detect which chip it is running on.Defines of the form `__AVR_partname__` are provided by the toolchain. However, it is often MORE useful to not have to have a giant block of #ifdefs, and have the part numbers distilled down to something more that can be checked more intuitively, since what you care about is the family, number of pins, or occasionally size of the flash. diff --git a/megaavr/extras/Ref_Differences.md b/megaavr/extras/Ref_Differences.md index c24f56ff..15c5cf48 100644 --- a/megaavr/extras/Ref_Differences.md +++ b/megaavr/extras/Ref_Differences.md @@ -1,7 +1,7 @@ # Differences between megaTinyCore and stock core We generally try to honor the Arduino API. Occasionally that is impractical. In some cases we are just explicitly stating what was true of the Arduino stock cores, but never acknowledged. -## Intended cases where behavior differs from official cores: +## Intended cases where behavior differs from official cores While we generally make an effort to emulate the official Arduino core, there are a few cases where the decision was made to have different behavior to avoid compromising the overall functionality; the official core is disappointing on many levels. The following is a (hopefully nearly complete) list of these cases. ### I2C **Requires** External Pullup Resistors @@ -18,12 +18,21 @@ Later efforts to improve serial further increased it's performance and reduced t ### Serial supports like, everything Almost every weirdo hardware feature that the USART has, we expose. It is also able to receive up to 1 rx buffer full of data at the maximum baud rate, with the transmitting device sending continuously. Official megaAVR core can crash if you try to redline it, and classic AVRs can't get the baud rate on target even way way below the maximum, so there's really no comparison there. +### Serial options have different numeric values +The stock megaavr core used 12 bits across two bytes to store the configuration written to CTRLC... *to the 1 byte register, CTRLC*... Say you have a register, and 6 bits in it might be configured. Your users generate the value by ORing together constants you supply. + +Do you: +1. Use the locations of the bits that get controlled, so the value passed to begin is the same as the value you have to write to ctrlc? +2. Make your own representation of it, which takes twice as many bits to represent, and is full of "reserved" positions for options that exist on parts that are not this AVR. + +Hopefully I don't need to tell you which way they did it. + ### The core *requires* compiling with -Os and LTO LTO (link time optimization - it gets you about 15-20% smaller binaries that perform comparably to or better than normal ones) is required. This is set up automatically and is not a concern unless you are building in a very-not-arduino-like context - but if you try to disable it, everything will fall down around your ears. We make *very* heavy use of builtin functions like `__builtin_constant_p()` and similar in order to maximize the potential optimization, and some expanded API functions (most notably fast digital I/O) **require** LTO in order to work (in the fast digital I/O case, we need to be able to see that the thing that the user passed as a pin is constant. If we can't peer into other files to figure out if it's a constant, we can't do fast digital I/O. Furthermore, -Os is required - we looked into offering -O3 - but we found that this caused a meticulously hand-implemented wall of intricate assembly that was implemented to eliminate duplication of code *to itself be duplicated* which in turn caused a compilation failure because it includes a named label. -The optimizer frankly isn't very good at doing either -Os or -O3, so you're missing less than you think. The compiler is very clever in some ways, but it's also very stupid in others. It's been too long since all the optimization passes were properly tuned and the avr-portion was up to date with the rest of it for the optimizer to really do a great job. +The optimizer frankly isn't very good at doing either -Os or -O3, so you're missing less than you think. The compiler is very clever in some ways, but it's also very stupid in others. It's been too long since all the optimization passes were properly tuned and the avr-portion was up to date with the rest of it for the optimizer to really do a great job. The compiler appears to have peaked at version 4.3. 5.4 was worse (enough so that it broke pulsein, which is why it's supplied as asm). 6.x had some bad bug that we couldn't abide by - but it added support for LTO, which was a total gamechanger; They quickly went to 7.3, and that's still where we are. It's established wisdom that you want to use the oldest version of GCC that will do what you need, not the newest. ### SerialEvent Support is Dropped This is deprecated on the official core and is, and always has been, a dreadful misfeature. Dropped as of 2.3.0. @@ -89,9 +98,9 @@ No more. The working registers are not mapped to the data space. Mapping the working registers to the dataspace is madness. WTF good was it? If you're writing C, then you don't know what working register a variable is in, and if you're writing assembly you just look at the register; if that feature was useful to you you were doing something wrong. Now the I/O space 0x00 to 0x3F is mapped to data space 0x0000 to 0x003F. -## Direct Register Manipulation -If you are manually manipulating registers controlling a peripheral, except as specifically noted in relevant reference pages, the stated behavior of API functions can no longer be assured. It may work like you hope, it may not, and it is not a bug if it does not, and you should not assume that calling said API functions will not adversely impact the rest of your application. For example, if you "take over" TCA0, you should not expect that using `analogWrite()` - except on the two pins on the 20/24-pin parts controlled by TCD0 - will work for generating PWM. If you reconfigure TCA0 except as noted in Ref_Timers, without calling `takeOverTCA0`, both `analogWrite()` and `digitalWrite()` on a PWM pin may disrupt your changed configuration. +## Direct Register Manipulation warning +If you are manually manipulating registers controlling a peripheral (except as specifically noted in relevant reference pages - some portions of the API are designed specifically permit this, particularly the PWM timers), *the stated behavior of API functions related to that by means obvious or obscure, can no longer be expected*. It may work like you hope, it may not - but it is not a bug if it doesn't work. (When you start poking at registers, you should usually be taking full control of that peripheral, and not using related api functions or libraries). Thus you should not assume that calling said API functions will not adversely impact the rest of your application. For example, if you "take over" TCA0, you should not expect that using `analogWrite()` - except on the two pins on the 20/24-pin parts controlled by TCD0 - will work for generating PWM. If you reconfigure TCA0 except as noted in Ref_Timers, without calling `takeOverTCA0`, both `analogWrite()` and `digitalWrite()` on a PWM pin may disrupt your changed configuration, or interfere with normal port operation. This, strictly speaking, is not a difference from the stock core. It will also melt down if you fiddle with hardware registers arbitrarily. There are two big differences: -1. The stock core is not explicit about this fact, despite the fact that it will always happen. +1. The stock core is not explicit about this fact, despite the fact that it's at least as bad. 2. The stock core exerts slightly more effort to picking up after other code defensively, but only slightly, and often at surprisingly high overhead. diff --git a/megaavr/extras/Ref_DirectPortManipulation.md b/megaavr/extras/Ref_DirectPortManipulation.md index 1f01c2d3..f33c089b 100644 --- a/megaavr/extras/Ref_DirectPortManipulation.md +++ b/megaavr/extras/Ref_DirectPortManipulation.md @@ -59,21 +59,21 @@ You will very often see constant names from the io headers used. Unlike the clas PORTx.DIR is the register which determines if the pin is an input or an output (like DDRx registers in classic AVR). After reset, all pins are set to input (0 in the register). In order to use the pin as an output the bit in the register must be set to 1, which can be done as follows: -``` +```c PORTA.DIR = PIN4_bm ``` This will set bit 4 on PORTA - if using an ATtiny1616, for example, that would correspond to Arduino pin 0. Multiple pins on the same port can be configured by writing each bit, or by setting multiple bitmasks -``` +```c PORTA.DIR = 0b01001000; // sets PA3 and PA6 as an output PORTA.DIR = PIN3_bm | PIN6_bm; // sets PA3 and PA6 as an output ``` Besides setting the pins to an input (0) or output (1) in the DIR register, you can also use the DIRSET and DIRCLR registers to set (set as output) or clear (set as input) a specific pin: -``` +```c PORTA.DIRSET = PIN4_bm; // use PA4 as an output PORTA.DIRCLR = PIN4_bm; // use PA4 as an input ``` @@ -82,21 +82,21 @@ You can even toggle between an input or output by writing to the DIRTGL register Turning the pin on and off is done with the PORTx.OUT register (this works like the PORTx register of classic AVR). Writing a 1 to the corresponding pin will set an output pin HIGH while a 0 will set it LOW: -``` +```c PORTA.OUT |= PIN4_bm; // write PB4 high - Don't do it like this in real life! PORTA.OUT &= ~PIN4_bm; // write PB4 low - Don't do it like this in real life! ``` Note that the two examples above for flipping a single bit are not atomic - it's a read-modify-write operation, and will take between 4 and 8 clocks and between 3 and 7 words of flash. If an interrupt fires between the read and the write, the change that the ISR made will be reverted - if you have ISRs that are flipping pins (Servo.h does this), lines like those shown above would have to be be performed with interrupts disabled (just like classic AVR). Fortunately, the modern AVR architecture provides a better solution - the OUTSET and OUTCLR registers, just like the DIRSET and DIRCLR registers described above - this is atomic - as well as being faster and smaller -``` +```c PORTA.OUTSET = PIN4_bm; // turn PA4 output on - Atomic operation taking 2-3 words, 2-3 clocks PORTA.OUTCLR = PIN4_bm; // turn PA4 output off ``` Or when you just want to toggle the output you can use: -``` +```c PORTA.OUTTGL = PIN4_bm; // toggle PA4 output ``` @@ -104,21 +104,21 @@ PORTA.OUTTGL = PIN4_bm; // toggle PA4 output You can read the state of a pin by using the IN register (this is like the PINx register of classic AVR): -``` +```c bool status = PORTA.IN & PIN5_bm; ``` Unlike the classic AVRs, setting a pin HIGH with the OUT register while it is set as an input will not turn on the internal pullup. If you want to use the internal pullup resistor, you can set this in the PINnCTRL register as follows: -``` +```c PORTA.PIN6CTRL |= PORT_PULLUPEN_bm; // use the internal pullup resistor on PA6 PORTA.PIN6CTRL &= ~PORT_PULLUPEN_bm; // don't use the internal pullup resistor on PA6 ``` Note that this does mean that each pin has its own PINnCTRL register - unlike the classic AVRs where there was one register to control pullup for each port, with one bit per pin. The rest of the PINnCTRL register configures the "Input Sense Configuration", otherwise known as pin interrupts, as well as providing an way to disable the pin input buffer entirely to save power, and an option to invert the pin (some other parts may have additional advanced features here). Assuming you aren't using those, these are valid. -``` +```c PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // use the internal pullup resistor on PA6 PORTA.PIN6CTRL = 0; // don't use the internal pullup resistor on PA6 ``` @@ -139,7 +139,7 @@ The |= and &= assignment operators (assigning the bitwise OR or bitwise AND of t HOWEVER, on those magic 32 registers in the "low I/O space", provided you are setting or clearing a single bit, AND that bit is compile time known, they optimize to a single instruction: sbi or cbi (set or clear bit index), which is not only atomic, but runs in a single clock cycle (unlike the 2 on classic AVRs). That makes it faster than simple assignment, which requires two instructions (potentially 3 in unusual circumstances) - an LDI (load immediate) to put the value into a CPU register, and an OUT instruction to write it out (the single cycle IN and OUT instructions only work up to address 0x3F, above that the only available options are slower. So which are these magic registers? Unlike classic AVR devices, where a near-random assortment of registers, in addition to the pin manipulation ones, were placed in the low I/O space, on modern AVR devices, it is very simple: Only the VPORT registers and the four GPIO registers (which do nothing, they're for general application use). Similarly, there's also a single instruction branch for testing a single, compile time known bit in these registers. -So, while many examples of direct port writes for classic AVRs will use |= and &= for single bit flips, when using VPORT registers (Or when using the GPIO registers, but if you're using those, you're using them exclusively because of this, and know what you're doing). **There is never a right time to write to use |= or &= with any register in PORTx except for PINnCTRL** registers. It is always slower, and it is never interrupt safe. So don't do that. Specifically, as warned about above, you should NEVER use |= or &= with the PORTx.DIRCLR, PORTx.DIRSET, PORTx.OUTSET or PORTx.OUTCLR; the SETs are simply inefficient, while the CLRs produce unexpected behavior. The same hazard that applies to the DIRCLR and OUTCLR regisers also applies to INTFLAGS - it's a register where writing 1's clears bits instead of setting them, and the compiler does not know anything about them other than that the're 'volatile' and it is required to do exactly what you say. +So, while many examples of direct port writes for classic AVRs will use |= and &= for single bit flips, when using VPORT registers (Or when using the GPIO registers, but if you're using those, you're using them exclusively because of this, and know what you're doing). **There is never a right time to write to use |= or &= with any register in PORTx except for PINnCTRL** registers. It is always slower, and it is never interrupt safe. So don't do that. Specifically, as warned about above, you should NEVER use |= or &= with the PORTx.DIRCLR, PORTx.DIRSET, PORTx.OUTSET or PORTx.OUTCLR; the SETs are simply inefficient, while the CLRs produce unexpected behavior. The same hazard that applies to the DIRCLR and OUTCLR registers also applies to INTFLAGS - it's a register where writing 1's clears bits instead of setting them, and the compiler does not know anything about them other than that the're 'volatile' and it is required to do exactly what you say. ## VPORTx.OUT vs PORTx.OUTSET/PORTx.OUTCLR So, knowing all this, when is it more efficient to use `VPORTx.OUT |= PIN3_bm` and when is it more efficient to use `PORTx.OUTSET=PIN3_bm`? @@ -149,11 +149,11 @@ So, knowing all this, when is it more efficient to use `VPORTx.OUT |= PIN3_bm` a ## Equivalents to classic AVR registers -Classic AVR | modern AVR | VPORT --------------|---------------|-------------- - PORTx | PORTx.OUT | VPORTx.OUT - PINx | PORTx.IN | VPORTx.IN - DDRx | PORTx.DIR | VPORTx.DIR +| Classic AVR | modern AVR | VPORT | +|-------------|---------------|--------------| +| PORTx | PORTx.OUT | VPORTx.OUT | +| PINx | PORTx.IN | VPORTx.IN | +| DDRx | PORTx.DIR | VPORTx.DIR | **NOTE** Unlike classic AVRs, setting the bit in PORTx.OUT while pin is set as an INPUT will *NOT* enable the pullups. Only the PORTx.PINnCTRL registers can do that. There is no VPORT register that allows changing pullup status. Writing to PORTx.IN does *NOT* toggle the output of the bit, but writing to VPORTx.IN *DOES*. @@ -289,7 +289,7 @@ Not so DIRCLR - If you do `PORTx.DIRCLR |= (1 << 2) | (1 << 3)`. The compiler wi None of that behavior is useful. Only use simple assignment with the SET/CLR/TGL registers, or regret it. -### Don't use a bloody pointer or reference to a vport register. +### Don't use a bloody pointer or reference to a vport register * You don't get CBI/SBI speed. * If you know you can't get the benefits of VPORT registers (likely, because you don't know which port at compile time and are using a pointer or something), you can do things equally well or better using the PORTx registers. * Really, VPORTs should only be used if the port, and preferably the bit, are known at compile time, and you are either using simple assignment, flipping a single bit, or testing a single bit - that is required to get the performance benefit. And that's what makes them better than the PORTx registers - otherwise, they're just PORTx registers with fewer features that can only atomically set or clear one bit at a time. diff --git a/megaavr/extras/Ref_Functions.md b/megaavr/extras/Ref_Functions.md index 22bca6e7..dcf62953 100644 --- a/megaavr/extras/Ref_Functions.md +++ b/megaavr/extras/Ref_Functions.md @@ -219,7 +219,7 @@ Part of the standard API, but not documented. Does exactly what the name implies #define microsecondsToClockCycles icroseconds * clockCyclesPerMicrosecond(); ``` -### (standard) `_NOP()` Execute a single cycle NOP (no operation) instruction which takes up 1 word of flash. +### (standard) `_NOP()` Execute a single cycle NOP (no operation) instruction which takes up 1 word of flash ### (DxC/mTC) `_NOPNOP()` or `_NOP2()` Execute a 2 cycle NOP (no operation) instruction (`rjmp .+0`)which takes up 1 word of flash. (Added 1.3.9) diff --git a/megaavr/extras/Ref_Interrupts.md b/megaavr/extras/Ref_Interrupts.md index b1bb2207..26532b3c 100644 --- a/megaavr/extras/Ref_Interrupts.md +++ b/megaavr/extras/Ref_Interrupts.md @@ -269,7 +269,7 @@ One of the worst things is calling a function that won't end up being inlined or ### ISRs benefit the most from using the GPRs If you're desperate for speed - or space - and if you need to set, clear or test a global flag, you can use one of the general purpose registers: GPIOR0/1/2/3. The only place the core uses any of those is at the very beginning of execution, when the reset cause is stashed in `GPIOR0` before the reset flags are cleared and the sketch is run (that way you can see what the reset cause was - while the 99% of users who do not need to check that flag benefit from having the reset flags cleared, because it enables the initialization code to turn a crash that might hang or result in incorrect behavior to instead simply reset cleanly see [the reset reference](Ref_Reset.md)); the other 2 bits of GPIOR0 are used to record potential errors when a tuned internal clock is selected, again just so that user code has a way of accessing them. All of this is done before setup (ie, if you need it for this sort of thing, set it to 0 in setup). -To get the full benefit of the GPR's, you must use single-bit operations only, and be sure that the bit is known at compile time. These are lighning fast, and use no working registers at all. `GPIOR1 |= (1 << n)` where n is known at compile time, is a single clock operation which consumes no registers - it gets turned into a `sbi` - set bit index, with the register and bit being encoded by the opcode itself. The same goes for `GPIOR &= ~(1 << n)` - these are also atomic (an interrupt couldn't interrupt them like it could a read-modify-write). There are analogous instructions that make things like `if(GPIOR1 & (1 << n))` and `if (!(GPIOR1 & (1 << n))` lightning fast. GPIOR's are only magic when manipulating a single bit, and the bit and must be known at compile time: `GPIOR1 |= 3` is a 3 clock non-atomic read-modify-write operation which needs a working register to store the intermediate value in while modifying it, which is just slightly faster than `MyGlobalByte |= 3`, which is a 6-clock non-atomic read-modify-write using 1 working register for the intermediate. `GPIOR1 |= 1; GPIOR1 |= 2;`, which achieves the same thing as GPIOR1 |= 3, is 2 clocks, each of which is an atomic operation which does not require a register to store any intermediate values. Note that atomicity is only a concern for code running outside the ISR, or code within a level 0 priority ISR when some other ISR is configured with level 1 priority. However, the fact that has no register dependence is a bigger deal in an ISR, because each working register used concurrently has to be saved at the start of the ISR, and restored at the end (total 3 clocks and 2 words). The most surprising thing is that `GPIOR1 = 3` is a 2 clock operation *which needs a working register*, that is, it would often cost 5 clock cycles, and could not be used as part of a naked ISR (next section) without assemble, however, `GPIOR1 |= 1; GPIOR1 |= 2;` can! +To get the full benefit of the GPR's, you must use single-bit operations only, and be sure that the bit is known at compile time. These are lightning fast, and use no working registers at all. `GPIOR1 |= (1 << n)` where n is known at compile time, is a single clock operation which consumes no registers - it gets turned into a `sbi` - set bit index, with the register and bit being encoded by the opcode itself. The same goes for `GPIOR &= ~(1 << n)` - these are also atomic (an interrupt couldn't interrupt them like it could a read-modify-write). There are analogous instructions that make things like `if(GPIOR1 & (1 << n))` and `if (!(GPIOR1 & (1 << n))` lightning fast. GPIOR's are only magic when manipulating a single bit, and the bit and must be known at compile time: `GPIOR1 |= 3` is a 3 clock non-atomic read-modify-write operation which needs a working register to store the intermediate value in while modifying it, which is just slightly faster than `MyGlobalByte |= 3`, which is a 6-clock non-atomic read-modify-write using 1 working register for the intermediate. `GPIOR1 |= 1; GPIOR1 |= 2;`, which achieves the same thing as GPIOR1 |= 3, is 2 clocks, each of which is an atomic operation which does not require a register to store any intermediate values. Note that atomicity is only a concern for code running outside the ISR, or code within a level 0 priority ISR when some other ISR is configured with level 1 priority. However, the fact that has no register dependence is a bigger deal in an ISR, because each working register used concurrently has to be saved at the start of the ISR, and restored at the end (total 3 clocks and 2 words). The most surprising thing is that `GPIOR1 = 3` is a 2 clock operation *which needs a working register*, that is, it would often cost 5 clock cycles, and could not be used as part of a naked ISR (next section) without assemble, however, `GPIOR1 |= 1; GPIOR1 |= 2;` can! ## Naked ISRs One of the most advanced techniques relating to interrupts. This requires that either your ISR be written entirely in assembly, with your own prologue and epilogue hand optimized for your use case, or that you know for a fact that the tiny piece of C code you use doesn't change `SREG` or use any working registers. diff --git a/megaavr/extras/Ref_Microchip.md b/megaavr/extras/Ref_Microchip.md index df47174f..4a7d31e0 100644 --- a/megaavr/extras/Ref_Microchip.md +++ b/megaavr/extras/Ref_Microchip.md @@ -64,6 +64,7 @@ If working with assembly you should make a rigorous study of the insruction set Notice that while datasheet information can be generalized across the whole family of parts The datasheets typically differ only in the header and footer (excepting the 1-series, which is effectively two part families, the 16k+ and the 2-8k parts). But because they use different dies, and dies are designed at discrete points in time, flash sizes released later have fewer bugs, because they've been spending the intervening months stomping out errata. On the 0 and 1 series tinyAVRs, there is a mindboggling amount of errata and a terrible shortage of die revisions. The 2-series tinyAVR by contrast has very little and the DD even less, though the EA's new RWW flash system is pretty janky. See also [Errata.md](Errata.md). +* Short and sweet for the 2-series. * [tinyAVR 2-series 32k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny3224-3226-3227-SilConErrataClarif-DS40002342A.pdf) * [tinyAVR 2-series 16k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny1624-26-27-SilConErrataClarif-DS80000902D.pdf) * [tinyAVR 2-series 4/8k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny424-426-427-824-826-827-SilConErrataClarif-DS80000955B.pdf) @@ -71,8 +72,8 @@ See also [Errata.md](Errata.md). * [tinyAVR 1+series 32k parts - does NOT apply to smaller parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny3216-17-SilConErrataClarif-DS80000887B.pdf) * [tinyAVR 1+series 16k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny1614-16-17-SilConErrataClarif-DS80000886C.pdf) - yeah, the 32k ones are at Rev. C of the die, and had no Rev B, and most of the bugs are fixed, but down on 16k, we did get a Rev. B which fixed almost nothing. * [tinyAVR 1-series 8k parts + 417](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny417-814-816-817-SilConErrataClarif-DS80000934A.pdf) - Based on the early revision of this document, I am skeptical of it's completeness - * [tinyAVR 1-series 2-4k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny212-214-412-414-416-SilConErrataClarif-DS80000933.pdf) - Based on the early revision of this document, I am skeptical of it's completeness -* Less than the 1-series, but that's unsurprising since these cut so many features (and for so little cost savings), so there's just less for there to be errata in. + * [tinyAVR 1-series 2-4k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny212-214-412-414-416-SilConErrataClarif-DS80000933.pdf) (note: this is now the latest version) +* Down a bit on the 0-series. This is unsurprising; based on device ID's, they look to have been registered in the database later (and things do get registered some time before release, because parts that were never released leave "holes" )) * [tinyAVR 0-series 8-16k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny804-06-07-1604-06-07-SilConErrataClarif-DS80000951A.pdf) - Based on the early revision of this document, I am skeptical of it's completeness * [tinyAVR 0-series 2-4k parts](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/Errata/ATtiny202-204-402-404-406-SilConErrataClarif-DS80000956A.pdf) diff --git a/megaavr/extras/Ref_Printf.md b/megaavr/extras/Ref_Printf.md index 9db41068..66b9cb8e 100644 --- a/megaavr/extras/Ref_Printf.md +++ b/megaavr/extras/Ref_Printf.md @@ -1,5 +1,5 @@ # printf() method, and "printf() in general" - considerations for correct use -There are a whole bunch of standard c functions with names that are some variantion on "printf()" (fprintf, sprintf, snprintf, vprintf, etc). If you have desktop C experience, you've likely used them there, and you may have played with it on Arduino too. However, "stuff involving format strings" is also a notorious source of strange bugs in software, including no small number that were exploitable to great impact - and that's on desktop PC's, where the type weirdness doesn't become quite so visible, as you'll see below. +There are a whole bunch of standard c functions with names that are some variation on "printf()" (fprintf, sprintf, snprintf, vprintf, etc). If you have desktop C experience, you've likely used them there, and you may have played with it on Arduino too. However, "stuff involving format strings" is also a notorious source of strange bugs in software, including no small number that were exploitable to great impact - and that's on desktop PC's, where the type weirdness doesn't become quite so visible, as you'll see below. ## We have the printf() method Unlike the official board packages, but like many third party board packages, megaTinyCore includes the `printf()` method for the Print class. Print is the parent class of Stream, and either Print or Stream is the parent class of - uh - just about every class you see with a print() method, including HardwareSerial and TwoWire. diff --git a/megaavr/extras/Ref_Reset.md b/megaavr/extras/Ref_Reset.md index a18bc005..a9847b1e 100644 --- a/megaavr/extras/Ref_Reset.md +++ b/megaavr/extras/Ref_Reset.md @@ -79,7 +79,7 @@ The reset sources are listed below. Note that for brevity and familiarity to tho ### The power of two software resets The fact that there are now two ways of resetting from software changes things. There is an obvious way that both features can be used (an application uses the WDT to detect that it has gotten hung up somewhere and reset itself, and issues a software reset when some set of conditions is met or per user command). This is likely the most common use of them outside of Arduino. But there is also a second way that these can be used, and this is how we use it by default - Use SWRF as a "reset and run bootloader" while you can trigger resets without SWRF by the standard method of setting the WDT for minimum time and then going into forever loop which will trigger the reset. -It is not recommended to set windowed mode and continualy send WDRs. You're doing something strange. When someone else reads it, or you read it later, you will likely have to come back and read either this document or the datasheet to understand how continually WDRs which normally would prevent the WDT from resetting, but for windowed mode. That would be okay - an ugly hack if it would reult in elimiating the 8 ms wait - but it just replaces it with a slightly slower one - WDRs, like everything related to the WDT, have a 2-3 WDT clock sync delay. The WDT clock is around 1 kHz derived from the internal 32 kHz ULP. Once you write the settings to the WDT register, it will take 2-3ms. Then the timer starts up. Now you can execute WDR, and it's no longer a no-op. The first WDR goes in - but you can't queue up a second WDR - WDR will again be a no-op unil, 2-3 ms later, the first WDR finishes syncing; it will reach the WDT and reset the timer - only after this first WDR is the window used, and only after that WDR is synced will a WDR instruction do start syncing a WDR. 2-3 ms later, it will finally slam into the closed window. Now a total of 3 sync periods have happened, for a total of 6-9 ms If we had not done this and simply used the shortest timeout, we would reset in 10-11 ms (2-3 ms sync before it turns on, minimum setting of 8 WDT ticks as WDT period. We could save as little as 1ms or as much as 6ms. +It is not recommended to set windowed mode and continualy send WDRs. You're doing something strange. When someone else reads it, or you read it later, you will likely have to come back and read either this document or the datasheet to understand how continually WDRs which normally would prevent the WDT from resetting, but for windowed mode. That would be okay - an ugly hack if it would reult in eliminating the 8 ms wait - but it just replaces it with a slightly slower one - WDRs, like everything related to the WDT, have a 2-3 WDT clock sync delay. The WDT clock is around 1 kHz derived from the internal 32 kHz ULP. Once you write the settings to the WDT register, it will take 2-3ms. Then the timer starts up. Now you can execute WDR, and it's no longer a no-op. The first WDR goes in - but you can't queue up a second WDR - WDR will again be a no-op unil, 2-3 ms later, the first WDR finishes syncing; it will reach the WDT and reset the timer - only after this first WDR is the window used, and only after that WDR is synced will a WDR instruction do start syncing a WDR. 2-3 ms later, it will finally slam into the closed window. Now a total of 3 sync periods have happened, for a total of 6-9 ms If we had not done this and simply used the shortest timeout, we would reset in 10-11 ms (2-3 ms sync before it turns on, minimum setting of 8 WDT ticks as WDT period. We could save as little as 1ms or as much as 6ms. No matter what, triggering a reset with the WDT hence takes far longer than other methods., but at 11 ms, still a very short period of tme by human standards. diff --git a/megaavr/extras/Ref_Robust.md b/megaavr/extras/Ref_Robust.md index c7bccaa2..76ce5adb 100644 --- a/megaavr/extras/Ref_Robust.md +++ b/megaavr/extras/Ref_Robust.md @@ -13,7 +13,7 @@ See the datasheet for your part to check the exact power consumption and compare BOD will keep the chip in reset when you it knows the voltage is too low to keep the chip running. -### A boootloader is likely the right approach if you want end users to be uploading updates +### A bootloader is likely the right approach if you want end users to be uploading updates There's a python library to upload via STK500 (the protocol we use) which would allow you to make an appropriate updater for your device That is the only case in which it makes sense to use Optiboot on a device you are going to be selling (except a development board, but in that case, your users are hopefully able to manage "burn bootloader" if they want it.... @@ -54,7 +54,7 @@ The Dx-series parts overclock fantastically well. That should not be an invitati ### Check the thickness of the wire you're using I don't mean reading the number on the insulation anyone can print "22 AWG" on wire, but that doesn't make it 22 AWG. Strip it, measure the bundle of wire, separate it to strands, count them, and measure one strand as accurately as you can (need to measure to at least 0.01 mm) and calculate what gauge it actually is. Some dishonest Chinese manufacturers (and probably some in other countries, but China is by far the most prolific producer of everything) frequently use wire 2-6 AWG smaller than the number they print on the insulation. In order to keep people from immediately noticing (because it's too thin) they make up the difference with extra thick insulation. Cheap hookup wire (UL1007) on AliExpress, for example, is usually 3-4 AWG too thin. Some pre-wired JST-SM connectors I've gotten had what appears to be 32-gauge wire marked 24. I didn't discover this until heat from the load through the too-small wires had resulted in it heating up and turning the red insulation black; when investigating the subsequent failure, I was mystefied as to why there were two black wires instead of a black and a red... -Apparently it's not just wire being exported that's like this - I hear stories of assembled equipment also made with the undersized wire, abd failing as a result. The impression I get is that some of the manufacturers using it don't realize it's undersized (likely they are aware that undersized wire exists, were assered that the stuff they were getting was not, and maybe even measured it back when they started buying from that supplier...) +Apparently it's not just wire being exported that's like this - I hear stories of assembled equipment also made with the undersized wire, abd failing as a result. The impression I get is that some of the manufacturers using it don't realize it's undersized (likely they are aware that undersized wire exists, were asserted that the stuff they were getting was not, and maybe even measured it back when they started buying from that supplier...) ### Do not use dupont line in production devices At least not the cheap crap normally sold as that. The cheao dupont line is for prototyping only, and barely suitable for that. Real DuPont line with real DuPont terminals is fine (that division is now run by Amphenol, the line is called MiniPV), as long as you make sure you get ones made by Amphenol obtained through a reputable western supply house. They should cost a minimum of 10 cents each in quantiy for no-frills terminals, and typically more like 20-30 for decent gold plated ones. Use the highest spring tension ones unless you have more than 20 pins ins a connector, and make sure that you or your manufacturing partner is doing a good job crimping them on (go try the pull test on some cheap dupont line - most of it doesn't even pass the pull test) If you look at the terminal of real dupont connectors, they are obviously not the same design as the cheap ones. @@ -67,7 +67,7 @@ Any connector that can be plugged in backwards will be. Try to design the pinout Or my 3-pin UPDI header. Ground in the middle, Vcc and UPDI on the sides. Plugging it in backwards connects power to UPDI and UPDI to power. There's almost always a current limiting resistor somewhere on the UPDI line (there should be!), and the modern devices are much more forgiving of current through the clamp diodes than classic AVRs are, often being rated for max of 15-20mA, and on the tinyAVRs, it's also the HV pin (otherwise, it has protection/clamp diodes, but that's what the resistor limits the current through). Don't use two connectors of the same type, same gender, and same number of pins on one device if they're not interchangible. It is worth using a larger connector and leaving one slot empty. You can cut the pin off the socket and fill the eopty hole in the housing with glue (the plastic polarizing pins, if you can find them, are a lot more graceful, but they seem to be scarcer than hens teeth). -If you can't keep people from reversing a connector, and you can't make it harmless if they do so through pinout choice, or don't want to because it wiould require too many other compromises, how about a diode in series with the problem wire (liekly power)? +If you can't keep people from reversing a connector, and you can't make it harmless if they do so through pinout choice, or don't want to because it wiould require too many other compromises, how about a diode in series with the problem wire (likely power)? ### The Second law of Connectors You will have one or more connectors that carry power. Connectors get abused over time, and foreign objects can get into them. You do not want them to cause a short when thety do. There are many connectors that manage to keep both inacessable (for example, USB). With other connectors, generally **The side which supplies power should always be the female** - it is almost always easier for the male terminals to be shorted by contact with the environment than female terminals. Also, the convention is that the negative side is ground, and is considered less hazsrdous. That is whty barrrel jacks are almost always center positive. @@ -103,4 +103,4 @@ If it doesn't work, you need to either solve the technical problem, or clearly d In warm conditions, are any heatsinks you're using sufficient? How close does the temperature get to the ratings? ### Shake, poke, drop -Everything gets abused. Make sure your physical assembly is sufficiently robust. Shake it while it's running and make sure it doesn't reset (unless that's what it's designed to do, or is too heavy to shake). Poke and prod it while it's running (assuming it doesn't have dangerous voltages exposed, which it really ought not to have!). Hold it by it's wires, Yank out it's cable (Micro USB connectors should be the kind with the through holes, which prevent a yank from pulling the connector off the PCB, unless you have a case around it to keep the force from being handled mainly by the connector. The crap connectors that many developemnt boards have used are so bad they've given MicroUSB a bad rep in some circles - including for a time Arduino circles due to the connector on early Arduino Micros. Pick them up and drop them from as high a height as is plausible. Your customers may use the products while frazzled and hurried, at awkward physical angles, and while intoxicated - someimes all of the above. If your product is unavoidably fragile, warn people (but don't cry wolf if it's not). +Everything gets abused. Make sure your physical assembly is sufficiently robust. Shake it while it's running and make sure it doesn't reset (unless that's what it's designed to do, or is too heavy to shake). Poke and prod it while it's running (assuming it doesn't have dangerous voltages exposed, which it really ought not to have!). Hold it by it's wires, Yank out it's cable (Micro USB connectors should be the kind with the through holes, which prevent a yank from pulling the connector off the PCB, unless you have a case around it to keep the force from being handled mainly by the connector. The crap connectors that many developemnt boards have used are so bad they've given MicroUSB a bad rep in some circles - including for a time Arduino circles due to the connector on early Arduino Micros. Pick them up and drop them from as high a height as is plausible. Your customers may use the products while frazzled and hurried, at awkward physical angles, and while intoxicated - sometimes all of the above. If your product is unavoidably fragile, warn people (but don't cry wolf if it's not). diff --git a/megaavr/extras/Ref_TCD.md b/megaavr/extras/Ref_TCD.md index 63b63165..6526d622 100644 --- a/megaavr/extras/Ref_TCD.md +++ b/megaavr/extras/Ref_TCD.md @@ -7,7 +7,7 @@ TCD not a friendly timer. It is one of the most complex and byzantine peripheral In addition to the synchronization, some of the intentional features make it challenging to work with, namely that it seems to be designed for applications where the slightest misconfiguration will cause catastrophic failure (ex, safety critical parts in automotive systems, big or high speed BLDC motors that can spin themselves into pieces, PSC's that could shoothrough and fail dramaticaly, etc) For this reason, much of the configuration is concerned with fault detection, and with preventing pins from being unintentionally enabled. Not only is it enable-locked, it also requires `_PROTECTED_WRITE()` to write to some key registers, which produces the timed write procedure required for reconfiguring such critical things as the clock system - and the TCD output pins. You can turn the timer off without the timed write. But you cannot turn pins on or off without it, nor set their "default state", which is what value the pins have when the fault detection events are used to turn off the output. ## Normal analogWrite() operation -`TCD0` provides two PWM channels: `WOA` can output on PA4 or PA6, `WOB` on PA5, PA7. Those channels can each drive either - or both - of those pins, but only at one duty cycle. Users may prefer to configure this manually - `TCD0` is capable of, among other things, generating much higher frequency PWM, as it can be clocked from the PLL at 48MHz (or more, if you don't mind exceeding the specified operating ratings - I've gotten it up to 128 MHz, allowing 8-bit pwm at 500 kHz, or a 64 MHz squarewave). It is supposed to be remappable to other sets of pins. This doesn't work in current silicon, but future silicon revs will fix this - in all cases, the core uses channel A for the 2 even numbered pins, and channel B for the two odd numbered pins (if you take it over, the two higher nubmered pins can be set to either one, we only support one option through analogWrite(). If you try to turn on third PWM pin (or both even or both odd numbered ones), the other pin controlled by that channel will also change duty cycle - you can only get two proper PWM signals out at a time. You should digitalWrite() the other pin first, in the unlikely event that you want to PWM different pins at different times in the program. (I've never wanted to do this, nor has anyone else I talked to.) +`TCD0` provides two PWM channels: `WOA` can output on PA4 or PA6, `WOB` on PA5, PA7. Those channels can each drive either - or both - of those pins, but only at one duty cycle. Users may prefer to configure this manually - `TCD0` is capable of, among other things, generating much higher frequency PWM, as it can be clocked from the PLL at 48MHz (or more, if you don't mind exceeding the specified operating ratings - I've gotten it up to 128 MHz, allowing 8-bit pwm at 500 kHz, or a 64 MHz squarewave). It is supposed to be remappable to other sets of pins. This doesn't work in current silicon, but future silicon revs will fix this - in all cases, the core uses channel A for the 2 even numbered pins, and channel B for the two odd numbered pins (if you take it over, the two higher numbered pins can be set to either one, we only support one option through analogWrite(). If you try to turn on third PWM pin (or both even or both odd numbered ones), the other pin controlled by that channel will also change duty cycle - you can only get two proper PWM signals out at a time. You should digitalWrite() the other pin first, in the unlikely event that you want to PWM different pins at different times in the program. (I've never wanted to do this, nor has anyone else I talked to.) ```c++ analogWrite(PIN_PA4,128); // 50% PA4. - like usual analogWrite(PIN_PA7,192); // 50% PA4, 75% PA7 - like usual diff --git a/megaavr/extras/Ref_Timers.md b/megaavr/extras/Ref_Timers.md index 9f344bed..c76c44f9 100644 --- a/megaavr/extras/Ref_Timers.md +++ b/megaavr/extras/Ref_Timers.md @@ -167,7 +167,7 @@ They can be pressed into service as a rather poor PWM timer. TCB in PWM mode is **Errata Alert** - `TCBn.CCMP` is effected by silicon errata on all available silicon save the DD and EA-series: It still acts like a 16-bit register. That means that it uses the TCB.TEMP register for access, and that you must read and write both bytes together, starting with the low byte, then high byte: Writes to the low byte are redirected to the temp register, reading the low byte copies the high byte of the CCMP register to TEMP, and writing to the high byte is what copies the low byte from the TEMP register to the actual CCMP register low byte. However, if you only write the high byte, and write the low byte only once, writes to the high byte alone do work. Until you do something like reading the CNT register, at which point everything will fall over. #### Extra features on 2-series and Dx/Ex-series -The tinyAVR 2-series and Dx parts add three upgrades, two useful, and the other less-so. The less useful one is a separate OVF event/interrupt source. I find this to be of dubious untility - likely the best use of the separate OVF bit is as a 17th bit in input capture mode, but this can generally be done without using it as an interrupt. +The tinyAVR 2-series and Dx parts add three upgrades, two useful, and the other less-so. The less useful one is a separate OVF event/interrupt source. I find this to be of dubious utility - likely the best use of the separate OVF bit is as a 17th bit in input capture mode, but this can generally be done without using it as an interrupt. A much more interesting option is the clock-on-event option: The TCBs now have a second event user, which can be selected as the clock source! Combined with the CCL, this can, for example, be used to get a prescaled system clock into the TCB different from that of a TCA (see the Logic library documentation and examples for discussion of how the CCL filter and synchronizer can be used to generate s prescaled clock). @@ -693,7 +693,7 @@ uint8_t _getCurrentMillisTimer(); * sleepTime library two additional options will be possible: TIMERRTC and TIMERPIT. These are * returned in the unlikely event that this is called when sleeptime has been sleeping with RTC * timekeeping and hasn't switched back yet. - * See Apppendix I. */ + * See Appendix I. */ (macro) MILLIS_TIMER /* This is the value (from appendix I - anything that _gCMT() can return except TIMERRTC and TIMERPIT, but including TIMERRTC_INT, TIMERRTC_XTAL, and TIMERRTC_EXT. These indicate that the RTC is *permanently* the millis source (mTC only). diff --git a/megaavr/extras/TakingOverTCA0.md b/megaavr/extras/TakingOverTCA0.md index 782fba38..51c83442 100644 --- a/megaavr/extras/TakingOverTCA0.md +++ b/megaavr/extras/TakingOverTCA0.md @@ -49,7 +49,7 @@ PORTMUX.CTRLC = PORTMUX_TCA00_ALTERNATE_gc; // Move it to PA7 ``` ## Examples -Now for the fun part - example code! What's so much fun about example code? Because you can steal it and re-use it, of course! +Now for the fun part - example code! What's so much fun about example code? Because you can steal it and reuse it, of course! A note about the pin numbers - we use the PORT_Pxn notation to refer to pins; when I mention in the comments the pin number, that is an Arduino (logical) pin number, not a physical pin number (generally, this documentation does not refer to physical pin numbers except on the pinout charts). Because the mappings of peripherals to pins by the port and pin within the port is constant across the non-8-pin parts, this means the examples (except the one for 8-pin parts) will all work on all 14, 20, and 24-pin parts - and neither the mapping nor timer has changed between the 0-series and 2-series (okay, the timer's EVCTRL register was expanded on the 2-series and it can take a second event now and it can use all event generators because the weird event system of the 0/1-series was replaced with a normal one, but that's way beyond the scope of this document). @@ -166,7 +166,7 @@ void setFrequency(unsigned long freqInHz) { ``` ### Example 3: High speed 8-bit PWM -A megaTinyCore user requested (#152) high speed PWM. They wanted split mode disabled, and PWM frequency higher than 62KHz. This is indeed possible - though do note that the maximum frequency of PWM possible with a full 8 bits of resolution is 78.125 kHz when running at 20 MHz (20000000/256); at 24, it's 93.75 kHz, and overclocked to 32 MHz, 125 kHz. The next higher frequency for which perfect 8-bit resolution is possible is half of those frequencies. Higher fequencies require lower resolution (see above example for one approach, which can also be used for intermediate frequencies) though if the frequency is constant, varying your input between 0 and the period instead of using map() is desirable, as map may not be smooth. As a further aside, if 78.125kHz is suitable, there is no need to disable split mode. It strikes me now, as I adapt this example for the Dx-series parts, that 62 KHz is almost exactly the maximum possible for 8-bit PWM at 16 MHz system clock. I'm pretty sure there's a connection! +A megaTinyCore user requested (#152) high speed PWM. They wanted split mode disabled, and PWM frequency higher than 62KHz. This is indeed possible - though do note that the maximum frequency of PWM possible with a full 8 bits of resolution is 78.125 kHz when running at 20 MHz (20000000/256); at 24, it's 93.75 kHz, and overclocked to 32 MHz, 125 kHz. The next higher frequency for which perfect 8-bit resolution is possible is half of those frequencies. Higher frequencies require lower resolution (see above example for one approach, which can also be used for intermediate frequencies) though if the frequency is constant, varying your input between 0 and the period instead of using map() is desirable, as map may not be smooth. As a further aside, if 78.125kHz is suitable, there is no need to disable split mode. It strikes me now, as I adapt this example for the Dx-series parts, that 62 KHz is almost exactly the maximum possible for 8-bit PWM at 16 MHz system clock. I'm pretty sure there's a connection! Do note that if pushing the PWM frequency is your aim, you can go considerably higher by using the Type D timer - it is rated for a TCD clock of up to 48 MHz.... (and I was able to generate PWM from it without anomalies with it clocked at 128 MHz (32 MHz system clock multiplied by 4) - these parts have a ton of headroom on frequency at room temp and under non-adverse conditions) diff --git a/megaavr/libraries/Comparator/README.md b/megaavr/libraries/Comparator/README.md index 45c55d7f..e5c5c784 100644 --- a/megaavr/libraries/Comparator/README.md +++ b/megaavr/libraries/Comparator/README.md @@ -29,7 +29,7 @@ These options are radically different between the "lesser" 1-series parts - thos There are several things that may be surprising about this peripheral and the wrapper this class provides. ### Regarding input pins -In accordance with the recommendations of Microchip from the datasheet, we disable the digital input for all pins used by the analog comparator through the PINnCTRL register; this is done when `init()` is called. digitalRead() will always return `LOW` on these pins. When the `stop()` method is called, these pins will remain off until manually reconfigured, unless told to restore them via `stop(true)`. You can call `stop(true)` even if the comparator is not currently enabled, but has been initialized. This may be useful if you have been using it with one set of pins but now want to stop using those pins and switch to different pins. (No, no I don't think this is going to be a particularly common use case. What kind of device is this with a pin that that switches between being an analog voltage that has a magic level other than the voltage at which it transitions between high and low, and would ever make sense to hook up to an analog comaparator +In accordance with the recommendations of Microchip from the datasheet, we disable the digital input for all pins used by the analog comparator through the PINnCTRL register; this is done when `init()` is called. digitalRead() will always return `LOW` on these pins. When the `stop()` method is called, these pins will remain off until manually reconfigured, unless told to restore them via `stop(true)`. You can call `stop(true)` even if the comparator is not currently enabled, but has been initialized. This may be useful if you have been using it with one set of pins but now want to stop using those pins and switch to different pins. (No, no I don't think this is going to be a particularly common use case. What kind of device is this with a pin that that switches between being an analog voltage that has a magic level other than the voltage at which it transitions between high and low, and would ever make sense to hook up to an analog comparator When the PINnCTRL register is modified by the class at any point, any other configuration (input level on DB/DD, inversion, and internal pullup) will be returned to the default values. You likely don't want any of those options while using the analog comparator anyway. The pullup will throw off the reading (and is not of a tightly controlled strength, so you can't use it as part of a resistor divider), and as these pins will have analog voltages likely between the input high and low thresholds applied to them, leaving the digital input enabled will increase power consumption. @@ -353,5 +353,5 @@ enterStandbySleep(); // enter standby sleep mode until the comparator interrupt ``` ## *Future development* -*shouldn't LP_MODE/PROFILE and RUNSTBY be properties, and treated like everything else? Why **aren't** they? I would imagine that wanting to wake on the AC int would be one of the most common uses of that interrupt. -They certainly **want** to be properties and it would make the library more coherent. But it would come at a 4-8 bytes of flash (unsure if per comparator or total) and 1 or 2 bytes of ram per comparator, depending on implementation details. Probably wouldn't be popular with people on 212's, but that's a pretty small overhead considering the general level of bloat introduced by classy wrappers around peripherals like Logic, Comparator and Opamp). Maybe a new optional argument to start it in low power, low power - run standby, and run standby (corespondingly more options on DxCore of course). Because how often are you going to be changing the mode once you've turned it on? That's an odd use case (and in any case, the intuitive solution of calling start with a different argument to change it would behave as expected. By passing as the argument the value to be written to the CTRLA register it would have almost no overhead, too. -SK +*shouldn't LP_MODE/PROFILE and RUNSTBY be properties, and treated like everything else? Why **aren't** they? I would imagine that wanting to wake on the AC int would be one of the most common uses of that interrupt.* +*They certainly **want** to be properties and it would make the library more coherent. But it would come at a 4-8 bytes of flash (unsure if per comparator or total) and 1 or 2 bytes of ram per comparator, depending on implementation details. Probably wouldn't be popular with people on 212's, but that's a pretty small overhead considering the general level of bloat introduced by classy wrappers around peripherals like Logic, Comparator and Opamp). Maybe a new optional argument to start it in low power, low power - run standby, and run standby (correspondingly more options on DxCore of course). Because how often are you going to be changing the mode once you've turned it on? That's an odd use case (and in any case, the intuitive solution of calling start with a different argument to change it would behave as expected. By passing as the argument the value to be written to the CTRLA register it would have almost no overhead, too. -SK* diff --git a/megaavr/libraries/Event/src/Event_parts.h b/megaavr/libraries/Event/src/Event_parts.h index 76ea1684..4c2ea1ca 100644 --- a/megaavr/libraries/Event/src/Event_parts.h +++ b/megaavr/libraries/Event/src/Event_parts.h @@ -801,7 +801,7 @@ namespace event { portg_evgen1 = 0x4D, #endif #if defined(PORTH) // At some point they will need to either surrender a non-trivial amount of market share, or offer a migration path from the m2560. - // Becayse the uniform EVSYS channels introduced with the EA leave plenty of holes, it's likely that that part would have it's channels nubered similarly, + // Becayse the uniform EVSYS channels introduced with the EA leave plenty of holes, it's likely that that part would have it's channels numbered similarly, // with it's extra ports porth_evgen0 = 0x4E, porth_evgen1 = 0x4F, diff --git a/megaavr/libraries/Logic/README.md b/megaavr/libraries/Logic/README.md index 8014ddc9..34e269b9 100644 --- a/megaavr/libraries/Logic/README.md +++ b/megaavr/libraries/Logic/README.md @@ -381,7 +381,7 @@ Logic2.clocksource = logic::clocksource::oschf; // Set block 2 to use unprescale ### edgedetect -Property to enable the edge detector. The edge detector can be used to generate a pulse when detecting a rising edge on its input. To detect a falling edge, the TRUTH table should be programmed to provide inverted output. "In order to avoid unpredictable behavior, a valid filter option must be enabled" (note: that's what the datasheet says; it's not clear whether you can get the unpredictable behavior, or if the edge detecter won't be connected unless a filter or synchronizer is enabled). Note that the edge detector is likely only of use when the output is being used for sequential logic or as the input to another logic block; it looks particularly useful on the odd LUT input to a J-K flip-flop sequential logic unit. +Property to enable the edge detector. The edge detector can be used to generate a pulse when detecting a rising edge on its input. To detect a falling edge, the TRUTH table should be programmed to provide inverted output. "In order to avoid unpredictable behavior, a valid filter option must be enabled" (note: that's what the datasheet says; it's not clear whether you can get the unpredictable behavior, or if the edge detector won't be connected unless a filter or synchronizer is enabled). Note that the edge detector is likely only of use when the output is being used for sequential logic or as the input to another logic block; it looks particularly useful on the odd LUT input to a J-K flip-flop sequential logic unit. ```c++ logic::edgedetect::disable; // No edge detection used @@ -607,7 +607,7 @@ Changes can always be freely made to the `Logic` classes - changes aren't writte ### Testing if the enable-lock erratum is present At present, there is never a need to test this, because you know from the part family whether or not it has this erratum - nothing that shipped with this broken has gotten a die rev that fixed it, so it impacts all tinyAVR, mega0, DA, and DB. However DxCore provides #defines for all Arduino-relevant errata, and this errata can be tested like this; note that **this is not a macro and cannot be made a macro. The die rev is not compile time known!** How could it be? The compiler doesn't know what you're going to do with the hex file. ```c -if (checkErrata(ERRATA_CCL_PROTECTION)) { /*true if errata presnt */ +if (checkErrata(ERRATA_CCL_PROTECTION)) { /*true if errata present */ Logic::stop(); Logic1.init(); Logic::start(); diff --git a/megaavr/libraries/Wire/src/Wire.cpp b/megaavr/libraries/Wire/src/Wire.cpp index bc0ce527..82615bc4 100644 --- a/megaavr/libraries/Wire/src/Wire.cpp +++ b/megaavr/libraries/Wire/src/Wire.cpp @@ -402,7 +402,7 @@ uint8_t TwoWire::specialConfig( __attribute__ ((unused)) bool smbuslvl, __attrib else { //not compile time constant so have to check at runtime if (sda_hold_dual) { // only have to do SDA hold 0 - already caught bad input levels. ret |= 2; //error code. - //sda_hold_dual= 0; not needed to zero out if no dual mode, we don't use this value anmore. + //sda_hold_dual= 0; not needed to zero out if no dual mode, we don't use this value anymore. } } #else diff --git a/megaavr/libraries/megaTinyCore/examples/ClockDiagnose/ClockDiagnose.ino b/megaavr/libraries/megaTinyCore/examples/ClockDiagnose/ClockDiagnose.ino index 990c8e7f..578cd296 100644 --- a/megaavr/libraries/megaTinyCore/examples/ClockDiagnose/ClockDiagnose.ino +++ b/megaavr/libraries/megaTinyCore/examples/ClockDiagnose/ClockDiagnose.ino @@ -129,7 +129,7 @@ void setup() { Serial.println("The OSCCFG fuse is set to 16 MHz however a tuned speed has been requested that can only be reached with the 20 MHz oscillator."); } if (errortype & 8) { - Serial.println("Core configured to use the internal oscillator. But somehow we got here with an external clock selected.\r\nI have not the faintest idea how you mananged that unless you modified the core (this sketch does not support modified versions of the core)"); + Serial.println("Core configured to use the internal oscillator. But somehow we got here with an external clock selected.\r\nI have not the faintest idea how you managed that unless you modified the core (this sketch does not support modified versions of the core)"); } if (errortype & 64) { Serial.println("Tuned internal selected, but tuning sketch has not run successfully.If you can read this, but got a wad of gibberish before, "); diff --git a/megaavr/libraries/megaTinyCore/examples/TCA0Demo3/TCA0Demo3.ino b/megaavr/libraries/megaTinyCore/examples/TCA0Demo3/TCA0Demo3.ino index 283512e3..22d2b0ae 100644 --- a/megaavr/libraries/megaTinyCore/examples/TCA0Demo3/TCA0Demo3.ino +++ b/megaavr/libraries/megaTinyCore/examples/TCA0Demo3/TCA0Demo3.ino @@ -4,7 +4,7 @@ * disabled, and PWM frequency higher than 62KHz. This is indeed possible - though do note * that the maximum frequency of PWM possible with a full 8 bits of resolution and 20MHz * system clock is 78.125 kHz (20000000/256) - and the next higher frequency for which - * perfect 8-bit resolution is possible is half that, 39.061 kHz. Higher fequencies require + * perfect 8-bit resolution is possible is half that, 39.061 kHz. Higher frequencies require * lower resolution (see above example for one approach, which can also be used for * intermediate frequencies) though if the frequency is constant, varying your input between * 0 and the period instead of using map() is desirable, as map may not be smooth. As a further diff --git a/megaavr/libraries/megaTinyCore/examples/TuningSource/TuningSource.ino b/megaavr/libraries/megaTinyCore/examples/TuningSource/TuningSource.ino index 99fddb98..dbcabb5b 100644 --- a/megaavr/libraries/megaTinyCore/examples/TuningSource/TuningSource.ino +++ b/megaavr/libraries/megaTinyCore/examples/TuningSource/TuningSource.ino @@ -21,7 +21,7 @@ #if (_AVR_PINCOUNT > 14) // Except under tight constraints of 14-pin non-tinies, we can always count on their being a PA2. #define TUNE_OUTPUT_PIN PIN_PA2 - #elif defined(PIN_PC2) /*AVR DD-series has no PC0, PA2-7, and the only pins nubered 2 or lower in any port + #elif defined(PIN_PC2) /*AVR DD-series has no PC0, PA2-7, and the only pins numbered 2 or lower in any port * are thus PA0, PA1, and PC1 and PC2. The pins on PORTA are needed for the crystal if this is to be used * and you actually want to tune to within an accuracy measured in (tens of) "ppm" instead of (several) "%" on datasheet specs. * So we're using either PC1 or PC2!