PIC18F6520使用Timer2中断实现LED闪烁故障排查求助
Timer2中断实现LED闪烁故障排查
问题描述
尝试通过Timer2中断实现LED闪烁功能,此前不使用中断时LED可正常闪烁,但目前该功能无法运行。已配置外设中断,设置PR2寄存器与TMR2相等时触发中断标志并启动中断;为保证LED闪烁可见,在中断函数中添加了延时(临时操作,无延时闪烁过快无法观测)。代码如下:
// PIC18F6520 Configuration Bit Settings // 'C' source line config statements // CONFIG1H #pragma config OSC = HS // Oscillator Selection bits (HS oscillator) #pragma config OSCS = OFF // Oscillator System Clock Switch Enable bit (Oscillator system clock switch option is disabled (main oscillator is source)) // CONFIG2L #pragma config PWRT = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOR = OFF // Brown-out Reset Enable bit (Brown-out Reset disabled) #pragma config BORV = 25 // Brown-out Reset Voltage bits (VBOR set to 2.5V) // CONFIG2H #pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit)) #pragma config WDTPS = 128 // Watchdog Timer Postscale Select bits (1:128) // CONFIG3L // CONFIG3H #pragma config CCP2MUX = OFF // CCP2 Mux bit (CCP2 input/output is multiplexed with RE7) // CONFIG4L #pragma config STVR = OFF // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset) #pragma config LVP = OFF // Low-Voltage ICSP Enable bit (Low-voltage ICSP disabled) // CONFIG5L #pragma config CP0 = OFF // Code Protection bit (Block 0 (000800-001FFFh) not code-protected) #pragma config CP1 = OFF // Code Protection bit (Block 1 (002000-003FFFh) not code-protected) #pragma config CP2 = OFF // Code Protection bit (Block 2 (004000-005FFFh) not code-protected) #pragma config CP3 = OFF // Code Protection bit (Block 3 (006000-007FFFh) not code-protected) // CONFIG5H #pragma config CPB = OFF // Boot Block Code Protection bit (Boot Block (000000-0007FFh) not code-protected) #pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected) // CONFIG6L #pragma config WRT0 = OFF // Write Protection bit (Block 0 (000800-001FFFh) not write-protected) #pragma config WRT1 = OFF // Write Protection bit (Block 1 (002000-003FFFh) not write-protected) #pragma config WRT2 = OFF // Write Protection bit (Block 2 (004000-005FFFh) not write-protected) #pragma config WRT3 = OFF // Write Protection bit (Block 3 (006000-007FFFh) not write-protected) // CONFIG6H #pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected) #pragma config WRTB = OFF // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected) #pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected) // CONFIG7L #pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks) #pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks) #pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks) #pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks) // CONFIG7H #pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include <xc.h> #define _XTAL_FREQ 8000000 void main(void) { TRISC = 0x00; // Initialise PORTC as all 0's, LED on PORTC RC2. // Setup Timer2 1:1 post and pre scaler. T2CONbits.T2CKPS = 0b00; T2CONbits.T2OUTPS = 0b0000; T2CONbits.TMR2ON = 1; // Turn on Timer2. PR2 = 15; // Set PR2 register to jump to interrupt when equal to TMR2 register PIE1bits.TMR2IE = 1; // Initialise Timer2 interrupt enable. INTCONbits.PEIE = 1; // Setup peripheral interrupts. // Run main loop for ever. while(1){ } } // Interrupt routine. void __interrupt() isr(void) { // Check to see if TMR2 interrupt flag set. if(PIR1bits.TMR2IF){ PIR1bits.TMR2IF = 0; // Reset TMR2 interrupt flag. PORTCbits.RC2 ^= 1; // Toggle RC0. __delay_ms(500); // Delay Toggle to make it possible to see the LED flash. } }
故障原因及解决方法
1. 全局中断未启用
PIC18系列中,仅开启外设中断允许位INTCONbits.PEIE无法触发中断,必须同时开启全局中断允许位INTCONbits.GIE,这是中断触发的必要条件。
解决方法:在main函数中添加全局中断启用代码:
INTCONbits.PEIE = 1; // Setup peripheral interrupts. INTCONbits.GIE = 1; // 启用全局中断,允许所有已开启的中断触发
2. 中断服务程序中使用延时函数
中断服务程序(ISR)必须遵循短平快的原则,__delay_ms(500)会阻塞系统500ms,期间无法响应其他中断,主循环也会完全停滞,属于严重的设计错误。
解决方法:移除中断内的延时,通过调整Timer2的分频器和PR2值,让中断周期匹配LED闪烁需求。例如要实现500ms一次中断:
- 晶振8MHz,Timer2时钟源为
Fosc/4=2MHz - 设置预分频
T2CKPS=0b11(1:16),后分频T2OUTPS=0b1111(1:16),Timer2计数频率为2MHz/16=125kHz,单个计数周期8us - 500ms所需计数次数:
500ms / 8us / 16 = 3906,因此PR2设为3905(TMR2从0计数到PR2时触发匹配)
修改Timer配置代码:
// Setup Timer2 1:16 pre-scaler, 1:16 post-scaler T2CONbits.T2CKPS = 0b11; T2CONbits.T2OUTPS = 0b1111; T2CONbits.TMR2ON = 1; // Turn on Timer2. PR2 = 3905; // 配置为约500ms的中断周期
中断函数简化为:
void __interrupt() isr(void) { if(PIR1bits.TMR2IF){ PIR1bits.TMR2IF = 0; // 清除中断标志 PORTCbits.RC2 ^= 1; // 翻转LED引脚 } }
3. 注释与代码不一致
代码中注释标注// Toggle RC0,但实际操作的是PORTCbits.RC2,虽不影响功能,但易造成混淆,建议修正注释:
PORTCbits.RC2 ^= 1; // Toggle RC2 (LED pin)
4. Timer2中断频率过高导致LED无可见闪烁
原代码中PR2=15、分频比1:1,中断周期仅8us,每秒触发125000次,LED翻转频率远超人类视觉分辨范围,即使中断正常触发,LED也会呈现常亮状态。通过调整分频器和PR2值可解决此问题,参考第2点的配置即可。
内容的提问来源于stack exchange,提问作者ProgramMasher
相关产品推荐
相关产品推荐

