首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在C中为一个不断变化的变量(比如一个定时器)拍摄快照?

如何在C中为一个不断变化的变量(比如一个定时器)拍摄快照?
EN

Stack Overflow用户
提问于 2018-06-02 22:51:39
回答 1查看 242关注 0票数 0

我目前正在编写一个很小的微控制器,并希望为游戏设置暂停功能。我已经能够从ISR计数器溢出创建一个计时器,但是我还不能想出如何暂停这个计数器。我试过了:

代码语言:javascript
复制
ISR(TIMER1_OVF_vect) {
    if (paused == 0){   
        overflow_counter ++;
    }
}

这似乎对计数器没有影响,无论我在函数中放什么指令,计数器都会继续运行。我尝试过各种if语句,但它们都被忽略了--函数只是出于某种原因增加了计数器(即使我把overflow_counter --)!

所以我试着设置另一个变量,当暂停按钮被按下时,它会拍摄时间的快照,然后当游戏取消暂停时,它会拍摄另一个快照,并计算差值。然后,这将从总时间中去掉。

代码语言:javascript
复制
double snapshot_time = 0;
double get_current_time(void){
    double time = ( overflow_counter * 65536.0 + TCNT1 ) * PRESCALE  / FREQ;
    if (paused == 0 && switch_state[5] == 1){
        snapshot_time = time;
    }
    return time;
}

我曾尝试将snapshot_time设置为全局变量,并将其设置为等于time,我想这可能会动态捕获静态变量,但不幸的是它没有。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-06-03 07:30:26

你的问题中隐藏了很多方面。

1.首先,计数器变量应该标记为volatile。编译器正在对变量应用不同的优化,因此,假设寄存器只是存储变量内容的位置,那么它可能会将变量加载到寄存器中,并继续使用寄存器。如果变量是用关键字volatile声明的,那么编译器知道它在任何时候都可能偶尔改变,因此每次访问变量时,编译器都会重新加载和/或重写该变量。因此,它可以这样声明

代码语言:javascript
复制
volatile uint16_t overflow_counter;

paused变量也是如此。

2.您应该记住,如果不禁用中断,则定时器中断可能发生在任何两个处理器的指令之间。由于处理器是8位的,所以它使用8位宽的总线访问内存。这意味着,要读取16位数据,需要2条指令。假设我们将计数器值复制到一个局部变量中:

代码语言:javascript
复制
uint16_t counter_snapshot = overflow_counter;

局部变量将分配两个寄存器,并将执行两个内存读取操作。让我们假设中断发生在第一个中断之后,但在第二个中断之前。因此,在输出中,您将从它之前的值复制一半的数字,而从它复制的第二个数字是新的。即,该值将被损坏。如果变量是8位的并由一条指令复制,则不会发生这种情况。但是如果它更宽,或者如果它是读-修改-写的,那么应该采取预防措施:

代码语言:javascript
复制
 uint8_t old_sreg = SREG; // SREG i/o register contains processor state flags, including "interrupt flag", which allows interrupt
 cli(); // clear the "interrupt flag", preventing interrupts from happening
 uint16_t counter_snapshot = overflow_counter; // safely taking the snapshot
 SREG = old_sreg; // restoring the "interrupt flag" to it's previous state. Other flags also restored but we do not care about them.

3.如上所述,中断可以在任何时间发生。这意味着如果您尝试同时读取overflow_counter和TCNT1,中断可能会在两者之间发生,因此,结果将不会像预期的那样。特别是如果这两个值的读取被诸如浮点乘法这样的长操作所分隔。因此,解决方法可能如下所示:

代码语言:javascript
复制
 uint8_t old_sreg = SREG; // saving state
 cli(); // locking interrupts
 // latching values we are interested in
 uint16_t latch_overflow_counter = overflow_counter;
 uint16_t latch_tcnt1 = TCNT1; 
 uint8_t latch_tifr1 = TIFR1;
 SREG = old_sreg; // restoring interrupts
 /* We are disabling interrupts, but it do not stop the timer from counting,
 therefore TCNT1 value continue changing, and timer could overflow in any time
 within that block above. But which moment exactly? Before we read TCNT1 or just after? 
 Let's assume if TCNT1 have high values then we got it's value just before the timer overflow;
 otherwise, overflow happened before that */
 if ((latch_tifr1 & (1 << TOV1)) && // we got the overflow flag set
     (latch_tcnt < 32768) { // we are within the low values
     latch_overflow_counter++; // increasing the latched value
 }

double time = ( latch_overflow_counter * 65536.0 + latch_tcnt1 ) * PRESCALE  / FREQ; // now using latched values to calculate...

顺便说一句,如果避免在不必要的情况下使用浮点,则可以大大提高吞吐量。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50658105

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档