2009-12-23 4 views
4

У меня есть код C, на который я нацелен для AVR. Код компилируется с помощью avr-gcc, в основном компилятор gnu с правом бэкэнда.Не удалось получить местоположение указателя функции

То, что я пытаюсь сделать, это создать механизм обратного вызова в одной из моих управляемых событиями/прерываниями библиотек, но, похоже, у меня проблемы с сохранением значения указателя функции.

Для начала у меня есть статическая библиотека. Он имеет заголовочный файл (twi_master_driver.h), который выглядит следующим образом:

#ifndef TWI_MASTER_DRIVER_H_ 
#define TWI_MASTER_DRIVER_H_ 

#define TWI_INPUT_QUEUE_SIZE 256 

// define callback function pointer signature 
typedef void (*twi_slave_callback_t)(uint8_t*, uint16_t); 

typedef struct { 
    uint8_t buffer[TWI_INPUT_QUEUE_SIZE]; 
    volatile uint16_t length; // currently used bytes in the buffer 
    twi_slave_callback_t slave_callback; 
} twi_global_slave_t; 

typedef struct { 
    uint8_t slave_address; 
    volatile twi_global_slave_t slave; 
} twi_global_t; 

void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback); 

#endif 

Теперь файл C (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) 
} 

Затем создадим эти два файла в статическую библиотеку (.a) и построить свою основную программу (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; 
} 

Я тщательно провожу события, вызывающие прерывание, и вызывает соответствующий обработчик. Используя некоторые fprintfs, я могу сказать, что местоположение, назначенное twi->slave.slave_callback в функции twi_init, отличается от функции в функции twi_slave_interrupt_handler.

Хотя цифры не имеют смысла, в twi_init значение равно 0x13b, а в twi_slave_interrupt_handler при печати значение равно 0x100.

Добавляя комментируемой обходной линии в twi_driver.c:

twi->slave.slave_callback = twi->slave.slave_callback; 

Проблема уходит, но это явно магическое и нежелательное решение. Что я делаю не так?

Насколько я могу судить, я отметил соответствующие переменные volatile, и я пробовал маркировать другие порции летучих и удалять летучие маркировки. Я придумал обходной путь, когда заметил, что после вывода twi_init операторы заставили считывать значение по-разному.

Проблема заключается в том, как я прохожу через указатель на функцию - и особенно часть программы, которая обращается к значению указателя (самой функции?), Технически находится в другом потоке.

Любые идеи?

изменение:

  • разрешенных опечатки в коде.

  • ссылки на реальные файлы: http://straymark.com/code/ [test.c | twi_driver.c | twi_driver.h]

  • FWIW: опции компилятора: -Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL

  • я пробовал тот же код включен непосредственно (а не через библиотеку), и у меня такая же проблема.

редактирует (круглые 2):

  • Я удалил все оптимизации, без моего "обходного" код работает, как ожидалось. Добавление back -Os вызывает ошибку. Почему -O развращает мой код?
+0

Возможно, ничего не изменилось, но попробуйте удалить '&' из первой строки в main: 'twi_init (2, & bus_a, my_callback);' – pmg

+0

Выйти в течение нескольких часов, но я проверю позже и повторю попытку если не решена: Краткие идеи; Это работает, если вы не используете библиотеку? (т. е. все отображаемое в одном файле). Можете ли вы действительно fprintf() от прерывания (звучит опасно) ?. Репост показывает все; Что вы имеете, не совсем совершенны в деталях, например length vs len в my_callback(), где вы разрешаете прерывания? это twi_driver.h или twi_master_driver.h? Извините, что не очень помог, так как я говорю, что попробую позже. –

+0

Как сборка, сгенерированная avr-gcc, отличается и без обходной линии? т. е. вы могли бы опубликовать результаты 'avr-gcc -S twi_driver.c' для обеих версий? На какие AVR вы нацеливаетесь? – mrkj

ответ

2

Просто догадка, но что произойдет, если вы включите эти две линии вокруг:

twi->slave.slave_callback = slave_callback; 
twi->slave.length = 0; 

исправить ли удаление НКУ флаг -fpack-struct проблему? Интересно, не наткнулся ли вы на ошибку, когда запись этого поля length перезаписывает часть значения обратного вызова.


Он смотрит на меня как с -Os оптимизациями на (вы можете попробовать сочетание отдельных оптимизаций обеспечиваемых -Os, чтобы увидеть, какой именно из них вызывает его), компилятор не излучающий правильный код, чтобы манипулировать поле длины uint16_t, когда оно не выровнено по 2-байтовой границе. Это происходит, когда вы включаете twi_global_slave_t внутри , который упакован, потому что начальный uint8_t член twi_global_t вызывает размещение структуры twi_global_slave_t по нечетному адресу.

Если вы сделаете это начальное поле twi_global_t a uint16_t, оно, вероятно, исправит его (или вы можете отключить упаковку структуры). Попробуйте последнюю сборку gcc и посмотрите, все ли это происходит - если это так, вы должны создать минимальный тестовый пример, который показывает проблему, поэтому вы можете отправить отчет об ошибке в проект gcc.

+0

похоже, что это на правильном пути, я поменял порядок и это исправляет проблему. Почему поле длины должно переписывать поле обратного вызова? –

+0

еще больше усложняет ситуацию, если я переупорядочу элементы структуры, проблема также исчезает. –

0

Заменить «& my_callback» на «my_callback» в функции main().

Поскольку разные потоки обращаются к обратному адресу, попробуйте защитить его с помощью мьютекса или блокировки чтения-записи.

Если указатель функции обратного вызова не обрабатывается обработчиком сигнала, то «летучий» квалификатор не нужен.

+0

Я пробовал это, это не имеет значения. Значение устанавливается (и не изменяется) до того, как прерывания включены, поэтому не требуется блокировка чтения и записи мьютекса доступа (контроль доступа осуществляется по соглашению). –

+1

Поскольку 'my_callback' является функцией, нет никакой разницы между' & my_callback' и 'my_callback'. И поскольку «различный поток» на самом деле является просто выполнением кода в контексте прерывания, блокировка будет просто тупиковой. Если прерывания отключены, а функция 'twi_init' - это то, что необходимо, что обеспечивается, если прерывания не включаются до тех пор, пока после этой точки. – caf

+0

@caf, вот что у меня есть. @Steve Emmerson: указатель функции обратного вызова обращается обработчиком сигнала, это точка, это не * изменено * этим обработчиком или любым другим после иналинизации. –

1

Это действительно звучит как проблема с повреждением стека/памяти. Если вы запустите avr-size в своем файле эльфа, что вы получите? Удостоверьтесь (данные + bss) < ОЗУ у вас на части. Эти типы проблем очень трудно отследить. Тот факт, что удаление/перемещение несвязанного кода изменяет поведение, - это большой красный флаг.