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

PIC18F67K22中uint8_t类型sec变量赋值低数值时程序崩溃求助

PIC18F67K22时钟程序sec变量异常问题
  • 毫秒定时器每毫秒触发标志,每1000毫秒重置ms变量功能正常;
  • 尝试每10秒(调试用)重置sec变量时,程序直接停止运行;
  • 将sec赋值为小于10的数值就会导致崩溃,赋值大于10则正常,执行sec--操作也会触发崩溃。

问题定位在CLK_ISR中断函数中,以下是最小复现代码:

main.c

#include "fuses.h"

#include <stdbool.h>
#include <stdio.h>
#include <xc.h>

#include "clock.h"
#include "interrupt.h"
#include "LCDSerialTerminalMaster.h"

uint8_t prevSec = 0;

void main(void)
{
    // disable comparators
    CM1CONbits.CON = 0;
    CM2CONbits.CON = 0;
    CM3CONbits.CON = 0;
    // disable analog input
    ADCON0bits.ADON = false;
    ANCON0 = 0;
    ANCON1 = 0;
    ANCON2 = 0;
    
    INT_Init();
    CLK_Init();
    LCD_Init();

    
    while(1)
    {
        if(CLK_GetSec() != prevSec)
        {
            prevSec = CLK_GetSec();
            
            LCD_Write();
        }
    }
    
}

interrupt.c

#include <stdbool.h>

#include "interrupt.h"

#include "clock.h"
#include "pin.h"
void INT_Init(void)
{
    // Enable Interrupt Priority Vectors
    IPEN = 1;

    // RCI - high priority
    UART1_RC_IP = 1;
    UART2_RC_IP = 1;
    
    UART1_TX_IP = 0;
    UART2_TX_IP = 0;

    MASTER_SYNC_IP = 1;
    
    INT_Toggle(true);
}

void INT_Toggle(bool toggle)
{
    INT_ToggleHigh(toggle);
    INT_ToggleLow(toggle);
}

void INT_ToggleHigh(bool toggle)
{
    INTCONbits.GIEH = toggle;
}

void INT_ToggleLow(bool toggle)
{
    INTCONbits.GIEL = toggle;
}

void __interrupt() INT_InterruptManagerHigh(void)
{
    
    if(PIR4bits.CCP4IF)
        CLK_ISR();
    
}

clock.c

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <xc.h>

static uint8_t hour = 0;
static uint8_t min = 0;
static uint8_t sec = 0;
static volatile uint16_t ms = 0;

void CLK_Init(void)
{
    T3CONbits.TMR3CS = 0b00; // Source = Fosc/4
    T3CONbits.SOSCEN = 0b1;  // SOSC disabled
    T3CONbits.T3CKPS = 0b00; // Prescaler 1:1

    CCP4CONbits.CCP4M = 0b1011; // Compare Mode: Special Event Trigger, reset TMR3 on CCP4 match
    CCPTMRS1bits.C4TSEL = 0b01; // TMR3/TMR4

    // set compare value to 5000
    CCPR4L = 136;
    CCPR4H = 19;

    // Disable TMR3 interrupt.
    PIE2bits.TMR3IE = 0;

    // clear CCP4 interrupt.
    PIR4bits.CCP4IF = 0;

    // enable CCP4 interrupt.
    PIE4bits.CCP4IE = 1;
    IPR4bits.CCP4IP = 1;

    // Start the Timer by writing to TMRxON bit.
    T3CONbits.TMR3ON = 1;
}

void CLK_GetTime(char *buffer)
{
    sprintf(buffer, "%02hhu:%02hhu:%02hhu", hour, min, sec);
}

uint8_t CLK_GetSec(void)
{
    return sec;
}

void CLK_ISR(void)
{   
    PIR4bits.CCP4IF = 0;
    ms++;
    if(ms == 1000)
    {
        ms = 0;
        sec++;
        if(sec == 10)
        {
            sec = 0;
            min++;
            if(min == 60)
            {
                min = 0;
                hour++;
                if(hour == 24)
                    hour = 0;
            }
        }
    }
}

问题根源分析

  1. 变量未加volatile修饰:sec、min、hour是中断函数会修改的变量,但未声明为volatile,编译器会对这些变量做缓存优化,导致主程序读取到旧值,引发逻辑混乱。
  2. 无中断保护的变量读写:主程序读取sec时,若中断触发修改该变量,可能出现读取不完整的情况,尤其在编译器优化后,这种异步访问会导致系统行为异常。
  3. 定时器配置误差:当前CCPR4配置值对应2.5ms触发一次中断(而非预期的1ms),导致ms计数逻辑错误,间接引发时间更新逻辑混乱。
  4. 高频LCD操作阻塞:当sec频繁变化时,LCD_Write()被高频调用,若该函数耗时较长,会阻塞中断响应,最终导致系统崩溃。

修复方案

1. 为中断变量添加volatile修饰

修改clock.c中的变量声明,确保编译器不会缓存这些异步修改的变量:

static volatile uint8_t hour = 0;
static volatile uint8_t min = 0;
static volatile uint8_t sec = 0;
static volatile uint16_t ms = 0;

2. 读取变量时添加中断保护

在读取中断修改的变量前,临时关闭高优先级中断,避免读写冲突:

uint8_t CLK_GetSec(void)
{
    uint8_t temp_sec;
    INTCONbits.GIEH = 0; // 关闭高优先级中断
    temp_sec = sec;
    INTCONbits.GIEH = 1; // 恢复中断
    return temp_sec;
}

void CLK_GetTime(char *buffer)
{
    uint8_t temp_hour, temp_min, temp_sec;
    INTCONbits.GIEH = 0;
    temp_hour = hour;
    temp_min = min;
    temp_sec = sec;
    INTCONbits.GIEH = 1;
    sprintf(buffer, "%02hhu:%02hhu:%02hhu", temp_hour, temp_min, temp_sec);
}

3. 修正定时器CCPR配置

假设系统时钟为8MHz,要实现1ms触发一次中断,CCPR4应设置为2000(对应0x07D0):

// 替换原CCPR赋值代码
CCPR4L = 0xD0;
CCPR4H = 0x07;

4. 优化LCD更新逻辑

如果LCD_Write()耗时较长,可将显示数据存入缓冲区,在主循环中分批处理,避免长时间阻塞中断响应。

内容的提问来源于stack exchange,提问作者Wesley Kienhuis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 20:50:23