使用HCS12微控制器防止撕裂读数

Tag*_*agc 7 c embedded microcontroller c89 68hc12

摘要

我正在尝试为MC9S12VR微控制器编写嵌入式应用程序.这是一个16位微控制器,但我处理的一些值是32位宽,而在调试时我捕获了一些似乎是由于读取错误导致的异常值.

我正在为C89中的这个微处理器编写固件,并通过飞思卡尔HC12编译器运行它,我想知道是否有人建议如何在这个特定的微控制器上阻止它们,假设情况就是这样.

细节

我的部分应用涉及驱动电机并根据编码器产生的脉冲估算其位置和速度(在电机的每次完整旋转时都会产生脉冲).

为此,我需要配置一个MCU定时器,以便跟踪脉冲之间经过的时间.但是,定时器的时钟频率为3 MHz(预分频后),定时器计数器寄存器仅为16位,因此计数器每隔~22ms溢出一次.为了补偿,我设置了一个中断处理程序,它在定时器计数器溢出时触发,并将"溢出"变量递增1:

// TEMP
static volatile unsigned long _timerOverflowsNoReset;

// ...

#ifndef __INTELLISENSE__
__interrupt VectorNumber_Vtimovf
#endif
void timovf_isr(void)
{
  // Clear the interrupt.
  TFLG2_TOF = 1;

  // TEMP
  _timerOverflowsNoReset++;

  // ...
}
Run Code Online (Sandbox Code Playgroud)

然后我可以计算出当前时间:

// TEMP
unsigned long MOTOR_GetCurrentTime(void)
{
  const unsigned long ticksPerCycle = 0xFFFF;
  const unsigned long ticksPerMicrosecond = 3; // 24 MHZ / 8 (prescaler)
  const unsigned long ticks = _timerOverflowsNoReset * ticksPerCycle + TCNT;
  const unsigned long microseconds = ticks / ticksPerMicrosecond;

  return microseconds;
}
Run Code Online (Sandbox Code Playgroud)

main.c,我暂时编写了一些调试代码,用于在一个方向上驱动电机,然后定期拍摄各种数据的"快照":

// Test
for (iter = 0; iter < 10; iter++)
{
  nextWait += SECONDS(secondsPerIteration);
  while ((_test2Snapshots[iter].elapsed = MOTOR_GetCurrentTime() - startTime) < nextWait);
  _test2Snapshots[iter].position = MOTOR_GetCount();
  _test2Snapshots[iter].phase = MOTOR_GetPhase();
  _test2Snapshots[iter].time = MOTOR_GetCurrentTime() - startTime;
  // ...
Run Code Online (Sandbox Code Playgroud)

在这个测试中,我MOTOR_GetCurrentTime()在代码中非常接近地读取两个地方,并将它们分配给全局可用结构的属性.

在几乎所有情况下,我发现读取的第一个值超出了while循环终止点的几微秒,第二个读取是在此之后的几微秒 - 这是预期的.但是,偶尔我发现第一次读取明显高于while循环应终止的点,然后第二次读取小于第一个值(以及终止值).

下面的屏幕截图给出了一个例子.在我能够重现它之前,它花了大约20次重复测试.在代码中,<snapshot>.elapsed写入之前<snapshot>.time所以我希望它具有稍小的值:

因为snapshot[8],我的应用程序首先读取20010014(超过应该终止繁忙循环的10ms以上),然后读取19988209.如上所述,每22ms发生一次溢出 - 具体而言,_timerOverflowsNoReset一个单位的差异将产生65535 / 3计算的微秒值的差异.如果我们说明这一点:

19988209 +\frac {65535} {3}  -  20010014 = 20010054  -  20010014 = 40

差异为40并不是我在其他读取对之间看到的差异(~23/24),所以我的猜测是,有一种撕裂正在进行,包括逐个读取_timerOverflowsNoReset.在忙碌循环中,它会执行一次调用MOTOR_GetCurrentTime(),错误地看到_timerOverflowsNoReset一个比实际更大的调用,导致循环提前结束,然后在下一次读取之后再次看到正确的值.

我的应用程序有其他问题我无法确定,我希望如果我解决这个问题,它可能会解决这些其他问题,如果他们有类似的原因.

编辑:在其他更改中,我已经更改了_timerOverflowsNoReset一些其他全局变量从32位无符号到16位无符号在我现在的实现中.

Cli*_*ord 2

计算滴答计数,然后检查溢出是否发生变化,如果是则重复;

#define TCNT_BITS 16 ; // TCNT register width

uint32_t MOTOR_GetCurrentTicks(void)
{
   uint32_t ticks = 0 ;
   uint32_t overflow_count = 0;

   do
   {
       overflow_count = _timerOverflowsNoReset ;
       ticks = (overflow_count << TCNT_BITS) | TCNT;
   }
   while( overflow_count != _timerOverflowsNoReset ) ;

   return ticks ;
}
Run Code Online (Sandbox Code Playgroud)

while 循环将不再迭代一次或两次。