You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 23:45:53