使用 ATmega48PA 中断时的未定义行为
Undefined behavior when using interrupts with ATmega48PA
我正在构建一个简单的 Timer/Counter 应用程序,它使用 Atmel 的 ATmega48PA 中的正常模式生成延迟,使用 Timer1,以恒定的时间间隔切换 LED。当使用中断时会发生什么情况,LED 会切换一定时间,然后切换效果停止,让 LED 始终亮起!
我相信 sei() 函数或在 SREG 中启用全局中断是有问题的,因为我在使用中断之前在同一个微控制器上遇到过这种行为。
这是随我的问题一起提供的代码片段,尽管任何人都会认为这段代码非常正常并且必须正常工作!
#include <avr/io.h>
#include <atmel_start.h>
#include <util/delay.h>
#include <math.h>
#include <clock_config.h>
#include "avr/iom48pa.h"
#define LOAD_VALUE 49911UL
#define SET_BIT( REG, BIT ) REG |= ( 1 << BIT )
#define CLR_BIT( REG, BIT ) REG &= ~( 1 << BIT )
#define TOG_BIT( REG, BIT ) REG ^= ( 1 << BIT )
void Timer16_Init( void );
void Timer16_DelayMS( unsigned short delayMS );
unsigned short delayMS = 50;
ISR( TIMER1_OVF_vect ){
Disable_global_interrupt();
TOG_BIT( PORTC, 2 );
TOG_BIT( PORTC, 3 );
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
Enable_global_interrupt();
}
int main( void ){
/* Initializes MCU, drivers and middle ware */
atmel_start_init();
/* configure pin 2 and pin 3 in PORTC as output */
SET_BIT( DDRC, 3 );
SET_BIT( DDRC, 2 );
Enable_global_interrupt();
Timer16_Init();
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
while( 1 ){
}
}
void Timer16_Init( void ){
SET_BIT( TCCR1B, CS10 );
SET_BIT( TCCR1B, CS12 );
SET_BIT( TIMSK1, TOIE1 );
}
我只想知道,这里到底发生了什么?!
乍一看,你的代码没有什么特别的问题。那么让我们检查一下可能性:
首先你做了一些32位长的计算并将结果放入16位寄存器:
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
它会导致在寄存器中输入不可预测的值。所以我建议您使用适当的值或在您的代码中使用 (long) 和 (int) 来防止数据溢出。
第二个你没有输入正确的数据行顺序:
Enable_global_interrupt();
Timer16_Init();
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
您启用了中断,然后初始化了计时器,然后应用了计时器的值。这是不正确的,因为定时器运行并能够产生中断,但它的值尚未设置。顺序必须是这样的:
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
Enable_global_interrupt();
Timer16_Init();
第三你已经在正常模式下使用了定时器,进入溢出中断并在中断例程中设置定时器值。我强烈建议您改用比较模式,因为它不需要在中断例程中设置定时器值。
我正在构建一个简单的 Timer/Counter 应用程序,它使用 Atmel 的 ATmega48PA 中的正常模式生成延迟,使用 Timer1,以恒定的时间间隔切换 LED。当使用中断时会发生什么情况,LED 会切换一定时间,然后切换效果停止,让 LED 始终亮起! 我相信 sei() 函数或在 SREG 中启用全局中断是有问题的,因为我在使用中断之前在同一个微控制器上遇到过这种行为。
这是随我的问题一起提供的代码片段,尽管任何人都会认为这段代码非常正常并且必须正常工作!
#include <avr/io.h>
#include <atmel_start.h>
#include <util/delay.h>
#include <math.h>
#include <clock_config.h>
#include "avr/iom48pa.h"
#define LOAD_VALUE 49911UL
#define SET_BIT( REG, BIT ) REG |= ( 1 << BIT )
#define CLR_BIT( REG, BIT ) REG &= ~( 1 << BIT )
#define TOG_BIT( REG, BIT ) REG ^= ( 1 << BIT )
void Timer16_Init( void );
void Timer16_DelayMS( unsigned short delayMS );
unsigned short delayMS = 50;
ISR( TIMER1_OVF_vect ){
Disable_global_interrupt();
TOG_BIT( PORTC, 2 );
TOG_BIT( PORTC, 3 );
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
Enable_global_interrupt();
}
int main( void ){
/* Initializes MCU, drivers and middle ware */
atmel_start_init();
/* configure pin 2 and pin 3 in PORTC as output */
SET_BIT( DDRC, 3 );
SET_BIT( DDRC, 2 );
Enable_global_interrupt();
Timer16_Init();
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
while( 1 ){
}
}
void Timer16_Init( void ){
SET_BIT( TCCR1B, CS10 );
SET_BIT( TCCR1B, CS12 );
SET_BIT( TIMSK1, TOIE1 );
}
我只想知道,这里到底发生了什么?!
乍一看,你的代码没有什么特别的问题。那么让我们检查一下可能性:
首先你做了一些32位长的计算并将结果放入16位寄存器:
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
它会导致在寄存器中输入不可预测的值。所以我建议您使用适当的值或在您的代码中使用 (long) 和 (int) 来防止数据溢出。
第二个你没有输入正确的数据行顺序:
Enable_global_interrupt();
Timer16_Init();
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
您启用了中断,然后初始化了计时器,然后应用了计时器的值。这是不正确的,因为定时器运行并能够产生中断,但它的值尚未设置。顺序必须是这样的:
TCNT1 = ( ( 4194304 - delayMS ) * 1000 ) / 64;
Enable_global_interrupt();
Timer16_Init();
第三你已经在正常模式下使用了定时器,进入溢出中断并在中断例程中设置定时器值。我强烈建议您改用比较模式,因为它不需要在中断例程中设置定时器值。