你的代码有问题
中断逻辑错误:定时器 0 中断中使用if(flag=20)和if(flag=0)(赋值语句而非判断语句),导致倒计时逻辑完全错乱,无法正常递减。
倒计时边界处理缺失:未处理num减至 0 后的复位逻辑,且flag=60的赋值无意义,导致数值溢出或停滞。
动态扫描时序不合理:主函数中无延时直接切换数码管位选,扫描频率过快,肉眼无法识别,表现为 “不显示” 或 “显示模糊”。
数码管段码可能不匹配:若使用共阳极数码管,当前段码表(共阴极)会导致显示异常,需确认硬件连接类型。
- #include"stc8.h"
- #include "intrins.h"
- #define uint unsigned int
- #define uchar unsigned char
- #define SEG P1 // 数码管段选引脚
- sbit SCON1 = P3^7; // 个位数码管位选
- sbit SCON2 = P3^6; // 十位数码管位选
- sbit KEY = P3^2; // 按键(预留,可用于启动/复位)
- uchar count = 0; // 数码管扫描计数
- char num = 60; // 倒计时初始值(0-60)
- uchar ge, shi; // 个位、十位拆分值
- uchar flag = 0; // 定时器计数标志
- // 共阴极数码管段码表(0-9),若为共阳极需改为0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xF6
- uchar code table[] = {
- 0x03,/*0*/ 0x9F,/*1*/ 0x25,/*2*/ 0x0D,/*3*/ 0x99,/*4*/
- 0x49,/*5*/ 0x41,/*6*/ 0x1F,/*7*/ 0x01,/*8*/ 0x09,/*9*/
- };
- // 定时器0初始化:5毫秒@11.0592MHz(12T模式)
- void T0_init() {
- AUXR &= 0x7F; // 定时器时钟12T模式
- TMOD &= 0xF0; // 清除定时器0模式
- TMOD |= 0x01; // 定时器0工作模式1(16位定时)
- TL0 = 0x00; // 定时5ms初始值(11.0592MHz计算得出)
- TH0 = 0xEE; // 重载值:TH0=0xEE, TL0=0x00,单次定时约5ms
- TF0 = 0; // 清除溢出标志
- TR0 = 1; // 启动定时器0
- ET0 = 1; // 使能定时器0中断
- EA = 1; // 开启总中断
- }
- // IO口初始化:P1推挽输出(段选),P3准双向(位选/按键)
- void IO_init() {
- P3M0 = 0x00; P3M1 = 0x00; // P3口准双向模式
- P1M0 = 0xFF; P1M1 = 0x00; // P1口推挽输出(驱动数码管)
- SCON1 = 1; SCON2 = 1; // 初始位选关闭(避免串扰)
- }
- // 毫秒级延时函数(用于数码管动态扫描,调整显示清晰度)
- void Delay_ms(uint ms) {
- uint i, j;
- for(i = ms; i > 0; i--)
- for(j = 110; j > 0; j--);
- }
- // 定时器0中断服务函数:5ms触发一次,累计1秒递减num
- void TM0_Isr() interrupt 1 {
- TL0 = 0x00; // 重新加载定时初始值
- TH0 = 0xEE;
- flag++; // 每5ms计数+1
-
- // 累计20次=100ms?修正:200次=1000ms(1秒),原逻辑计时过快
- if (flag == 200) {
- flag = 0; // 计数清零
- num--; // 每秒递减1
- if (num < 0) { // 倒计时结束(0→-1),复位为60
- num = 60;
- }
- }
- }
- void main() {
- IO_init(); // IO口初始化
- T0_init(); // 定时器0初始化
-
- while (1) {
- // 拆分十位和个位(num范围0-60)
- shi = num / 10; // 十位:60→6,0→0
- ge = num % 10; // 个位:60→0,9→9
-
- // 动态扫描显示:个位→十位,每段延时5ms,避免闪烁
- switch (count) {
- case 0: // 显示个位
- SCON1 = 0; // 开启个位数码管
- SCON2 = 1; // 关闭十位数码管
- SEG = table[ge]; // 输出个位段码
- Delay_ms(5); // 保持显示,确保肉眼识别
- break;
- case 1: // 显示十位
- SCON1 = 1; // 关闭个位数码管
- SCON2 = 0; // 开启十位数码管
- SEG = table[shi]; // 输出十位段码
- Delay_ms(5); // 保持显示
- break;
- default:
- count = 0; // 重置扫描计数
- break;
- }
- count++;
- if (count > 1) {
- count = 0; // 仅两位数码管,循环扫描
- }
- }
- }
复制代码
1. 修复中断逻辑错误
将中断中的if(flag=20)改为if(flag==200)(判断语句而非赋值),且修正计时逻辑:5ms×200=1000ms(1 秒),确保每秒递减 1。
增加num<0的边界处理,倒计时结束后自动复位为 60,实现循环倒计时。
2. 优化数码管动态扫描
新增Delay_ms()函数,在每位数码管显示后延时 5ms,降低扫描频率,使肉眼能清晰识别数字(原代码无延时,扫描过快导致 “不显示”)。
初始位选设置为SCON1=1、SCON2=1(关闭状态),避免初始化时数码管串扰。
3. 修正数码管段码适配
段码表标注共阴极 / 共阳极差异,若硬件使用共阳极数码管,需替换段码表(注释中已给出共阳极段码),否则会出现 “全亮” 或 “不显示”。
4. 完善 IO 口初始化
明确 P1 口为推挽输出(驱动数码管段选),P3 口为准双向模式(位选和按键输入),匹配硬件驱动需求。 |