ATmega328 上的 AVR 定时器加速

电器工程 avr 大气压 spi 计时器
2022-02-03 16:22:42

在 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 驱动器:这些芯片需要一个外部时钟以及在时钟周期结束时的复位。

1个回答

为了快速调试,我会尝试使用Arduino Library for TLC5940来做同样的事情,看看它是否越来越快。如果它适用于图书馆,您可以检查其来源并与您的比较。由于您熟悉 AVR,您应该轻松地将 Arduino 源代码转换为原生 AVR。

以防万一您不知道如何将已编译的 Arduino 草图上传到 AVR:当您编译草图时,它会创建一个 hex 文件(您可以通过在设置中打开详细模式来查看文件的确切位置)。您可以使用您最喜欢的程序员将该十六进制上传到您的 AVR。

希望能帮助到你