函数指针位置未传递

发布于 2024-08-15 14:12:32 字数 3521 浏览 12 评论 0原文

我有一些针对 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 "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_init 函数中分配给 twi->slave.slave_callback 的位置与 twi_slave_interrupt_handler 中的位置不同 函数。

尽管这些数字没有意义,但在 twi_init 中该值为 0x13b,而在 twi_slave_interrupt_handler 中打印时该值为 0x100。

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

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

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

据我所知,我已经标记了适当的变量易失性,并且我尝试将其他部分标记为易失性并删除易失性标记。当我注意到在 twi_init 中赋值后删除 fprintf 语句导致稍后以不同方式读取值时,我想出了解决方法。

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

有什么想法吗?

编辑:

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

  • 实际文件的链接: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 会破坏我的代码?

I've got some C code I'm targeting for an AVR. The code is being compiled with avr-gcc, basically the gnu compiler with the right backend.

What I'm trying to do is create a callback mechanism in one of my event/interrupt driven libraries, but I seem to be having some trouble keeping the value of the function pointer.

To start, I have a static library. It has a header file (twi_master_driver.h) that looks like this:

#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

Now the C file (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)
}

Then I build those two files into a static library (.a) and construct my main program (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;
}

I carefully trigger the events that cause the interrupt to fire and call the appropriate handler. Using some fprintfs I'm able to tell that the location assigned to twi->slave.slave_callback in the twi_init function is different than the one in the twi_slave_interrupt_handler function.

Though the numbers are meaningless, in twi_init the value is 0x13b, and in twi_slave_interrupt_handler when printed the value is 0x100.

By adding the commented workaround line in twi_driver.c:

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

The problem goes away, but this is clearly a magic and undesirable solution. What am I doing wrong?

As far as I can tell, I've marked appropriate variables volatile, and I've tried marking other portions volatile and removing the volatile markings. I came up with the workaround when I noticed removing fprintf statements after the assignment in twi_init caused the value to be read differently later on.

The problem seems to be with how I'm passing around the function pointer -- and notably the portion of the program that is accessing the value of the pointer (the function itself?) is technically in a different thread.

Any ideas?

Edits:

  • resolved typos in code.

  • links to actual files: http://straymark.com/code/ [test.c|twi_driver.c|twi_driver.h]

  • fwiw: compiler options: -Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL

  • I've tried the same code included directly (rather than via a library) and I've got the same issue.

Edits (round 2):

  • I removed all the optimizations, without my "workaround" the code works as expected. Adding back -Os causes an error. Why is -Os corrupting my code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

合约呢 2024-08-22 14:12:32

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

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

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


在我看来,就像启用了 -Os 优化(您可以尝试组合使用 -Os 启用的各个优化,以准确查看是哪一个优化引起的),编译器当 uint16_t 长度字段未在 2 字节边界上对齐时,不会发出正确的代码来操作该长度字段。当您在打包的 twi_global_t 中包含 twi_global_slave_t 时,会发生这种情况,因为 twi_global_t 的初始 uint8_t 成员会导致要放置在奇数地址的 twi_global_slave_t 结构体。

如果您将 twi_global_t 的初始字段设置为 uint16_t ,它可能会修复它(或者您可以关闭结构打包)。尝试最新的 gcc 版本,看看它是否仍然发生 - 如果仍然发生,您应该能够创建一个显示问题的最小测试用例,以便您可以向 gcc 项目提交错误报告。

Just a hunch, but what happens if you switch these two lines around:

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

Does removing the -fpack-struct gcc flag fix the problem? I wonder if you haven't stumbled upon a bug where writing that length field is overwriting part of the callback value.


It looks to me like with the -Os optimisations on (you could try combinations of the individual optimisations enabled by -Os to see exactly which one is causing it), the compiler isn't emitting the right code to manipulate the uint16_t length field when its not aligned on a 2-byte boundary. This happens when you include a twi_global_slave_t inside a twi_global_t that is packed, because the initial uint8_t member of twi_global_t causes the twi_global_slave_t struct to be placed at an odd address.

If you make that initial field of twi_global_t a uint16_t it will probably fix it (or you could turn off struct packing). Try the latest gcc build and see if it still happens - if it does, you should be able to create a minimal test case that shows the problem, so you can submit a bug report to the gcc project.

柠檬色的秋千 2024-08-22 14:12:32

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

This really sounds like a stack/memory corruption issue. If you run avr-size on your elf file, what do you get? Make sure (data + bss) < the RAM you have on the part. These types of issues are very difficult to track down. The fact that removing/moving unrelated code changes the behavior is a big red flag.

莫相离 2024-08-22 14:12:32

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

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

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

Replace "&my_callback" with "my_callback" in function main().

Because different threads access the callback address, try protecting it with a mutex or read-write lock.

If the callback function pointer isn't accessed by a signal handler, then the "volatile" qualifier is unnecessary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文