From c8a87faf1fe473d81680c0bb2e0d08c1377c1212 Mon Sep 17 00:00:00 2001 From: Spence Konde Date: Wed, 21 Jun 2023 00:04:10 -0400 Subject: [PATCH] Mostly minor --- ChangeLog.md | 8 ++ README.md | 19 +-- megaavr/bootloaders/optiboot_x/optiboot_x.c | 2 +- megaavr/cores/megatinycore/HardwareSerial.h | 114 +++++++++++------- megaavr/cores/megatinycore/UART.cpp | 2 +- megaavr/extras/Ref_Digital.md | 50 +++++--- .../examples/ErsatzReset/ErsatzReset.ino | 2 +- 7 files changed, 120 insertions(+), 77 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 011d8729..6be01e1a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,9 +17,17 @@ These items are in addition to what was listed under changes already in release. * Port any applicable enhancements made to DxCore to megaTinyCore, should such happen be made. * If there are *other substantial changes that need to occur* within the core, I am unaware of the complaints and hence have no plans to address them before the heat death of the universe. If you desire changes on a more rapid timeline, please create an issue so that I am aware of the presence of said problem, deficiency, or imperfection. Those form the action item list for core development activity, so if something is not listed there, **it is unlikely to be implemented/fixed/etc** simply due to my being unaware of any concern. + ## Unreleased changes Changes listed here are checked in to GitHub ("master" branch unless specifically noted; this is only done when a change involves a large amount of work and breaks the core in the interim, or where the change is considered very high risk, and needs testing by others prior to merging the changes with master - everything else goes straight into master). These changes are not yet in any "release" nor can they be installed through board manager, only downloading latest code from github will work. These changes will be included in the listed version, though planned version numbers may change without notice - critical fixes may be inserted before a planned release and the planned release bumped up a version, or versions may go from patch to minor version depending on the scale of changes +### Planned 2.6.9 or 2.7.0 +* Bugfix: optiboot_x.c that has been in use since newyears day 2023 was never committed. +* Bugfix: Remove boot_opt.h which is not applicable to modern AVRs. +* Bugfix: Remove the useless dummy app that forced us to use avr-size -A to see the size of the bootloader separated from the app, and switch avr-size -A to normal avr-size to take advantage of this. +* Rebuild all bootloader hex files Size appears unchanged. We need to get some eyes on this prior to release to make sure it works. This is simple stuff to do -it doesn't need to be done by me, so if some others could test the ones now checked into github that would be awesome + + ## Released Versions ### 2.6.8 * CRITICAL bugfix: Fix issues introduced by pwm option menu. This prevented compilation on 1-series or Microchip boards. There were at least *4 separate issues* feeding into this. diff --git a/README.md b/README.md index 6212e3c1..c8bf0491 100644 --- a/README.md +++ b/README.md @@ -61,19 +61,12 @@ Read the [**SerialUPDI documentation**](https://github.com/SpenceKonde/AVR-Guida As of 2.3.2, with the dramatic improvements in performance, and the proven reliability of the wiring scheme using a diode instead of a resistor, and in light of the flakiness of the jtag2updi firmware, this is now the recommended programming method. As of this version, programming speed has been increased by as much as a factor of 20, and now far exceeds what was possible with jtag2updi (programming via jtag2updi is roughly comparable in speed to programming via SerialUPDI on the "SLOW" speed option, 57600 baud; the normal 230400 baud version programs about three times faster than the SLOW version or jtag2updi, while the "TURBO" option (runs at 460800 baud and increases upload speed by approximately 50% over the normal one. The TURBO speed version should only be used with devices running at 4.5v or more, as we have to run the UPDI clock faster to keep up (it is also not expected to be compatible with all serial adapters - this is an intentional tradeoff for improved performance), but it allows for upload and verification of a 32kB sketch in 4 seconds. -#### Coming before year end 2022: HV programming tool -An HV programming tool to be called HyperUPDI is expected to be available (though silicon shortages may limit quantities) by year end. It is *not* intended to replace SerialUPDI. -* HV programming support for AVR-DD, AVR-EA, and tinyAVR parts, allowing the UPDI pin to be used as GPIO without precluding further programming. -* An on-board buffer will allow data to be sent in chunks of 2k or more. The result of this will be a dramatic improvement in programming speed. I expect an improvement of perhaps 5-10% on Dx-series vs SerialUPDI (as it is already very close to the theoretical maximum) - but the benefits on tinyAVR c will be considerably greater, as at TURBO speed they spent half their time in a USB latency period/ -* When a normal serial console is used to access it, it will operate in passthrough mode, featuring the classic FTDI pinout. -* When in programming mode, the nominal CTS line is used to output the UPDI signal. Many boards (including those I sell) now have a solder-jumper to connect CTS to UPDI. This will allow a you to upload via UPDI and then open the serial console without changing any connections nor the use of a bootloader! -* It will utilize a new upload script (neither Prog.py nor avrdude) which leverages the python installation we bring in for SerialUPDI already. -* Because of the built in awareness of the UPDI protocol and the NVMCTRL of supported parts other features like partial erase (to supplement Flash.h on the Dx-series). -* Voltage options of 5V, 3.3V, and Vtarget will be selectable with a slide switch, allowing programming where the target voltage is as low as 1.8V, programming devices running directly off LiPo batteries at 3.7-4.2v and so on. -* Due to the considerably more complex hardware, HyperUPDI will obviously not be a $1 device like SerialUPDI (which I expect most people will continue to use) - -#### Realistically coming before year end 2022: Superior serial adapters -A single-port serial adapter with a switch that toggles between UPDI and normal mode and another switch for 5v and 3.3v, and which exposes all modem liaison pins, with an optional stackable rider board that uses the more durable JST-XH connectors to help deal with the incredibly short lifespan of a dupont cable when beingfrequently plugged in and unplugged, particularly if you don't always connect them with perfect precision. This is a feature I added primarily with myself in mind, but if there is demand, I can totally get bulk JST-XH <-> Dupont adapter cables made - but is this worthwhile? That's a good question, because the whole reason I made the rider board was that dupont connectors have a short operating life - so you'd still need to be able to replace the dupont terminals your self or buy new adapter cables from me (note: the 3-pin cable suffers this issue far more often than the 6-pin ones. One might even consider putting a JST-XH header on the target board for this reason. The rider board will also feature 2 RGB leds facing sideways (so you can see them in almost any orientation) that can be enabled to show the status of modem liaison lights (this is particularly helpful for debugging). +#### Hopefully coming 2023 early Q3: Superior serial adapters +Three designs are being iterated: A dual port serial adapter where both are serial ports, a dual port serial adapter where one port is always UPDI, and and a single port one witch a switch to select the mode, and an optional addon board to give leds indicating status of modem control lines. + +These will allow use of either a SMT JST-XH connector or dupont connector - either way with 6 pins for serial (FTDI pinout as marked) and 3 pins (for UPDI). + +All three of these will be able to supply 3.3 or Vusb (nom. 5V), or disconnect both Vusb and 3V3 from the power, and expect that the target device is powered with 5.5V > Vdd > 1.8V. The logic levels used in this case will be the voltage of whatever is applied. Be warned that on dual serial devices, the VccIO power rail is shared! They must both be running at the same voltage, be the same device, or the adapter must be set to supply them and their power disconnected. #### (New in 2.5.6) What's With All The Different SerialUPDI Options? Depending on adapter model, and operating system, it has been found that different timing settings are required; however, settings needed to keep even 230400 baud from failing on Linux/Mac with most adapters impose a much larger time penalty on Windows, where the OS's serial handling is slow enough that nothing needs that delay... diff --git a/megaavr/bootloaders/optiboot_x/optiboot_x.c b/megaavr/bootloaders/optiboot_x/optiboot_x.c index 29c1368c..9a2d1476 100644 --- a/megaavr/bootloaders/optiboot_x/optiboot_x.c +++ b/megaavr/bootloaders/optiboot_x/optiboot_x.c @@ -374,7 +374,7 @@ int main(void) { // That means for overhead penalty of between 6 and 34 bytes added to app binary size, which is usable // for other code, you would be able to.... ... enter the bootloader less robustly, and save 10 bytes // in the bootloader, where you can't use it. - // I do belive the phrase "strictly worse" describes this. + // I do believe the phrase "strictly worse" describes this. __asm__ __volatile__("clr __zero_reg__"); // known-zero required by avr-libc ch = RSTCTRL.RSTFR; // get reset cause diff --git a/megaavr/cores/megatinycore/HardwareSerial.h b/megaavr/cores/megatinycore/HardwareSerial.h index cd882369..ab6beedd 100644 --- a/megaavr/cores/megatinycore/HardwareSerial.h +++ b/megaavr/cores/megatinycore/HardwareSerial.h @@ -68,55 +68,38 @@ * Since the USE_ASM_* = 1 option is apparently working, we do not recommend disabling it, as it will waste flash and hurt performance. * * Flash versus RAM table - * | | modern tinyAVR series parts | Other modern parts | - * | Flash | 0-series | 1-series | 2-series | mega | All Dx | EA | - * |-------|----------|----------|----------|------|--------|------| - * | 2048 | 128 | 128 | - | - | - | - | - * | 4096 | 256 | 256 | 512 | - | - | - | - * | 8192 | 512 | 512 | 1024 | 1024 | - | 1024 | - * | 16384 | 1024 | 2048 | 2048 | 2048 | 2048 | 2048 | - * | 32768 | - | 2048 | 3072 | 4096 | 4096 | 4096 | - * | 49152 | - | - | - | 6120 | - | - | - * | 65536 | - | - | - | - | 8192 | 6120 | - * | 128k | - | - | - | - | 16384 | - | - * This ratio is remarkably consistent. No AVR part was ever made with - * less than 8:1 flash:ram, nor more than 16:1, since first ATmegas! - * The sole exception? The ATmega2560/2561 has only 8k RAM, a 32:1 flash to ram ratio. - * (to be fair, you are allowed to use external RAM - which was a very rare feature indeed, + * | | modern tinyAVR series parts | Other modern parts | + * | Flash | 0-series | 1-series | 2-series | mega | All Dx | EA | EB | + * |-------|----------|----------|----------|------|--------|------|------| + * | 2048 | 128 | 128 | - | - | - | - | - | + * | 4096 | 256 | 256 | 512 | - | - | - | - | + * | 8192 | 512 | 512 | 1024 | 1024 | - | 1024 | 1024 | + * | 16384 | 1024 | 2048 | 2048 | 2048 | 2048 | 2048 | 2048 | + * | 32768 | - | 2048 | 3072 | 4096 | 4096 | 4096 | 3072 | + * | 49152 | - | - | - | 6120 | - | - | - | + * | 65536 | - | - | - | - | 8192 | 6120 | - | + * | 128k | - | - | - | - | 16384 | - | - | + * This ratio is remarkably consistent. No AVR part was ever made with less than 8:1 flash:ram, + * nor more than 16:1, since the earliest recognizable AVRs. I am only aware of one exception. Was it some bizarro part + * from the dark ages? Nope - it's the surprisingly popular ATmega2560! + * The ATmega2560/2561 has only 8k RAM, a 32:1 flash to ram ratio. (to be fair, you are allowed to use external RAM + * on those, which was a very rare feature indeed, and that is by far the most widespread part with such a feature - though if you're using the + * XMEM interface, you've burned 19 GPIO lines right there.... The ATmega2560 is kind of the "I have a job too big for an AVR. + * But I don't know how to program anything else!" part. That is not a compliment. + * + * | RAM | TX | RX | Amount of RAM implied | Total ram used | + * |-------|----|----|-----------------------|----------------| + * | < 512 | 16 | 16 | 256b (0/1 w/2k or 4k) | 32b, all 1 port| + * | <1024 | 16 | 32 | 512b | 48b or 96b | + * | <2048 | 32 | 64 | 1024b | 96b or 192b | + * | More | 64 | 64 | 2048b or 3072b | 128b or 256b | + * + * (the two numbers in final column are given because 0/1-serieas has 1 port, but tiny2 has 2, though if you only use one, you only + * get one set of buffers) */ -#if !defined(LTODISABLED) -#if !defined(USE_ASM_TXC) - #define USE_ASM_TXC 2 // A bit slower than 1 in exchange for halfduplex. -//#define USE_ASM_TXC 1 // This *appears* to work? It's the easy one. saves 6b for 1 USART and 44b for each additional one -#endif - -#if !defined(USE_ASM_RXC) - #define USE_ASM_RXC 1 // This now works. Saves only 4b for 1 usart but 98 for each additional one -#endif -#if !defined(USE_ASM_DRE) - #define USE_ASM_DRE 1 // This is the hard one...Depends on BOTH buffers, and has that other method of calling it. saves 34b for 1 USART and 68b for each additional one -#endif -#else - #warning "LTO has been disabled! ASM TXC/RXC/DRE not available. USART falling back to the old, flash-inefficient implementation with fewer features." - #if defined(USE_ASM_TXC) - #undef USE_ASM_TXC - #endif +/* Buffer Sizing */ - #if defined(USE_ASM_RXC) - #undef USE_ASM_RXC - #endif - - #if defined(USE_ASM_DRE) - #undef USE_ASM_DRE - #endif -#endif - - -// savings: -// 44 total for 0/1, -// 301 for 2-series, which may be nearly 9% of the total flash! -// The USE_ASM_* options can be disabled by defining them as 0 (in the same way that buffer sizes can be overridden) // The buffer sizes can be overridden in by defining SERIAL_TX_BUFFER either in variant file (as defines in pins_arduino.h) or boards.txt (By passing them as extra flags). // note that buffer sizes must be powers of 2 only. @@ -169,6 +152,45 @@ #error "ERROR: RX buffer size must be a power of two." #endif +/* Buffer sizing done */ + + +#if !defined(LTODISABLED) +#if !defined(USE_ASM_TXC) + #define USE_ASM_TXC 2 // A bit slower than 1 in exchange for halfduplex. +//#define USE_ASM_TXC 1 // This *appears* to work? It's the easy one. saves 6b for 1 USART and 44b for each additional one +#endif + +#if !defined(USE_ASM_RXC) + #define USE_ASM_RXC 1 // This now works. Saves only 4b for 1 usart but 98 for each additional one +#endif + +#if !defined(USE_ASM_DRE) + #define USE_ASM_DRE 1 // This is the hard one...Depends on BOTH buffers, and has that other method of calling it. saves 34b for 1 USART and 68b for each additional one +#endif +#else + #warning "LTO has been disabled! ASM TXC/RXC/DRE not available. USART falling back to the old, flash-inefficient implementation with fewer features." + #if defined(USE_ASM_TXC) + #undef USE_ASM_TXC + #endif + + #if defined(USE_ASM_RXC) + #undef USE_ASM_RXC + #endif + + #if defined(USE_ASM_DRE) + #undef USE_ASM_DRE + #endif +#endif + + +// savings: +// 44 total for 0/1, +// 301 for 2-series, which may be nearly 9% of the total flash! +// The USE_ASM_* options can be disabled by defining them as 0 (in the same way that buffer sizes can be overridden) +// The buffer sizes can be overridden in by defining SERIAL_TX_BUFFER either in variant file (as defines in pins_arduino.h) or boards.txt (By passing them as extra flags). +// note that buffer sizes must be powers of 2 only. + #if USE_ASM_RXC == 1 && !(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) #error "Assembly RX Complete (RXC) ISR is only supported when RX buffer size are 256, 128, 64, 32 or 16 bytes" #endif diff --git a/megaavr/cores/megatinycore/UART.cpp b/megaavr/cores/megatinycore/UART.cpp index 84b69ee9..6ee4d48b 100644 --- a/megaavr/cores/megatinycore/UART.cpp +++ b/megaavr/cores/megatinycore/UART.cpp @@ -146,7 +146,7 @@ "out 0x3f, r24" "\n\t" // restore it "pop r24" "\n\t" // pop r24 to get it's old value back "pop r31" "\n\t" // and r31 - "pop r30" "\n\t" // Pop the register the ISR did + "pop r30" "\n\t" // Pop the register the ISR pushed "reti" "\n" // return from the interrupt. ::); __builtin_unreachable(); diff --git a/megaavr/extras/Ref_Digital.md b/megaavr/extras/Ref_Digital.md index fd93725e..0563a4d3 100644 --- a/megaavr/extras/Ref_Digital.md +++ b/megaavr/extras/Ref_Digital.md @@ -1,8 +1,28 @@ # Improved Digital I/O Functionality This core includes a number of features to provide more control or performance when doing digital I/O. This page describes how to use them. All of the options that can be configured for a pin are exposed. The only things that aren't exposed are the slew rate limiting feature, and the multi-pin configuration facilities. The slew rate limiting is can only be configured on a per port basis; turning it on and off is so simple (see below) that it needs no wrapper. The multi-pin configuration system does not have an obvious "right way" to expose it and should be handled directly by the sketch - it is very flexible and no wrapper around it would be able to preserve it's virtues while being much of a wrapper. +## But first, about the hardware +As there is a good chance you;ve noticed, a lot of hardware is better at driving pins low than high (sinking vs sourcing). Classic AVRs had symmetric drive - they were very similar in their ability to source and sink current. The tinyAVRs were... almost symmetric. The Dx and Ex, are far from, it. + +The modern AVR's aren't quite as symmetric. *this may impede techniques like the R-C filter on a pin to generate analog voltages if R is low and C large)* (as long as the current through the pin is low you don't notice). Let's compare the modern AVRs real quick here: + +| Part Family | tiny0/1 | tiny2 | Dx | Ex | tiny reset pin when used for UPDI or GPIO | +|------------------|---------|-------|----|----|-------------------------------------------| +| Abs max current | +/-40mA |+/-40mA|+/-50|+/-40 | - | +| Abs max injection| +/-15mA |+/-15mA|+/-20|+/-20 | - | +| .. @ Vdd > 4.9|+/- 1 mA |+/-1 mA|- | - | - | +| IOH,max from graph| | 20 mA |50mA|tbd | No graph, spec is same drop as normal I/O | +| VOH,min from graph| | 4.3V |3.5V|tbd | handling 30x the current. Even at 5V, 2k | +| IOL,max from graph| | 20 mA |100mA|tbd| output impedance. | +| VOH,max from graph| | 0.43 |1.4V|tbd | - | + +You might be scurrying to your datasheets thinking "I swear they were symmetric on the tinys!" and if you look at the table - the worst case specs - they are. They give themselves equal room on both sides. But the graphs show a voltage drop when sourcing 20mA on a tiny2 of avg 0.7V at room temp, but a voltage drop of 0.4-0.45 on at sinking the same amount of current! And what's with 100mA on Dx? abs max is 50! Good question. Personally I appreciate the transparency on behavior in overload conditions. + +What tinyAVR calls current injection, Dx/Ex-series call "Clamp current" - it's current through the protection diodes. Oddly It wasn't spec'ed on classic AVRs (but was well known as the usual cause of "blown" pins). Atmel employees are rumored to have said +/- 1mA maximum. Certainly we have made some progress on that front. + + ## Ballpark overhead figures -The digital I/O functions are astonishingly inefficient. This isn't my fault - it's the Arduino API's fault +The digital I/O functions are astonishingly inefficient. This isn't my fault (not on mTC - on DxC I have definitely not helped...) - it's the Arduino API's fault These figures are the difference between a sketch containing a single call to the function, with volatile variables used as arguments to prevent compiler from making assumptions about their values, which may substantially reduce the size of the binary otherwise. The comparison to the fast I/O functions is grossly unfair, because the fast I/O functions have constant arguments - but this gives an idea of the scale. @@ -39,14 +59,14 @@ About 200 bytes of that space is used for lookup tables, not instructions. This is why the fast digital I/O functions exist. -This is why the fast digital I/O functions exist, and why there are people who habitually do `VPORTA.OUT |= 0x80;` instead of `digitalWrite(PIN_PA7,HIGH);` +This is why the fast digital I/O functions exist, and why there are people who habitually do `VPORTA.OUT |= 0x80;` instead of `digitalWrite(PIN_PA7, HIGH);` It's also why I am not comfortable automatically switching digital I/O to "fast" type when constant arguments are given. The normal functions are just SO SLOW that you're sure to break code that hadn't realized they were depending on the time it took for digital write, even if just for setup or hold time for the thing it's talking to. ## openDrain() -It has always been possible to get the benefit of an open drain configuration - you set a pin's output value to 0 and toggle it between input and output. This core provides a slightly smoother (also faster) wrapper around this than using pinmode (since pinMode must also concern itself with configuring the pullup, whether it needs to be changed or not, every time - it's actually significantly slower setting things input vs output. The openDrain() function takes a pin and a value - `LOW`, `FLOATING` (or `HIGH`) or `CHANGE`. `openDrain()` always makes sure the output buffer is not set to drive the pin high; often the other end of a pin you're using in open drain mode may be connected to something running from a lower supply voltage, where setting it OUTPUT with the pin set high could damage the other device. +It has always been possible to get the benefit of an open drain configuration - you set a pin's output value to 0 and toggle it between input and output. This core provides a slightly smoother (also faster) wrapper around this than using pinmode (since pinMode must also concern itself with configuring the pullup, whether it needs to be changed or not, every time - it's actually significantly slower setting things input vs output. The openDrain() function takes a pin and a value - `LOW`, `FLOATING` (or `HIGH`), or `CHANGE`. `openDrain()` always makes sure the output buffer is not set to drive the pin high; often the other end of a pin you're using in open drain mode may be connected to something running from a lower supply voltage, where setting it OUTPUT with the pin set high could damage the other device. -Use pinMode() to set the pin `INPUT_PULLUP` before you start using `openDrain()`, or use `pinConfigure()` if you need the pullup enabled too; this doesn't change it. Note that there is nothing to stop you from doing `pinMode(pin,INPUT_PULLUP); openDrain(pin,LOW);` That is non-destructive (no ratings are exceeded), and would be a good place to start for a bitbanged open drain protocol, should you ever create one - but it doesn't exactly help with power consumption if you leave it like that! If running on batteries, be sure to turn off one of the two, either by using openDrain to release it to to pullups, or digitalWrite'ing it `HIGH`, or any other method of configuring pins that gets you a pin held in a defined state where it isn't working against it's own pullup. +Use pinMode() to set the pin `INPUT_PULLUP` before you start using `openDrain()`, or use `pinConfigure()` if you need the pullup enabled too; this doesn't change it. Note that there is nothing to stop you from doing `pinMode(pin,INPUT_PULLUP); openDrain(pin, LOW);` That is non-destructive (no ratings are exceeded), and would be a good place to start for a bitbanged open drain protocol, should you ever create one - but it doesn't exactly help with power consumption if you leave it like that! If running on batteries, be sure to turn off one of the two, either by using openDrain to release it to to pullups, or digitalWrite'ing it `HIGH`, or any other method of configuring pins that gets you a pin held in a defined state where it isn't working against it's own pullup. ```c++ openDrain(PIN_PA1, LOW); // sets pin OUTPUT, LOW. @@ -77,7 +97,7 @@ VPORTD.OUT |= 1 << 0; // The previous line is syntactic sugar for this. Beyond b |---------------------|-----------|----------|-----------------| | openDrainFast() | 14 words | 7 words | 2 words if LOW
1 if FLOATING | | digitalWriteFast() | 10 words | 6 words | 1 words | -| pinModeFast() | N/A | N/A | 1 word if OUTPUT
6 otherwise | +| pinModeFast() | N/A | N/A | 6 words - too easy to accidentally leave pullups on and drive pin low. | Execution time is 1 or sometimes 2 clocks per word that is actually executed (not all of them are in the multiple possibility options. in the case of the "any option" digitalWrite, it's 5-7 Note that the HIGH/LOW numbers include the overhead of a `(val ? HIGH : LOW)` which is required in order to get that result. That is how the numbers were generated - you can use a variable of volatile uint8_t and that will prevent the compiler from assuming anything about it's value. It is worth noting that when both pin and value are constant, this is 2-3 times faster and uses less flash than *even the call to the normal digital IO function*, much less the time it takes for the function itself (which is many times that. The worst offender is the normal digitalWrite(), because it also has to check for PWM functionality and then turn it off if enabled (and the compiler isn't allowed to skip this if you never use PWM). @@ -138,7 +158,7 @@ While pinConfigure is not as fast as the fast digital I/O functions above, it's Again, note that unlike digitalWrite() this does not turn off PWM. ## Slew Rate Limiting (Dx-series and 2-series only) -All of the Dx-series parts have the option to limit the [slew rate](https://en.wikipedia.org/wiki/Slew_rate) for the OUTPUT pins on a on a per-port basis. This is typically done because fast-switching digital pins contain high-frequency components (in this sense, high frequency doesn't necessarily mean that it repeats, only that if it continued, it would be high frequency; this way of thinking is useful in electrical engineering), which can contribute to EMI, as well as ringing (if you're ever looked on a scope and noticed that after a transition, the voltage briefly oscillates around the final voltage - that's ringing) which may confuse downstream devices (not usually, at least in arduino land, though). Often, you will not know exactly *why* it's an issue, either, rather a datasheet may specify a maximum slew rate on it's inputs. +All of the Dx-series parts have the option to limit the [slew rate](https://en.wikipedia.org/wiki/Slew_rate) for the OUTPUT pins on a on a per-port basis. This is typically done because fast-switching digital pins contain high-frequency components (in this sense, high frequency doesn't necessarily mean that it repeats, only that if it continued, it would be high frequency; this way of thinking is useful in electrical engineering), which can contribute to EMI, as well as ringing (if you're ever looked on a scope and noticed that after a transition, the voltage briefly oscillates around the final voltage - that's ringing) which may confuse downstream devices (not usually, at least in arduino land, though). Often, you will not know exactly *why* it's an issue or what goes wrong, you just see a maximum slew rate spec. If you're productizing something and it has to pass FCC`**` testing sometimes limiting the slew rate can reduce EMI. ```c // These work but are slow and inefficient. // Enable slew rate limiting on PORTA @@ -185,9 +205,9 @@ This used to be a function only used within wiring_digital. It is now exposed to ## Finding current PWM timer, if any: digitalPinToTimerNow() (DxCore only) On many cores, there is a `digitalPinToTimer(pin)` macro, mostly for internal use, which returns the timer associated with the pin. The Arduino API implicitly assumes that this is constant, and both core libraries and code in the wild often assume that it is. The digitalPinToTimer() implementation is as a macro, which frequently is optimized to a constant (provided the argument is constant, of course). The assumption that there exists not-more-than-1-to-1 timer:pin mapping was never universally valid even among AVRs (there are examples of classic AVRs that have multiple timers usable on some pins, and some even permit that to be remapped easily at runtime (the classic ATtiny841 being a notable example). All modern AVRs have at least two pins available to each timer output compare channel; and while the tinyAVRs never have more than two timers potentially usable with a given pin, the Dx-series allows one of up to 7 pins to be associated with a given timer (in groups), with some pins having as many as 4 options. -For the modern tinyAVRs, during development of megaTinyCore the design decision was made to not support PWM output via analogWrite() for any timer *type* if that *type* would add not more than 1 additional PWM pin when the type A timer was used in split mode (which has up to 6 channels) in order to make efficient use of limited flash. This leaves out the type D timer on 8-pin and 14-pin 1-series parts, and the type B timer(s) on all parts. Either of these would nearly double the flash required for PWM (there are plans to provide more than one option for PWM layouts in a future version of megaTinyCore via a menu option, fixed at compile time, which would provide a means for their capabilities to be more readily used without imposing unwanted overhead when not needed). In any event, on megaTinyCore, `digitalPinToTimer()` is simply an alias of `digitalPinToTimer()` and this will remain the case. +For the modern tinyAVRs, during development of megaTinyCore the design decision was made to not support PWM output via analogWrite() for any timer *type* if that *type* would add not more than 1 additional PWM pin when the type A timer was used in split mode (which has up to 6 channels) in order to make efficient use of limited flash. This leaves out the type D timer on 8-pin and 14-pin 1-series parts, and the type B timer(s) on all parts. Either of these would nearly double the flash required for PWM - however, as of recent versions you can choose between a numbert of PWM pin layounts. In any event, on megaTinyCore, `digitalPinToTimerNow()` is simply an alias of `digitalPinToTimer()` and this will remain the case, nor are there plans to add support for TCB PWM. -On the Dx-series parts, timers, pins, multiplexing options, and clock cycles are all more abundant, as are other peripherals competing for any given pin meanwhile, counterintuitively, the multiplexing scheme is actually simpler, and that inflexibility was not appropriate. As of 1.3.x, `analogWrite()` works for either type A timer *provided* that the appropriate `PORTMUX` register is set first. As of 1.5.x this is extended to the type D timer where hardware allows (at time of writing, only the AVR DD-series; there is a silicon bug impacting all production DA/DB-series parts. It is expected that future silicon revisions will correct this on those parts, if those are ever made available; having waited more than 2 years hoping for such fixes, and 5 years for other corrections for the tinyAVRs, I am not optimistic). On DxCore there is both `digitalPinToTimer()` and `digitalPinToTimerNow()`. The former never returns TIMERAn, but will return a constant referring to a type D timer *channel* or a type B *timer*. The latter tests the relevant `PORTMUX` register if that pin can use a type A timer; If it cannot, it then checks the standard `digitalPinToTimer()`. If that indicates that a type D timer channel can be used with the pin, it then performs a bitwise AND with the type D timer mux register, which will be true if the TCD is pointed at that pin. If that gives us a type B timer, that timer is returned as the result (notice that pins that can use either the type D timer or a type B timer will never return the type B timer, even if the type D timer is pointed elsewhere - this was a necessary compromise for flash and code complexity considerations. +On the Dx-series parts, timers, pins, multiplexing options, and clock cycles are all more abundant, as are other peripherals competing for any given pin meanwhile, counterintuitively, the multiplexing scheme is actually simpler, and that inflexibility was not appropriate. As of 1.3.x, `analogWrite()` works for either type A timer **even if you change where that timer is pointed** *provided* that the appropriate `PORTMUX` register is set first. As of 1.5.x this is extended to the type D timer where hardware allows (at time of writing, only the AVR DD-series; there is a silicon bug impacting all production DA/DB-series parts. It is expected that future silicon revisions will correct this on those parts, if those are ever made available; having waited more than 2 years hoping for such fixes, and 5 years for other corrections for the tinyAVRs, I am not optimistic). On DxCore there is both `digitalPinToTimer()` and `digitalPinToTimerNow()`. The former never returns TIMERAn, but will return a constant referring to a type D timer *channel* or a type B *timer*. The latter tests the relevant `PORTMUX` register if that pin can use a type A timer; If it cannot, it then checks the standard `digitalPinToTimer()`. If that indicates that a type D timer channel can be used with the pin, it then performs a bitwise AND with the type D timer mux register, which will be true if the TCD is pointed at that pin. If that gives us a type B timer, that timer is returned as the result (notice that pins that can use either the type D timer or a type B timer will never return the type B timer, even if the type D timer is pointed elsewhere - this was a necessary compromise for flash and code complexity considerations. **In no case will either of these macros ever return a timer which the core does not permit use of analogWrite() with.** @@ -195,17 +215,17 @@ Furthermore, the following situations should not be expected to produce the desi * Calling turnOffPWM, digitalPinToTimer, digitalPinToTimerNow or analogWrite on a pin nominally driven by a timer which the core has been instructed not to reconfigure using the takeOverTCxn() function. The core will treat the pin as an ordinary digital pin - by calling that, you assume full responsibility for all management of that timer. That function must be called if any manual configuration of a PWM timer will be or has been performed, except as specifically noted in in the timer and PWM reference or type D timer reference. * Calling turnOffPWM, digitalPinToTimer, digitalPinToTimerNow, analogWrite or digitalWrite on a pin nominally driven by a type A timer which has been manually reconfigured, particularly if it has been configured in*single mode* for 3x16-bit PWM channels. The Arduino API does not support PWM with > 8 bits of resolution. Users who require that level of control must call takeOverTCxn(). See the timer and PWM reference. * Calling turnOffPWM, digitalPinToTimer, digitalPinToTimerNow, analogWrite or digitalWrite on a pin nominally driven by a type B timer, but which has been configured to use an output pin other than the one shown on the DxCore pin mapping for that part - this will reconfigure PWM on the wrong pin if at all. -* Manually configuring any timer without calling takeOverTCxn. +* Manually configuring any timer without calling takeOverTCxn, except as explicitly documented in the core documentation (be sure you are reading the correct core's documentation - usually mTC = DxC, but this is one case where that assumption is *not* valid. DxC permits more manipulation of TCD0 in particular. ## Note on number of pins and future parts -The most any announced AVR has had is 86 digital pins, the ATmega2560; In the modern AVR era, the digital pin maximum is 55, and the maximum possible without rearchitecting how pins are accessed is 56 (ie, if UPDI could be turned into I/O on DA/DB we are already there). There is no way for an AVR that allows the same bit-level access that we enjoy to have it on on all pins to have more than 56 pins within the AVR instructionset. Each port takes up 4 of the 32 addresses in the low I/O space for the VPORT registers, on which the single cycle bit-level access relies. Only time will tell how this is handled. -* **No VPORTH or higher** - This is clean, simple, and limiting in some ways, but probably the best route. If you needed to use VPORT access, you'll just need to use the lower 7 ports for it. Surely you don't need 57+ pins all with VPORT atomic single cycle access! This is also, in my judgement, most likely based on the fact that on the ATmega2560, the only precedent we have for running out of low I/O registers for pins, they didn't even put ports H through L into the high I/O space. +The most any announced AVR has had is 86 digital pins, the ATmega2560; In the modern AVR era, the digital pin maximum is 55, and the maximum possible without rearchitecting how pins are accessed is 56 (ie, if UPDI could be turned into I/O on DA/DB we are already there). There is no way for an AVR that allows the same bit-level access that we enjoy, on all pins, to have more than 56 pins within the AVR instructionset. Each port takes up 4 of the 32 addresses in the low I/O space for the VPORT registers, on which the single cycle bit-level access relies. Only time will tell how this is handled. +* **No VPORTH or higher** - This is clean, simple, and limiting in some ways, but probably the best route. If you needed to use VPORT access, you'll just need to use the lower 7 ports for it. Surely you don't need 57+ pins all with VPORT atomic single cycle access! This is also, in my judgement, most likely based on the fact that on the ATmega2560, the only precedent we have for running out of low I/O registers for pins, they didn't even put ports H through L into the high I/O space (heck, not only that, they did not, as far as I can see, so much as mention the pin inequality in the datasheet!). * **VPORTs in High I/O** - There are many ways of dealing with it using a new set of differently behaving registers in the high I/O space. None of them retain the arbitrary compile-time-known-bit-write-is-one-clock of current VPORTs. At 8 registers per port, you could make a series of N operations on a bit or number of bits take N+1 clocks, and keep them atomic. This would mean a VPORT.DIR/DIRCLR/DIRSET, and VPORT.OUT/OUTSET/OUTCLR and VPORT.IN(writing to IN toggles, and there was never a single cycle DIR toggle). and VPORT.INTFLAGS. That's viable, though with the other registers in that, you could get at most only 3 more ports - which is only 24 pins: You couldn't quite do it for a 100 pin chip). Other solutions would lower capability and comprehensibility in exchange for squeezing in more pins. No matter how you do it, it doesn't really solve the problem. -* **VPORTs that can be remapped at runtime** - The stuff of library author's nightmares. You'd have some subset of the VPORTs (or maybe all of them) could be remapped. If you put the register for that in the high I/O space it could be written quickly. But this will cause unaware old code to break by writing to the wrong pins if it didn't know to check for it, and any code that used them (ie, code that is supposed to be fast) might have to check the mapping first. This is most like what was done on the XMegas, but also serves as a shining example of the over-complexity that I think doomed those parts to poor uptake and slow death. The best of these bad approaches would probably be to have VPORTG changed into say VPORTX. Could configure it with a register in the high I/O space so it was 2 cycle overhead to write (1 for LDI port number, 1 to OUT to register). That way all code for other ports wouldn't worry, and port G is present on the fewest parts so the least code would be impacted if ported, and would be straightforward to document +* **VPORTs that can be remapped at runtime** - The stuff of library author's nightmares. You'd have some subset of the VPORTs (or maybe all of them) that could be remapped. If you put the register for that in the high I/O space it could be written quickly. But this will cause unaware old code to break by writing to the wrong pins if it didn't know to check for it, and any code that used them (ie, code that is supposed to be fast) might have to check the mapping first. This is most like what was done on the XMegas, but also serves as a shining example of the over-complexity that I think doomed those parts to poor uptake and slow death. The best of these bad approaches would probably be to have VPORTG changed into say VPORTX. Could configure it with a register in the high I/O space so it was 2 cycle overhead to write (1 for LDI port number, 1 to OUT to register). That way all code for other ports wouldn't worry, and port G is present on the fewest parts so the least code would be impacted if ported, and would be straightforward to document `*` - Note on conventions for specifying numbers: 0x## refers to a hexadecimal number, while ## refers to a decimal digit and 0b######## refers to a value given as binary. For hexadecimal values, if the size of the datatype is unambiguosly known, we will typically represent them with an appropriate number of leading 0's - so 1 in a 1-byte datatype is written as 0x01, while 1 in a 16-bit datatype is written as 0x0001. Any time a number is not prefixed by 0x or 0b, the decimal form should be assumed. Which representation of a given value is chosen is based on the context of that value. Values that simply represent numbers are generally given in decimal. Values that are being subjected to bitwise operators, or that are bit masks, group codes, and similar, will be shown in hexadecimal. -## A few other macros you shouldn't need - meant for the CI testing; I need as many sketches as possible that run both here and megaTinyCor)' -** `_VALID_DIGITAL_PIN(n)`** where N will include at least 4 supported values, depending on how much work I feel like putting into it. The pins returned will be the same on all DD-series and tinyAVR parts. These are not intended to be *good* pins, just *valid* pins. That is, they are to be used when automated test success or failure based on "does the sketch compile" not "is the behavior on the hardware what is expected?") We don't have capability to conduct such testing at this time -** `_VALID_ANALOG_PIN(n)`** does the same thing for pins that can be used by analogRead(). PIN_PD0-PD7 for DA/DB series parts with at least 48 pins and all DA series parts. PD1-7 for 28 and 32-pin DD and DB-series. 20 pin parts get PD4-PD7, as do 14-pin ones. +`**` As far as the FCC testing goes, the net effect (a $5k+ barrier to entry applied only to domestic manufacturers) means that FCC might as well stand for Fail to Compete with China - Do you think any of the electronics we get from China passed FCC testing?! A good portion of them don't look to have been tested for basic functionality, let alone compliance. Since we;ve had a couple of decades of near unregulated electronic crap being imported, and there aren't computers failing, data infrastructure exploding, and airplanes crashing because of lost navigation (batteries catching fire? Well, I don't think any crashes? But definitely some emergecny landings)... maybe we don't need such strict rules (on radio emissions - and we could do wih? + +All of the cases I'm aware of where interference actually caused concern were from devices intentionally designed do disrupt other devices (usually GPS jammers being used by truck drivers to cheat someone or another. whether it's the delivery driver who jams their GPS, parks next to a ravine, and chucks packages into it, scanning them as delivered, or the long haul trucker dodging the limits on driving time and so he can drive for 3 days straight, enacted after incidents where drivers struck other vehicles diff --git a/megaavr/libraries/megaTinyCore/examples/ErsatzReset/ErsatzReset.ino b/megaavr/libraries/megaTinyCore/examples/ErsatzReset/ErsatzReset.ino index d90c649f..ac7af9d4 100644 --- a/megaavr/libraries/megaTinyCore/examples/ErsatzReset/ErsatzReset.ino +++ b/megaavr/libraries/megaTinyCore/examples/ErsatzReset/ErsatzReset.ino @@ -17,7 +17,7 @@ // so not ideal choices, and PA3 is right off the menu (for this code, but not for you) // because we use these sketches for CI testing, and one of the configurations that we use // for that testing is with an external clock - so we can't use the CLKI pin or the CI -// will fail, and spurrious failures like that are treated as release blocking issues +// will fail, and spurious failures like that are treated as release blocking issues // because of their tendency to hide non-spurrious errors. // In realworld situations, on the 8-pin parts, the correct pin to use is "whatever pin you // can spare" (if any).