在 ATmega328 上以 64 的时钟预分频器运行时,我的一个计时器在执行的特定时间出于未知原因加速。
我在 ATmega328 上使用两个定时器来生成TLC5940所需的时钟(原因见下文;这对问题无关紧要)。TIMER0
使用 Fast PWM on 生成时钟信号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
旋转数据线以每 256TIMER0
个周期产生一个消隐脉冲,设置如下:
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 使用 20Mhz 晶体计时,然后使用以下代码减慢 64 倍:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
这是做什么用的:我正在尝试控制TLC5940 LED 驱动器:这些芯片需要一个外部时钟以及在时钟周期结束时的复位。