Расположение указателя функции не передается - PullRequest
4 голосов
/ 23 декабря 2009

У меня есть некоторый 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 "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, и я попытался пометить другие части как энергозависимые и удалить летучие пометки. Я нашел обходной путь, когда заметил, что удаление операторов fprintf после назначения в 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 приводит к ошибке. Почему -Ос портит мой код?

Ответы [ 3 ]

2 голосов
/ 23 декабря 2009

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

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

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


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

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

1 голос
/ 24 декабря 2009

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

0 голосов
/ 23 декабря 2009

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

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

Если указатель на функцию обратного вызова не доступен обработчику сигнала, то квалификатор volatile не нужен.

...