Some of these are pretty briefly-explained here, but may be better-explained elsewhere (especially in my own code)... if you've questions, feel free to ask!
Table Of Contents:
- Storing data across resets without using EEPROM/FLASH (use EEAR(L/H)... or just use ".noinit")
- PWM/Compare-Match precision/accuracy quirks
- Using subi/sbci to *increment*
- ... (more to come, I'm sure)
- Code-size reduction... check out #limited-code hacks/tips (added 11-26-16)
- I HAVE NOT been keeping this ToC up-to-date... check out the project-logs!
See Also (in Comments, below):
- Generating a clean reset - UART Cleared + WDT-Reset (Thanks @Bruce Land!)
- LED memory-retention + .noinit - (Thanks @Jens Geisler!)
- ... (more to come?)
1. Where to store data across resets, without eating-up your EEPROM write-cycles?
EEARL/H (the EEPROM Address Registers) appear to go un-initialized...
Test:
- print-out the values of the EEAR registers
- store a value to them
- reset
- repeat
- (Upon power-up their values will be random. Subsequent resets will printout the value written)
(Tested on: ATmega8515)
Notes: "static" variables, in C, are automatically initialized to 0, even if you do not explicitly state to... Apparently globals are, as well. Locals, of course, are often stored to registers, which are initialized upon reset. An alternative, possibly, would be to define an uninitialized array in the SRAM? C might recognize use of an uninitialized value as an error, or might not...
Or, maybe there's something in this... from <avr/wdt.h>... "noinit" would probably do it.
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
2. PWM/Compare-Match precision/accuracy quirks
The basic jist: There are (at least) TWO methods for compare-matching, depending on the AVR device and/or its timer. (verified in FastPWM mode).
Some(?) AVR Timers guarantee that setting the OCR value to 0 will result in a PWM output that's ALWAYS low, and setting the OCR value to "TOP" will result in a PWM output that's ALWAYS high.So, imagine a case where TOP = 3... (255 is more common) The TCNT register will increment 0, 1, 2, 3, then reset to 0 and repeat...(See note)
One Cycle: |<------------->| ___ ___|___ ___ ___ ___|___ TCNT: 2 X 3 X 0 X 1 X 2 X 3 X 0 ¯¯¯ ¯¯¯|¯¯¯ ¯¯¯ ¯¯¯ ¯¯¯|¯¯¯ PWM OUTPUTS @ OCR= _______| | 0: \\\\\\\\ | (0%) ¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯ _______|_______________|___ TOP=3: //////// | (100%) ¯¯¯¯¯¯¯| Perfect, right? Two cases... OCR=0 = 0% PWM, OCR=TOP = 100% PWM... BUT WAIT! There're *THREE* cases not shown above... ___ ___|___ ___ ___ ___|___ TCNT: 2 X 3 X 0 X 1 X 2 X 3 X 0 ¯¯¯ ¯¯¯|¯¯¯.¯¯¯.¯¯¯.¯¯¯|¯¯¯ |___. . . | A: / \ . . / (25%) | ¯¯¯¯¯¯¯¯¯¯¯| |_______. . | B: / \ . / (50%) | ¯¯¯¯¯¯¯| |___________. | C: / \ / (75%) | ¯¯¯| See the problem?
There are 4 OCR values which are relevent in this case... 0-3
Yet, there are 5 possible PWM duty-cycles.
In my experience, the data-sheets are not particularly clear about *how* this is implemented; I had to find it experimentally.
In fact, two devices--with *nearly identical* sections in the data-sheets regarding FastPWM--actually handle this particular scenario differently... (Was it the ATtiny85 vs the ATtiny861?).
Basically, the end-result of my experiments is that as I recall:
ATtiny861: Option A above is just flat-out not possible. "B" results from OCR=1, "C" from OCR=2. Again, a single-count pulse-width is *not possible* on this device, and quite plausibly many other devices.
ATtiny85: This device seems to handle "compare match" differently... I can't recall the exact details, but...
Read more »
According to this page http://wp.josh.com/2014/03/03/the-mystery-of-the-zombie-ram/ the whole SRAM is quite persistent and will easily survive a reset cycle (but beware of the bootloader cleaning it anyway). And with a tiny current source like a LED turned solar cell, retention will last for inifinity.
To prevent the compiler from zeroing a global variable it has to be declared like this:
"int foo __attribute__ ((section (".noinit")));"