函数指针位置未通过

时间:2009-12-23 01:44:18

标签: c function-pointers avr avr-gcc

我有一些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,并且我尝试将其他部分标记为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会破坏我的代码?

3 个答案:

答案 0 :(得分:2)

只是预感,但如果你切换这两条线会发生什么:

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

删除-fpack-struct gcc标志是否可以解决问题?我想知道你是不是偶然发现了写length字段覆盖回调值的一部分的错误。


在我看来,就像-Os优化一样(您可以尝试-Os启用的各个优化的组合,以确切地看到导致它的原因),编译器不会发出正确的代码,当它在2字节边界上未对齐时,操纵uint16_t长度字段。如果您在twi_global_slave_t中包含twi_global_t,则会发生这种情况,因为uint8_t的{​​{1}}成员会将twi_global_t结构放置在奇怪的地址。

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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

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