我正在尝试使用 atmel xmega avr 微 Controller 进行正交解码。 Xmega 只有 16 位
计数器。此外,我已经用完了所有可用的计时器。
现在制作32位
计数器我使用了一个16位
计数器并且在它的溢出/欠流中断
中我有递增/decrement 一个 16 位的全局变量,这样我们就可以通过组合它们来制作 32 位的计数器。
ISR(timer_16bit)
{
if(quad_enc_mov_forward)
{
timer_over_flow++;
}
else if (quad_enc_mov_backward)
{
timer_over_flow--;
}
}
目前一切正常。但是我需要在并行运行的各种任务中使用这个 32 位值。我正在尝试读取 32 位值,如下所示
uint32_t current_count = timer_over_flow;
current_count = current_count << 16;
current_count = current_count + timer_16bit_count;
`timer_16_bit_count` is a hardware register.
现在我面临的问题是,当我在第一条语句中将读取的 timer_over_flow
读取到 current_count
时,我添加了 timer_16bit_count
可能发生溢出,16 位
计时器可能已变为零
。这可能会导致采用完全错误的值(value)。
我正试图在多个任务中读取这个 32 位值。
有没有办法防止这种数据损坏并获得 32 位值的工作模型。
不同成员寻求的详细信息:
我的电机可以向前或向后移动,并相应地计算增量/减量。
在 ISR 的情况下,在启动我的电机之前,我设置了全局变量(
quad_enc_mov_forward
&quad_enc_mov_backward
),以便在出现溢出时/underflowtimer_over_flow
将相应更改。在 ISR 中修改的变量被声明为
volatile
。多任务意味着我使用 RTOS 内核处理大约 6 个任务(主要是 3 个并行运行的任务)。
在 XMEGA 中,我直接读取
TCCO_CNT
寄存器的低字节。
最佳答案
一种解决方案是:
uint16_t a, b, c;
do {
a = timer_over_flow;
b = timer_16bit_count;
c = timer_over_flow;
} while (a != c);
uint32_t counter = (uint32_t) a << 16 | b;
来自 user5329483 的评论, 这不能与禁用中断一起使用,因为提取到 b
的硬件计数器可能会发生变化,而修改 timer_over_flow
的中断服务例程 (ISR) 如果中断将不会运行被禁用。如果在此期间发生回绕,则 ISR 有必要中断此代码。
这会获取计数器并检查高位字是否发生了变化。如果是,此代码将重试。当循环退出时,我们知道低位字在读取期间没有回绕。 (除非有可能我们读取高字,然后包装低字,然后我们读取低字,然后以另一种方式包装,然后我们读取高字。如果您的系统中可能发生这种情况,另一种选择是添加一个标志,ISR 在高位字更改时设置该标志。读取器将清除标志,读取定时器字,然后读取标志。如果设置了标志,它会再次尝试。)
请注意,timer_over_flow
、timer_16bit_count
和标志(如果使用)必须是 volatile
。
如果 wrap-two-times 场景不会发生,那么你可以消除循环:
- 如上所述阅读
a
、b
和c
。 - 将
b
与0x8000
进行比较。 - 如果
b
有一个高值,要么没有换行,它是在向上换行之前读取的(0xffff 到 0),或者是在向下换行之后读取的。使用a
或c
中较小的一个。 - 否则,要么没有换行,
b
是在向上换行后读取的,或者是在向下换行之前读取的。使用a
或c
中较大的一个。
关于c - 制作 32 位计数器时出现全局变量问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48513086/