4

我有一些针对 AVR 的 C 代码。代码是用 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_callbacktwi_init函数中的位置不同twi_slave_interrupt_handler

虽然数字没有意义,twi_init但值是 0x13b,twi_slave_interrupt_handler打印时值是 0x100。

通过在中添加注释的解决方法行twi_driver.c

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

问题消失了,但这显然是一个神奇且不受欢迎的解决方案。我究竟做错了什么?

据我所知,我已经标记了适当的 variables volatile,并且我尝试将其他部分标记为 volatile 并删除 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 轮):

  • 我删除了所有优化,没有我的“解决方法”,代码按预期工作。添加回 -Os 会导致错误。为什么 -Os 会破坏我的代码?
4

3 回答 3

2

只是一种预感,但是如果您将这两行切换会发生什么:

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

删除-fpack-structgcc 标志是否可以解决问题?我想知道您是否没有偶然发现写入该length字段会覆盖部分回调值的错误。


在我看来,-Os优化(您可以尝试组合启用的各个优化-Os以准确查看导致它的原因),uint16_t当长度字段未对齐时,编译器没有发出正确的代码来操纵长度字段2 字节边界。当您包含一个被打包的twi_global_slave_t内部时会发生这种情况twi_global_t,因为 的初始uint8_t成员twi_global_t导致twi_global_slave_t结构被放置在一个奇数地址。

如果您制作twi_global_ta的初始字段,uint16_t它可能会修复它(或者您可以关闭结构打包)。尝试最新的 gcc 构建,看看它是否仍然发生 - 如果确实如此,您应该能够创建一个显示问题的最小测试用例,这样您就可以向 gcc 项目提交错误报告。

于 2009-12-23T04:55:47.303 回答
1

这听起来确实像是堆栈/内存损坏问题。如果你在你的 elf 文件上运行 avr-size,你会得到什么?确保 (data + bss) < 部件上的 RAM。这些类型的问题很难追踪。删除/移动不相关的代码会改变行为这一事实是一个很大的危险信号。

于 2009-12-24T03:53:18.910 回答
0

在函数 main() 中将“&my_callback”替换为“my_callback”。

因为不同的线程访问回调地址,请尝试使用互斥锁或读写锁来保护它。

如果信号处理程序不访问回调函数指针,则不需要“volatile”限定符。

于 2009-12-23T03:10:12.527 回答