4

我正在用 C 编写一个回调函数。它旨在初始化 I2C 传感器,并在每个(分阶段)配置步骤结束时调用;在第 9 次通话后,该设备几乎可以使用了。

该函数的基本思想是这样的:

void callback(void)
{
    static uint8_t calls = 0;

    if (++calls == 9) {
        // Finalise device setup (literally a single line of code)
    }
}

我的问题是上面的 if 语句从未被输入,尽管该函数被调用了 9 次。

我的函数的(反)汇编代码看起来很正常(除了增加的subi . 0xFF技巧,尽管包含了一条inc指令):

00000a4c <callback>:
     a4c:   80 91 9e 02     lds r24, 0x029E
     a50:   8f 5f           subi    r24, 0xFF   ; 255
     a52:   80 93 9e 02     sts 0x029E, r24
     a56:   89 30           cpi r24, 0x09   ; 9
     a58:   09 f0           breq    .+2         ; 0xa5c <callback+0x10>
     a5a:   08 95           ret
     a5c:   2e e1           ldi r18, 0x1E   ; 30
     a5e:   35 e0           ldi r19, 0x05   ; 5
     a60:   41 e0           ldi r20, 0x01   ; 1
     a62:   60 e0           ldi r22, 0x00   ; 0
     a64:   84 e7           ldi r24, 0x74   ; 116
     a66:   0c 94 c7 02     jmp 0x58e   ; 0x58e <twi_set_register>

我正在为 Atmel AVR 芯片编写代码,因此使用 avr-gcc 进行编译。我没有有意义的代码调试能力(我没有 JTAG 程序员,而且该功能在任何情况下都是异步/分相的;USART 打印太慢了)。

但是,我可以使用逻辑分析器,并且能够通过while (1) ;在代码中放置语句来确定许多事情:

  • 该函数被调用- 如果我在函数的开头放置一个无限循环,微控制器就会挂起
  • 该函数应该被调用 9 次- 该函数的触发器是 I2C 通信,在上一步中它在第一次通信后立即挂起;我可以观察到 9 个完整有效的 I2C 通信
  • 调用在函数内递增- 如果我在递增if (calls == 0) { while (1) ; } 之后添加,它不会挂起
  • 在函数开始时调用永远不会非零- 如果我在增量if (calls) { while(1) ; } 之前添加,它不会挂起

我完全没有想法。

有人对可能导致这种情况的原因有什么建议,甚至对我可以采取的新调试步骤有什么建议吗?

4

2 回答 2

4

我最终找到了错误的原因;另一个子系统由于第一次调用回调函数的副作用而中断,这意味着没有其他调用成功。

这解释了我看到的行为:

  • 它第一次挂起,因为它实际上被调用了
  • 它没有第二次(或任何未来的时间)挂起,因为它只被调用一次
  • 我观察到的 I2C 事务正在发生,但由于其他子系统(任务)中断,它们的回调机制无法正常运行

我能够通过使用几个 GPIO 引脚作为调试切换来解决这个问题,从而跟踪调用是如何通过 TWI 接口进行的。

谢谢你们的帮助。这并不是对提出的原始问题的真正答案,但它已经解决了,所以这就是:)

于 2013-09-05T08:27:59.787 回答
2

对于您所说的,我只能想到 3 种可能性:1)您认为在每次 I2C 通信上都调用该函数的假设是不正确的,2)您的程序在某些不相关的函数中存在错误(可能是内存泄漏),这会导致变量呼吁变得腐败。或 3) 两个或多个线程同时调用您的函数,并且调用的递增方式与您预期的不同,请使用>而不是==,如果这解决了问题,那么您正在运行在多线程环境中并且您不知道。

您需要一种准确的方法来知道调用的确切值,如果您没有调试器并且也没有输出文本的方法,那么您唯一可以玩的就是时间。我不认识你的编译器,但我确信它包含一些有用的计时函数,所以我会做的是在递增 10+calls 秒之前循环,然后再递增 10+calls 秒,例如:

sleep(1000*(10+calls));
++calls;
sleep(1000*(10+calls));

if(calls>8){
   // your actual code
}

我会(手中的计时器)预计第一次通话延迟为(10+0 加 10+1)= 21 秒,第二次通话为 23 秒,第三次通话为 25 秒,依此类推。这样我就可以确定调用的值从 0 开始,然后逐渐增加到 9。

此外,您必须测试您所期望的,而不是您不期望的,所以不要这样做:

++calls;
if (calls==0) while (1);

做这个:

++calls;
if (calls==1) while (1);

这样,如果您的程序挂起,您可以确定调用的值正好是 1,而不是任何不同于零的值。如果您计算一个有效的 I2C 通信并且您的程序挂起,则从 0 到 1 的转换正确完成,因此相应地更改挂起语句:

++calls;
if (calls==2) while (1);

同样,如果您在程序挂起之前计算 2 个有效的 I2C 通信,这意味着从 1 到 2 的转换是正确的,依此类推。

另一个建议,试试这个:

uint8_t Times(void){
   static uint8_t calls = 0;
   return ++calls;
}


void callback(void){

   if (Times()>8) {
       // Your actual code
   }

}

和这个:

void callback(void){
static uint8_t calls = 0;

   if (calls++>7) {
       // some code.
   }
}

希望这可以帮助。

于 2013-09-04T14:37:34.207 回答