I have a C code that I am targeting for AVR. The code is compiled using avr-gcc, mainly the gnu compiler with the correct backend.
What I'm trying to do is create a callback mechanism in one of my event driven / interrupt libraries, but I seem to have problems saving the value of the function pointer.
To get started, I have a static library. It has a header file ( twi_master_driver.h ) that looks like this:
#ifndef TWI_MASTER_DRIVER_H_ #define TWI_MASTER_DRIVER_H_ #define TWI_INPUT_QUEUE_SIZE 256
Now the C file ( twi_driver.c ):
#include <stdint.h> #include "twi_master_driver.h" void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback) { twi->slave.length = 0; twi->slave.slave_callback = slave_callback; twi->slave_address = slave_address; // temporary workaround <- why does this work?? twi->slave.slave_callback = twi->slave.slave_callback; } void twi_slave_interrupt_handler(twi_global_t *twi) { (twi->slave.slave_callback)(twi->slave.buffer, twi->slave.length); // some other stuff (nothing touches twi->slave.slave_callback) }
Then I create these two files in a static library (.a) and create my main program ( main.c ) # include # include # include # include #include "twi_master_driver.h"
// ...define microcontroller safe way for mystdout ... twi_global_t bus_a; ISR(TWIC_TWIS_vect, ISR_NOBLOCK) { twi_slave_interrupt_handler(&bus_a); } void my_callback(uint8_t *buf, uint16_t len) { uint8_t i; fprintf(&mystdout, "C: "); for(i = 0; i < length; i++) { fprintf(&mystdout, "%d,", buf[i]); } fprintf(&mystdout, "\n"); } int main(int argc, char **argv) { twi_init(2, &bus_a, &my_callback); // ...PMIC setup... // enable interrupts. sei(); // (code that causes interrupt to fire) // spin while the rest of the application runs... while(1){ _delay_ms(1000); } return 0; }
I carefully trigger events that cause an interrupt and call the appropriate handler. Using some fprintfs, I can say that the location assigned to twi->slave.slave_callback in the twi_init function is different from the location in the twi_slave_interrupt_handler function.
Although numbers do not make sense, in twi_init value is 0x13b, and in twi_slave_interrupt_handler when printing, the value is 0x100.
By adding a commented bypass line to twi_driver.c :
twi->slave.slave_callback = twi->slave.slave_callback;
The problem goes away, but this is clearly a magical and undesirable solution. What am I doing wrong?
As far as I can tell, I noted the corresponding volatile variables, and I tried labeling other volatile portions and removing volatile markings. I came up with a workaround when I noticed that after executing the twi_init task, the fprintf expressions are deleted, after which the value is read differently later.
The problem is how I look at the pointer to the function, and especially the part of the program that accesses the value of the pointer (the function itself?) Is technically in a different thread.
Any ideas?
Changes:
typos allowed in the code.
links to actual files: http://straymark.com/code/ [test.c | twi_driver.c | twi_driver.h]
fwiw: compiler options: -Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL
I tried the same code that was included directly (and not through the library) and I have the same problem.
Editing (round 2):
- I removed all optimizations, without my "workaround" the code works as expected. Adding back -Os causes an error. Why is -O corrupting my code?