为什么我的函数静态变量尽管递增但从不相同?

时间:2013-09-04 12:32:56

标签: c static embedded avr avr-gcc

我正在用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次。

我的函数的(dis)汇编代码似乎是理智的(除了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) ; } ,它就不会挂起

我完全不知所措。

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

2 个答案:

答案 0 :(得分:4)

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

这解释了我看到的行为:

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

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

感谢帮助人员。这并不是原来提出的问题的答案,但它已经解决了,所以这就是:)

答案 1 :(得分:2)

对于你说的我只能想到3种可能性:1)你假设在每次I2C通信时调用函数是不正确的,2)你的程序在一些不相关的函数中有一个错误(可能是内存泄漏)导致变量调用损坏。或者3)两个或多个线程同时调用您的函数,调用以不同于预期的方式递增,使用&gt; 而不是 == < / strong>,如果这样可以解决问题,那么你就是在一个多线程的环境中运行,而你却没有知道。

你需要一个准确的方法来知道调用的确切值,如果你没有调试器并且没有输出文本的方法,你唯一要留下的就是玩是时间。我不知道你编译器,但我确信它包含一些有用的计时功能,所以我要做的就是在增加10 +调用秒之前循环,然后再增加10 +调用秒,例如:

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.
   }
}

希望这有帮助。