ATmega328上的AVR计时器加速


9

当在ATmega328上以64的时钟预分频器运行时,我的一个定时器在执行的特定时间由于未知原因而加速。

我在ATmega328上使用了两个定时器来生成TLC5940所需的时钟(请参见下面的原因;这与问题无关紧要)。TIMER0使用快速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在溢出时(每256个周期)调用ISR。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设置。然后很快就以大约4倍的速度加速。最后,执行了消隐脉冲和锁存脉冲(通道3和4,位于图像的三分之一处),现在时钟脉冲恢复了其正常频率,串行数据变为再次发送。我尝试取出中的行,但获得了相同的结果。这是怎么回事?我应该注意,ATmega是由20Mhz晶体时钟驱动的,然后使用以下代码将速度降低64倍:1main()TIMER0delay_ms(1);main()

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

这是为了什么:我正在尝试控制TLC5940 LED驱动器:这些芯片需要外部时钟以及在时钟周期结束时进行复位。


如果您有调试器,请尝试在计时器过快时停止代码,然后读回该计时器的配置寄存器。一旦找到错误的值,就在此寄存器更改上触发一个断点,并查看代码的哪一部分执行错误。我猜问题出在您可以使用的外部库中,并且该计时器将计时器用于内部内容(例如延迟)。
Blup1980

两个问题:a)我没有JTAG程序员,所以我没有调试芯片的方法b)在上面显示的设置之后,我从不更改定时器寄存器的值,因此我不希望定时器寄存器的值真正改变。天真吗?
angelatlarge 2013年

1
实际上,您使用的一个库可能会更改UART设置。我看到您使用sendSerial()函数。它是代码的一部分还是外部库?更改设置的可能不是您,而是更改的库中的一段代码。我建议您使用串行端口输出配置参数,并尝试找出更改的内容。您还可以查看已使用库的来源(如果有),并确保它们也没有使用该计时器。
Blup1980

1
除了@ Blup1980提出的建议之外,还有另一件事可能值得一试:删除TLC5940,以确保它不会对时钟线产生任何影响。
–'PeterJ

@ Blup1980我不确定我是否看到UART的相关性:我不是将USART用于SPI,而只是使用“常规” SPI工具。sendSerial()我的代码是通过SPI发送数据的:它不触摸TCCR(定时器控制)寄存器。
angelatlarge

Answers:


1

为了进行快速调试,我将尝试使用TLC5940的Arduino库做同样的事情,看看它是否变快了。如果它与库兼容,则可以检查其来源并与您的来源进行比较。由于您熟悉AVR,因此应该轻松地将Arduino源转换为本地AVR。

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

希望能帮助到你

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.