You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于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_milharesdigito_centenasdigito_dezenasdigito_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_minutosbutt_horas,但也没看到这些变量的声明,虽然现在时钟计数的问题和按键无关,但也要补全避免编译警告。
  • 检查Proteus仿真配置:确认PIC18F445K22的时钟配置和代码一致(4MHz内部振荡器),数码管的接线是否正确(段码接PORTC,位选接PORTB对应引脚),有没有接对限流电阻。

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

火山引擎 最新活动