基于PIC18F445K22的七段数码管时钟仿真故障求助
PIC18F445K22七段数码管时钟仿真故障排查求助
我写了一个基于PIC18F445K22的七段数码管时钟代码,用Timer0实现数码管动态扫描,Timer1生成1秒延时。但在Proteus仿真时,只有LED在闪烁,数码管始终显示00:00完全不计数,有没有大佬能给点排查方向?
完整代码
全局变量与函数声明
int display(int num); int clock_PIC(); /*全局变量*/ char controlador = 0x01; int unidade, dezenas, centenas, milhares; char segundos = 0x00; char minutos = 0x00; char horas = 0x00; char clck_cont = 0x00; char flags_min = 0x00; char flags_hor = 0x00; // 注意:代码中使用的digito_xxxx变量未定义,需补充
中断服务函数(Timer0与Timer1)
void interrupt interrupcao () { if(INTCONbits.TMR0IF) { if(!digito_milhares && controlador == 1) { controlador = 0x02; digito_unidade = 0x00; digito_dezenas = 0x00; digito_centenas = 0x00; PORTC = 0x00; milhares = (horas%100)/10; digito_milhares = 0x01; PORTC = display(milhares); }//end if 千位数码管 else if(!digito_centenas && controlador == 2) { controlador = 0x03; digito_unidade = 0x00; digito_dezenas = 0x00; digito_milhares = 0x00; PORTC = 0x00; centenas = horas%10; digito_centenas = 0x01; PORTC = display(centenas); }//end if 百位数码管 else if(!digito_dezenas && controlador == 3) { controlador = 0x04; digito_unidade = 0x00; digito_centenas = 0x00; digito_milhares = 0x00; PORTC = 0x00; dezenas = (minutos%100)/10; digito_dezenas = 0x01; PORTC = display(dezenas); }//end if 十位数码管 else if(!digito_unidade && controlador == 4) { controlador = 0x01; digito_dezenas = 0x00; digito_centenas = 0x00; digito_milhares = 0x00; PORTC = 0x00; unidade = minutos%10; digito_unidade = 0x01; PORTC = display(unidade); }//end if 个位数码管 // 清除TMR0中断标志位 INTCONbits.TMR0IF = 0x00; }//end if TMR0IF if(PIR1bits.TMR1IF) { LATBbits.LATB7 = ~ LATBbits.LATB7; clck_cont ++; if(clck_cont == 0x02) { clck_cont = 0x00; segundos++; }//end clck_cont // 清除TMR1中断标志位 PIR1bits.TMR1IF = 0x00; //TMR1H 11; TMR1H = 0x0B; //TMR1L 220; TMR1L = 0xDC; }//end TMR1IF }//end interrupcao
主函数
void main(void) { PORTB = 0x4F; PORTC = 0x3F; /** TRISx寄存器 */ TRISB = 0x70; TRISC = 0x80; /** ANSELx寄存器 */ ANSELC = 0x00; ANSELB = 0x00; // SCS FOSC; IRCF 4MHz_HFINTOSC/4; IDLEN 禁用; OSCCON = 0x50; // PRISD 启用; SOSCGO 禁用; MFIOSEL 禁用; OSCCON2 = 0x04; // INTSRC 禁用; PLLEN 禁用; TUN 0; OSCTUNE = 0x00; // TMR0H 0; TMR0H = 0x00; // TMR0L 6; TMR0L = 0x06; // T0PS 1:16; T08BIT 8位; T0SE 高低递增; T0CS FOSC/4; TMR0ON 启用; PSA 分配; T0CON = 0xD3; //T1GSS T1G_pin; TMR1GE 禁用; T1GTM 禁用; T1GPOL 低; T1GGO 完成; T1GSPM 禁用; T1GCON = 0x00; //TMR1H 11; TMR1H = 0x0B; //TMR1L 220; TMR1L = 0xDC; // 启用中断前清除IF标志位 // PIR1bits.TMR1IF = 0; // T1CKPS 1:8; T1OSCEN 禁用; T1SYNC 不同步; TMR1CS FOSC/4; TMR1ON 启用; T1RD16 启用; T1CON = 0x37; INTCONbits.GIE = 1; //启用全局中断 PIE1bits.TMR1IE = 1; //启用Timer1中断 INTCONbits.TMR0IE = 1; //启用Timer0中断 INTCON2bits.nRBPU = 0; //启用Port B内部上拉 while (1) { clock_PIC(); }//end while }//end void
时钟逻辑函数
int clock_PIC() { if (segundos > 59) { segundos = 0x00; minutos++; if(minutos > 59) { minutos = 0x00; horas++; if (horas > 23) { horas = 0x00; }//end horas }//end minutos }//end segundos if(!butt_minutos) flags_min = 0x01; if(!butt_horas) flags_hor = 0x01; if(butt_minutos && flags_min) { flags_min = 0x00; minutos++; if(minutos > 59) minutos = 0x00; } if(butt_horas && flags_hor) { flags_hor = 0x00; horas++; if(horas > 23) horas = 0x00; } }//end clock_PIC
数码管显示函数
int display(int num) { int cathode; //存储BCD码 // -- BCD码数组 -- int SEGMENTO[10] = { 0x3F, //BCD 零 '0' 0x06, //BCD 一 '1' 0x5B, //BCD 二 '2' 0x4F, //BCD 三 '3' 0x66, //BCD 四 '4' 0x6D, //BCD 五 '5' 0x7D, //BCD 六 '6' 0x07, //BCD 七 '7' 0x7F, //BCD 八 '8' 0x67}; //BCD 九 '9' cathode = SEGMENTO[num]; //返回阴极码 return(cathode); //返回BCD数字 } //end display
排查建议
- 补全未定义变量:代码中使用的
digito_milhares、digito_centenas、digito_dezenas、digito_unidade这些动态扫描控制变量没有在全局变量里声明,编译器会默认它们为随机值,导致扫描逻辑完全混乱。赶紧把这些变量加到全局变量区:char digito_milhares = 0, digito_centenas = 0, digito_dezenas = 0, digito_unidade = 0; - 修正Timer1的重载顺序:PIC18的16位Timer1在写入寄存器时,先写
TMR1H会锁住TMR1L的当前值,正确的重载顺序应该是先写低字节TMR1L,再写高字节TMR1H,否则会导致计时误差,clck_cont可能永远达不到2,秒数就不会递增:// 把中断里的Timer1重载代码改成 TMR1L = 0xDC; TMR1H = 0x0B; - 完善数码管位选输出:目前代码只设置了段码
PORTC,但没有把digito_xxxx变量映射到PORTB的引脚来控制数码管的选通。比如如果是共阴极数码管,需要把对应位的引脚拉低来点亮,你得在每个扫描分支里加上位选控制,比如:// 比如在千位扫描分支里,假设RB0控制千位数码管 LATBbits.LATB0 = 0; // 点亮千位 LATBbits.LATB1 = 1; LATBbits.LATB2 = 1; LATBbits.LATB3 = 1; // 然后设置段码 PORTC = display(milhares); - 验证Timer1计时是否正常:可以在Timer1中断里直接给
segundos++,去掉clck_cont的判断,看看数码管会不会开始计数,先排除计时逻辑的问题。 - 补全按键变量定义:代码里用了
butt_minutos、butt_horas,但也没看到这些变量的声明,虽然现在时钟计数的问题和按键无关,但也要补全避免编译警告。 - 检查Proteus仿真配置:确认PIC18F445K22的时钟配置和代码一致(4MHz内部振荡器),数码管的接线是否正确(段码接PORTC,位选接PORTB对应引脚),有没有接对限流电阻。
内容的提问来源于stack exchange,提问作者Ntampata




