Таймер точности и прерываний
У меня есть PIC24F Curiosity Board (PIC24FJ128GA204), и я пытаюсь получить точную синхронизацию с TIM1. В качестве источника я использую вторичный генератор, который использует 32678 кГц XTL.
Таймер настроен на 32 периода, что соответствует 1 мс
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 у меня есть приложение, которое читает сообщение и записывает время, когда оно было получено, и разницу в миллисекундах относительно предыдущего сообщения.
Проблема в том, что у меня есть накопление ~8 мс для каждого сообщения.
22:05.026 1008
22:06.035 1009
22:07.045 1010
22:08.054 1008
22:09.063 1008
Подозревая, что это может быть связано с тем, что передача UART часто прерывается, я пытался отправлять сообщение каждые 10 секунд вместо 1. Задержка постоянно увеличивалась до ~80 мс каждые 10 секунд.
Наконец, возможно, что 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
Кажется, что период влияет на точность таймера. И я не понимаю как. Может ли быть так, что на каждое прерывание теряются несколько микросекунд? Я не вижу ничего на листе данных, но я не могу найти другого объяснения.
1 ответ
Имейте в виду, что при установке периода 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 тика), вы будете отключены, потому что вы не можете получить ровно 1 мс из кристалла 32768 Гц.
Одна вещь, которую вы можете сделать, это установить период на 31 (или 32 тика) и сравнить с 1024 тиками (вместо 1000). Это должно быть намного ближе к 1 секунде, если не точно (допуски не выдерживают). Ваши прерывания не будут точно на 1 мсек, но ваша 1 секунда будет чертовски близкой.