Таймер точности и прерываний

У меня есть 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 секунда будет чертовски близкой.

Другие вопросы по тегам