Прискорення таймера AVR на ATmega328


9

Під час запуску годинника prescaler 64 на ATmega328, один з моїх таймерів прискорюється з невідомих причин у певний час виконання.

Я використовую два таймери на ATmega328, щоб генерувати тактову частоту, необхідну TLC5940 (див. Нижче про те, чому це не має значення). TIMER0генерує тактовий сигнал, використовуючи швидку ШІМ увімкнену, OC0Bі встановлюється наступним чином:

TCCR0A = 0
    |(0<<COM0A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM0A0)    // 
    |(1<<COM0B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM0B0)
    |(1<<WGM01)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(1<<WGM00)
    ;
TCCR0B = 0
    |(0<<FOC0A)     // Force Output Compare A
    |(0<<FOC0B)     // Force Output Compare B
    |(1<<WGM02)     // Bit 3 – WGM02: Waveform Generation Mode
    |(0<<CS02)      // Bits 2:0 – CS02:0: Clock Select
    |(1<<CS01)
    |(0<<CS00)      // 010 = clock/8
    ;
OCR0A = 8;
OCR0B = 4;
TIMSK0 = 0;

TIMER2подвоює рядок даних, щоб генерувати пустий імпульс кожні 256 TIMER0циклів і встановлюється таким чином:

ASSR = 0;
TCCR2A = 0
    |(0<<COM2A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM2A0)    // 
    |(0<<COM2B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM2B0)
    |(0<<WGM21)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(0<<WGM20)
    ;
TCCR2B = 0
    |(0<<FOC2A)     // Force Output Compare A
    |(0<<FOC2B)     // Force Output Compare B
    |(0<<WGM22)     // Bit 3 – WGM02: Waveform Generation Mode
    |(1<<CS22)      // Bits 2:0 – CS02:0: Clock Select
    |(0<<CS21)
    |(0<<CS20)      // 100 = 64
    ;
OCR2A = 255;
OCR2B = 255;
TIMSK2 = 0
    |(1<<TOIE2);    // Timer/Counter0 Overflow Interrupt Enable

TIMER2викликає ISR при переповненні (кожні 256 циклів). ISR вручну генерує пульсуючий імпульс, а при необхідності імпульс замикання:

volatile uint8_t fLatch;

ISR(TIMER2_OVF_vect) {
    if (fLatch) {
        fLatch = 0;
        TLC5940_XLAT_PORT |=  (1<<TLC5940_XLAT_BIT);        // XLAT -> high
        for (int i=0;i<10;i++)
            nop();
        TLC5940_XLAT_PORT &= ~(1<<TLC5940_XLAT_BIT);        // XLAT -> high
    }
    // Blank
    TLC5940_BLANK_PORT |= (1<<TLC5940_BLANK_BIT);
    for (int i=0;i<10;i++)
        nop();
    TLC5940_BLANK_PORT &= ~(1<<TLC5940_BLANK_BIT);
}

nop()Затримка в наведеному вище коді просто зробити імпульс більш очевидним на логічний аналізатор сліду. Ось main()як виглядає цикл у функції: надішліть кілька послідовних даних, дочекайтеся, коли ISR подбає про засувку, а потім виконайте це знову:

for (;;) {
    if (!fLatch) {
        sendSerial();
        fLatch = 1;
        _delay_ms(1);
    }
    nop();
}

sendSerial()чи надсилає деякі SPI ( код на пастібін заради стислості ). Моє питання полягає в тому, що після sendSerial()завершення, під час очікування fLatchвстановлення на низький (оброблений) таймер синхронізації прискорюється. Ось слід логічного аналізатора (я порубав області, де той самий сигнал продовжує зменшувати графіку):

введіть тут опис зображення

На лівій стороні канали 0 і 1 показують задній кінець SPI-даних, що надсилаються. Також зліва, на каналі 4, можна побачити згасаючий імпульс. На каналі 2 тактові імпульсні затискачі вздовж, як очікувалося. Навколо місця, де є розрив у зображенні, fLatchвстановлюється 1всередині main()звичайної програми. І незабаром після цього TIMER0прискорюється приблизно в 4 рази. Врешті-решт, імпульс змикання та замикаючий імпульс виконуються (канали 3 та 4, права третина зображення), і тепер імпульс синхронізації відновлює свою регулярну частоту, і серійні дані відправлено знову. Я спробував вивести delay_ms(1);лінію main(), але отримані ті самі результати. Що відбувається? Слід зазначити, що ATmega згортає кристал 20 МГц, а потім сповільнюється на 64 рази, використовуючи наступний код:

CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);

Для чого це: я експериментую з керуванням драйвером світлодіодного дисплея TLC5940 : цим мікросхемам потрібен зовнішній годинник плюс скидання в кінці циклу синхронізації.


Якщо у вас відладчик, спробуйте зупинити свій код, коли таймер занадто швидкий, і прочитайте назад реєстр конфігурації цього таймера. Виявивши неправильне значення, запустіть точку перерви в цьому зміні реєстру і подивіться, яка частина вашого коду працює неправильно. Я думаю, що проблема розташована у зовнішній бібліотеці, яку ви можете використовувати, і яка використовує цей таймер для внутрішніх матеріалів, таких як затримки.
Blup1980

Дві проблеми: а) у мене немає програміста JTAG, тому у мене немає способу налагодження мікросхеми; б) я ніколи не змінюю значення регістра таймера після налаштування, показаного вище, тому я не очікую, що значення регістру таймера будуть насправді змінюються. Це наївно?
angelatlarge

1
Насправді одна бібліотека, яку ви використовуєте, може змінити налаштування UART. Я бачу, що ви використовуєте функцію sendSerial (). Це частина вашого коду чи це зовнішня бібліотека? Ви можете не змінити налаштування не ви, а фрагмент коду в бібліотеці, що називається. Я пропоную використовувати свій послідовний порт для виведення параметрів конфігурації і спробувати з'ясувати, що змінилося. Ви також можете переглянути джерело використаних бібліотек (якщо такі є) і переконатися, що вони також не використовують цей таймер.
Blup1980

1
Окрім того, що @ Blup1980 запропонував ще одне, що, можливо, варто спробувати - це видалити TLC5940, щоб переконатися, що він нічого не робить із лінією годинника.
PeterJ

@ Blup1980 Я не впевнений, що бачу відповідність UART: я не використовую USART для SPI, а лише "звичайні" засоби SPI. sendSerial()мій код, який надсилає дані через SPI: він не торкається TCCRреєстрів (управління таймером).
angelatlarge

Відповіді:


1

Для швидкої налагодження я б спробував зробити те саме, використовуючи бібліотеку Arduino для TLC5940 і побачити, стає вона швидкою чи ні. Якщо він працює з бібліотекою, ви можете перевірити його джерело та порівняти з вашим. Оскільки ви знайомі з AVR, вам слід легко перетворити джерело Arduino в рідний AVR.

На випадок, якщо ви не знаєте, як завантажувати складені ескізи Arduino в AVR: Коли ви компілюєте свій ескіз, він створює шістнадцятковий файл (ви можете побачити точне місце розташування файлу, увімкнувши багатомовний режим у налаштуваннях). Ви можете завантажити цей шестигранник у свій AVR за допомогою улюбленого програміста.

Сподіваюся, це допомагає

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.