代码之家  ›  专栏  ›  技术社区  ›  Mark Elliot

未传递函数指针位置

  •  4
  • Mark Elliot  · 技术社区  · 16 年前

    我有一些针对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 ) 包括: 包括: 包括: #包括 #包括“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;
    }
    

    我小心地触发导致中断触发的事件,并调用适当的处理程序。使用一些fprintf,我可以知道分配给 twi->slave.slave_callback twi_init 函数与 twi_slave_interrupt_handler 功能。

    尽管这些数字毫无意义, 捻音 该值为0x13B,且在 twi_slave_中断处理程序 打印时,值为0x100。

    通过在 C驱动程序 :

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

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

    据我所知,我已经标记了适当的变量 volatile 我试着把其他部分标记为易挥发,去掉易挥发的标记。当我注意到移除时,我想出了解决方法。 fprintf 分配后的语句 捻音 导致稍后以不同方式读取值。

    问题似乎在于我如何传递函数指针——尤其是访问指针值的程序部分(函数本身?)从技术上讲是不同的。

    有什么想法吗?

    编辑:

    • 解决了代码中的拼写错误。

    • 链接到实际文件: http://straymark.com/code/ [测试.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会导致错误。为什么-os会破坏我的代码?
    3 回复  |  直到 16 年前
        1
  •  2
  •   caf    16 年前

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

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

    是否删除 -fpack-struct GCC标记修复问题?我想知道你是否没有在写那篇文章的时候偶然发现一个错误 length 字段正在覆盖回调值的一部分。


    在我看来 -Os 优化(您可以尝试通过 -操作系统 为了确切地知道是哪个导致了它),编译器没有发出正确的代码来操作 uint16_t 当长度字段不在2字节边界上对齐时。当你包括一个 twi_global_slave_t 里面 twi_global_t 因为首字母 uint8_t 成员 Twitter全局语言 导致 全球奴隶 结构放置在奇数地址。

    如果你把初始字段设为 Twitter全局语言 UIT1616 它可能会修复它(或者您可以关闭结构打包)。尝试最新的gcc构建,看看它是否仍然发生——如果仍然发生,您应该能够创建一个显示问题的最小测试用例,这样您就可以向gcc项目提交一个bug报告。

        2
  •  1
  •   Rob Curtis    16 年前

    这听起来像是堆栈/内存损坏问题。如果你在你的ELF文件上运行AVR大小,你会得到什么?确保(数据+BSS)<零件上的RAM。这些类型的问题很难找到。删除/移动不相关的代码会改变行为,这是一个巨大的危险信号。

        3
  •  0
  •   Steve Emmerson    16 年前

    在函数main()中将“&my&u callback”替换为“my&u callback”。

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

    如果回调函数指针没有被信号处理程序访问,那么“volatile”限定符是不必要的。