Таймер ускорения AVR на ATmega328

9

Когда на ATmega328 работает прескейлер часов 64, один из моих таймеров ускоряется по неизвестным причинам в определенный момент выполнения.

Я использую два таймера на ATmega328 для генерации тактирования, необходимого для TLC5940 (см. Ниже, почему; это несущественно для вопроса). TIMER0генерирует тактовый сигнал, используя Fast PWM, 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-посылки ( код на pastebin для краткости ). Моя проблема в том, что после 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 : для этих чипов требуются внешние часы плюс сброс в конце тактового цикла.

angelatlarge
источник
Если у вас есть отладчик, попробуйте остановить свой код, когда таймер работает слишком быстро, и прочитать регистр конфигурации этого таймера. Как только вы нашли тот с неправильным значением, запустите точку останова в этом изменении регистра и посмотрите, какая часть вашего кода работает неправильно. Я предполагаю, что проблема находится во внешней библиотеке, которую вы можете использовать и которая использует этот таймер для внутренних вещей, таких как задержки.
Blup1980
Две проблемы: а) у меня нет JTAG-программиста, поэтому у меня нет способа отладки чипа б) я никогда не изменяю значение регистра таймера после установки, показанной выше, поэтому я не ожидаю, что значения регистра таймера на самом деле изменить. Это наивно?
angelatlarge
1
Фактически одна используемая вами библиотека может изменить настройки UART. Я вижу, что вы используете функцию sendSerial (). Это часть вашего кода или внешняя библиотека? Не вы сами меняете настройки, а фрагмент кода внутри вызываемой библиотеки. Я предлагаю вам использовать ваш последовательный порт для вывода параметров конфигурации и попытаться выяснить, что изменилось. Вы также можете посмотреть на источник используемых библиотек (если они есть) и убедиться, что они тоже не используют этот таймер.
Blup1980
1
Помимо того, что @ Blup1980 предложил еще одну вещь, которую, возможно, стоит попробовать, это удалить TLC5940, чтобы убедиться, что он ничего не делает с тактовой линией.
PeterJ
@ Blup1980 Я не уверен, что вижу актуальность UART: я не использую USART для SPI, просто «обычные» средства SPI. sendSerial()мой код, который отправляет данные через SPI: он не касается TCCRрегистров (управление таймером).
angelatlarge

Ответы:

1

Для быстрой отладки я попытался бы сделать то же самое, используя Arduino Library для TLC5940, и посмотреть, быстро ли это или нет. Если она работает с библиотекой, вы можете проверить ее источник и сравнить с вашим. Поскольку вы знакомы с AVR, вы должны легко преобразовать исходный код Arduino в собственный AVR.

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

Надеюсь, поможет

superkeci
источник