PIC16F877A C语言编写0-36循环计数器代码报错问题求助
PIC16F877A数码管计数器功能异常排查
问题背景
- 开发环境:MPLAB-X + Proteus,C语言开发,主控芯片为PIC16F877A
- 预期功能:按键触发累加计数,从0开始计数到36后自动归零循环,两位数码管显示当前数值
- 异常情况:归零逻辑未按预期生效,已附芯片位配置、运行报错截图供参考
原实现代码
#include <xc.h> #define _XTAL_FREQ 4000000 // CONFIG #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) int ar = 0; int de = 0, un = 0, A = 0; int Display[10] = {0XC0, 0XF9, 0XA4, 0XB0, 0X99, 0X92, 0X82, 0XF8, 0X80, 0X98}; void visualizacion(void); void incremento(void); void main(void) { TRISC = 0X00; // PUERTO C COMO SALIDA TRISB = 0X00; // PUERTO B SALIDA TRISDbits.TRISD0 = 1; // PUERTO D0 COMO ENTRADA while (1) { if (PORTDbits.RD0 == 1) { ar = 1; } if (PORTDbits.RD0 == 0 && ar == 1) { un = un+1; ar = 0; } visualizacion(); incremento(); } } void visualizacion(void) { PORTC = 0X01; PORTB = Display[un]; __delay_us (500); PORTC = 0X02; PORTB = Display[de]; __delay_us (500); } void incremento(void) { if (un >= 10) { un = 0; de = de+1; } if(de >= 3 && un >= 6) { de = 0; } }
问题根因
- 归零逻辑错误
原计数处理函数中归零判断条件为de >=3 && un >=6,存在两个缺陷:一是使用>=判断会导致十位为3时,个位为6/7/8/9都会触发归零,不符合计数到36才复位的要求;二是触发判断后仅将十位de置0,未复位个位un,触发后会直接显示06而非0,逻辑不完整。 - 进位与归零逻辑时序冲突
原逻辑先处理个位进位,再判断归零:当计数到39后再次触发计数,un先自增到10,触发进位逻辑将un置0、de自增为4,此时un为0永远无法满足un>=6的判断条件,会出现十位数值溢出、数码管显示乱码的问题。 - 硬件配置隐患
配置位中FOSC = HS适配4MHz以上高速晶振,若使用4MHz晶振应选择XT模式,否则可能出现晶振不起振、程序完全不运行的问题;关闭上电延时PWRTE = OFF也会增加上电启动失败的概率。 - 编译失败诱因
代码中定义了变量A但全程未使用,若MPLAB-X开启了"警告视为错误"的编译选项,会直接触发编译报错。 - 按键无消抖处理
现有按键检测逻辑未做消抖,机械按键的触点抖动会导致一次按压触发多次计数,数值跳变不符合预期。
修正后可运行代码
#include <xc.h> #define _XTAL_FREQ 4000000 // 配置位修正:4MHz晶振适配XT模式,开启上电延时、掉电复位提升稳定性 #pragma config FOSC = XT #pragma config WDTE = OFF #pragma config PWRTE = ON #pragma config BOREN = ON #pragma config LVP = OFF #pragma config CPD = OFF #pragma config WRT = OFF #pragma config CP = OFF unsigned char key_flag = 0; unsigned char de = 0, un = 0; // 段码表加const修饰,存入Flash节省RAM const unsigned char Display[10] = {0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X98}; void display_scan(void); void count_proc(void); void main(void) { TRISC = 0X00; TRISB = 0X00; TRISDbits.TRISD0 = 1; PORTC = 0; PORTB = 0; while (1) { // 按键检测+10ms软件消抖 if (PORTDbits.RD0 == 1 && key_flag == 0) { __delay_ms(10); if(PORTDbits.RD0 == 1) { key_flag = 1; un++; } } // 松手检测 if (PORTDbits.RD0 == 0 && key_flag == 1) { __delay_ms(10); if(PORTDbits.RD0 == 0) { key_flag = 0; } } count_proc(); display_scan(); } } void display_scan(void) { PORTC = 0X01; PORTB = Display[un]; __delay_us(500); PORTC = 0X02; PORTB = Display[de]; __delay_us(500); } void count_proc(void) { // 个位进位处理 if (un >= 10) { un = 0; de++; } // 直接通过总数值判断归零,逻辑简单不易出错 if(de*10 + un >= 36) { de = 0; un = 0; } }
优化建议
- 主循环中可连续执行2-3次数码管扫描,避免逻辑处理占用时间导致数码管亮度低、闪烁
- 若需要显示36后下次按键再归零,只需将归零判断的阈值从36改为37即可
- PIC16F877A为8位单片机,优先使用
unsigned char类型存储0-255范围的变量,比int类型运行效率更高、更节省RAM - RD口无内部上拉,按键硬件电路建议外接10k下拉电阻,避免引脚悬空导致电平检测异常
内容的提问来源于stack exchange,提问作者boogiemane908
相关产品推荐
相关产品推荐

