定时器精度和中断
Timer accuracy and interrupts
我有一个 PIC24F Curiosity 开发板 (PIC24FJ128GA204),我正在尝试使用 TIM1 获得准确的秒计时。作为源,我使用辅助振荡器,它使用 32678 kHz xtl。
定时器配置了32个周期,对应1ms
void TMR1_Initialize (void)
{
//TMR1 0;
TMR1 = 0x0000;
//Period = 0.001 s; Frequency = 32000 Hz; PR1 32;
PR1 = 0x0020;
//TCKPS 1:1; TON enabled; TSIDL disabled; TCS External; TECS SOSC; TSYNC enabled; TGATE disabled;
T1CON = 0x8006;
IFS0bits.T1IF = false;
IEC0bits.T1IE = true;
tmr1_obj.timerElapsed = false;
}
因此,每 1 毫秒调用一次以下中断,其中毫秒计数存储在 uint32_t 变量中
void __attribute__ ( ( interrupt, no_auto_psv ) ) _T1Interrupt ( )
{
tmr1_obj.count++;
tmr1_obj.timerElapsed = true;
IFS0bits.T1IF = false;
}
在主循环中,我忙等待 tmr1_obj.count 变量,当它达到 1000 时,一条消息在 UART 中发送。
int main(void)
{
// initialize the device
SYSTEM_Initialize();
TMR1_Start();
char msg[] = "A\r\n";
while (1)
{
// Add your application code
int value = TMR1_SoftwareCounterGet();
if (value == 1000) {
printf("%s", msg);
TMR1_SoftwareCounterClear();
}
}
return -1;
}
在 UART 的另一端,我有一个应用程序读取消息并记录接收消息的时间以及与前一条消息的毫秒差值。
问题是我每条消息都有~8ms的累积。
22:05.026 1008
22:06.035 1009
22:07.045 1010
22:08.054 1008
22:09.063 1008
怀疑这可能与 UART 传输经常中断有关,我尝试每 10 秒而不是 1 秒发送一次消息。延迟一直累积到每 10 秒 ~80 毫秒。
最后,可能是 32 个刻度对应的不是 1 毫秒,而是 1.x 毫秒。为了测试我将周期更改为 32768,即 1 秒。我希望结果大致相同,但事实并非如此
31:15.216 999
31:16.216 999
31:17.216 1000
31:18.216 999
31:19.216 999
31:20.215 999
31:21.216 1000
看来周期会影响计时器的准确性。我不明白怎么做。难道每次中断都会损失一些微秒吗?我在数据 sheet 上没有看到任何内容,但我找不到任何其他解释。
请记住,在设置 PIC24 的周期时,您必须将“0”计为一个刻度,因此您要将周期设置为比您想要的周期少 1 个刻度(在本例中为 31) .
考虑一下您是否想要 5 个刻度的周期。如果将周期寄存器设置为 5,它将执行:
0->1 = 1 tick
1->2 = 2 ticks
2->3 = 3 ticks
3->4 = 4 ticks
4->5 = 5 ticks
5->0 = 6 ticks
所以你真的想将值设置为 4 以获得 5 个刻度(或 31 以获得 32 个刻度)。
现在开始您的 8 毫秒。因为您将它设置为 32,所以您得到 33 个刻度。每个刻度是:
1/32768 秒
其中 33 个是 33/32768 或大约 1.007 毫秒。其中 1000 个给你额外的 7 毫秒,这非常接近你的 8。所以你真正的问题是你试图使用一个不精确的值并且它会随着时间的推移累积错误。即使您将周期设置为 31(给出 32 个刻度),您也将关闭,因为您无法从 32768 Hz crystal 获得恰好 1ms。
您可以做的一件事是将周期设置为 31(或 32 个跳动)并与 1024 个跳动(而不是 1000)进行比较。如果不准确(不能承受公差),那应该更接近 1 秒。您的中断不会精确到 1 毫秒,但您的 1 秒将非常接近。
我有一个 PIC24F Curiosity 开发板 (PIC24FJ128GA204),我正在尝试使用 TIM1 获得准确的秒计时。作为源,我使用辅助振荡器,它使用 32678 kHz xtl。
定时器配置了32个周期,对应1ms
void TMR1_Initialize (void)
{
//TMR1 0;
TMR1 = 0x0000;
//Period = 0.001 s; Frequency = 32000 Hz; PR1 32;
PR1 = 0x0020;
//TCKPS 1:1; TON enabled; TSIDL disabled; TCS External; TECS SOSC; TSYNC enabled; TGATE disabled;
T1CON = 0x8006;
IFS0bits.T1IF = false;
IEC0bits.T1IE = true;
tmr1_obj.timerElapsed = false;
}
因此,每 1 毫秒调用一次以下中断,其中毫秒计数存储在 uint32_t 变量中
void __attribute__ ( ( interrupt, no_auto_psv ) ) _T1Interrupt ( )
{
tmr1_obj.count++;
tmr1_obj.timerElapsed = true;
IFS0bits.T1IF = false;
}
在主循环中,我忙等待 tmr1_obj.count 变量,当它达到 1000 时,一条消息在 UART 中发送。
int main(void)
{
// initialize the device
SYSTEM_Initialize();
TMR1_Start();
char msg[] = "A\r\n";
while (1)
{
// Add your application code
int value = TMR1_SoftwareCounterGet();
if (value == 1000) {
printf("%s", msg);
TMR1_SoftwareCounterClear();
}
}
return -1;
}
在 UART 的另一端,我有一个应用程序读取消息并记录接收消息的时间以及与前一条消息的毫秒差值。
问题是我每条消息都有~8ms的累积。
22:05.026 1008
22:06.035 1009
22:07.045 1010
22:08.054 1008
22:09.063 1008
怀疑这可能与 UART 传输经常中断有关,我尝试每 10 秒而不是 1 秒发送一次消息。延迟一直累积到每 10 秒 ~80 毫秒。
最后,可能是 32 个刻度对应的不是 1 毫秒,而是 1.x 毫秒。为了测试我将周期更改为 32768,即 1 秒。我希望结果大致相同,但事实并非如此
31:15.216 999
31:16.216 999
31:17.216 1000
31:18.216 999
31:19.216 999
31:20.215 999
31:21.216 1000
看来周期会影响计时器的准确性。我不明白怎么做。难道每次中断都会损失一些微秒吗?我在数据 sheet 上没有看到任何内容,但我找不到任何其他解释。
请记住,在设置 PIC24 的周期时,您必须将“0”计为一个刻度,因此您要将周期设置为比您想要的周期少 1 个刻度(在本例中为 31) .
考虑一下您是否想要 5 个刻度的周期。如果将周期寄存器设置为 5,它将执行:
0->1 = 1 tick
1->2 = 2 ticks
2->3 = 3 ticks
3->4 = 4 ticks
4->5 = 5 ticks
5->0 = 6 ticks
所以你真的想将值设置为 4 以获得 5 个刻度(或 31 以获得 32 个刻度)。
现在开始您的 8 毫秒。因为您将它设置为 32,所以您得到 33 个刻度。每个刻度是: 1/32768 秒
其中 33 个是 33/32768 或大约 1.007 毫秒。其中 1000 个给你额外的 7 毫秒,这非常接近你的 8。所以你真正的问题是你试图使用一个不精确的值并且它会随着时间的推移累积错误。即使您将周期设置为 31(给出 32 个刻度),您也将关闭,因为您无法从 32768 Hz crystal 获得恰好 1ms。
您可以做的一件事是将周期设置为 31(或 32 个跳动)并与 1024 个跳动(而不是 1000)进行比较。如果不准确(不能承受公差),那应该更接近 1 秒。您的中断不会精确到 1 毫秒,但您的 1 秒将非常接近。