Під час запуску годинника 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 : цим мікросхемам потрібен зовнішній годинник плюс скидання в кінці циклу синхронізації.
sendSerial()мій код, який надсилає дані через SPI: він не торкається TCCRреєстрів (управління таймером).