I discovered a bug in my code which only turns up if avr-gcc optimization is used.
Can somebody explain whats the problem here?
I'm aware that there are several smart ways to achieve some PWM, but that is not the point here.
Background Information
I play around with an LED and dimming it with PWM. To do some simple gamma correction I use a predefined array with the right values.
Code
This is the code I use, Main contains just the interrupt init and an empty while(1) loop:
volatile size_t fade;
volatile uint16_t counter = 0;
const uint16_t PROGMEM pwmtable_10[64] = {
0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10,
11, 12, 13, 15, 17, 19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55,
61, 68, 76, 85, 94, 105, 117, 131, 146, 162, 181, 202, 225, 250,
279, 311, 346, 386, 430, 479, 534, 595, 663, 739, 824, 918, 1023
};
ISR(TIMER0_OVF_vect) {
// fade is set in the main loop or somewhere else
if(counter < pwmtable_10[fade]) {
counter=counter+1;
PORTB = 0;
} else {
counter=0;
PORTB |= RED;
}
}
A value of 1 gives a very bright light, a value of 1023 gives a very dimmed light. The value 1023 is accessible through the array index 63.
The Problem
It doesn't matter which value I set fade to, the LED is always bright, if I compile my code with avr-gcc -Os
.
By using avr-gcc -O0
the code works.
Changing to code doesn't work even by setting fade to 63 inside the interrupt:
ISR(TIMER0_OVF_vect) {
fade=63;
if(counter < pwmtable_10[fade]) {
//[..]
This is what works:
- Replacing
pwmtable_10[fade]
with 1023. - Replacing
pwmtable_10[fade]
withpwmtable_10[63]
- Replacing
fade
with a new variable directly declared before the compare
Additional Info
Platform: Atmega 168
% avr-gcc --version
avr-gcc (GCC) 4.8.2
Commands to build and flash the code:
avr-gcc -Wall -Wextra -Os -mmcu=atmega168 -DF_CPU=16000000 -o moody.elf moody.c
avr-objcopy -j .text -j .data -O ihex moody.elf moody.hex
avrdude -b4 -c usbasp -v -p m168 -P usb -U flash:w:moody.hex
Best Answer
I believe problem is in PROGMEM attribute which saves const array into flash memory. To get values you need a memcpy_P() or so. For direct access try avoiding PROGMEM attribute. pwmtable_10[fade] gets random RAM value.
See http://www.nongnu.org/avr-libc/user-manual/pgmspace.html