【问题标题】:Why is my function static variable never different despite being incremented?为什么我的函数静态变量尽管增加了却没有什么不同?
【发布时间】:2013-09-04 12:32:56
【问题描述】:

我正在用 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) ; } ,它不会挂起

我完全没有想法。

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

【问题讨论】:

  • 编译器似乎没有正确处理静态变量。您是否尝试过使用全局变量?
  • 根据您提供的事实,唯一可以推测的是您实际上并没有调用 9 次回调。反汇编是正确的(如预期的那样),并且您说回调至少被调用一次。但是,您没有在哪里确认它被调用了 9 次。一个建议:将循环计数器从 9 降低(到 7 或 5 或 2),然后查看您实际调用 twi_set_register 的时间点。另一个想法是,如果你有空闲的 I/O 在进入/退出时切换一个 pin 到 callback() 并使用逻辑分析器来确认你在其中的频率。
  • @IngoLeonhardt - 我尝试使用全局变量,结果相同。我还检查了地址 0x029E 是否在 .data 中(它在内部 SDRAM 中,所以应该适合读/写访问)
  • @Sapi 我同意调用在开始时为零。但是鉴于您提供的事实,我不相信回调被调用了 9 次。它可能被多次调用,但不会被调用 9 次。因此,如果您将检查从 9 降低到任何值,您可以确定它被调用了多少次;从而将您指向代码问题(可能不是此段)...
  • 能否提供触发回调的代码?与内存损坏、静态变量处理不正确等相反,我更容易相信这不起作用。

标签: c static embedded avr avr-gcc


【解决方案1】:

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

这解释了我看到的行为:

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

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

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

【讨论】:

    【解决方案2】:

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

    您需要一种准确的方法来知道 调用 的确切值,如果您没有调试器并且也没有输出文本的方法,那么您唯一需要做的就是玩就是时间。我不认识你的编译器,但我确信它包含一些有用的计时函数,所以我会做的是在递增 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 秒,依此类推。这样我就可以确定 calls 的值从 0 开始,然后逐渐增加到 9。

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

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

    这样做:

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

    这样,如果您的程序挂起,您可以确定 calls 的值正好是 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.
       }
    }
    

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      • 2019-07-08
      • 2021-12-06
      • 2021-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多