|
| 调整中断时间。实物也一样。用RTC芯片会简单有效提高精度。 |
| 定时器中断中每10ms增加count,优化timer_init(); |
rana_0222 发表于 2026-3-13 00:06 用模拟示波器看一下中断频率是否正确。有可能MCU属性中时钟选择错误,有可能中断程序拥堵,也有可能仿真软件版本过低。 |
| #include <reg51.h> #include <absacc.h> // 8255端口地址定义(基于P2.5=A0, P2.6=A1, P2.7=CS,低有效) #define PA8255 XBYTE[0x0000] // A口:P2.7=0, P2.6=0, P2.5=0 #define PB8255 XBYTE[0x2000] // B口:P2.7=0, P2.6=0, P2.5=1 #define PC8255 XBYTE[0x4000] // C口:P2.7=0, P2.6=1, P2.5=0 #define COM8255 XBYTE[0x6000] // 控制口:P2.7=0, P2.6=1, P2.5=1 // 共阴极数码管段码表(0-9,空白) code unsigned char seg_code[] = { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F, // 9 0x00 // 空白 }; // 按键位定义(PC0~PC5) #define KEY_START_PAUSE 0x01 #define KEY_RESET 0x02 #define KEY_SAVE 0x04 #define KEY_QUERY 0x08 #define KEY_PREV 0x10 #define KEY_NEXT 0x20 // 全局变量 unsigned long count = 0; // 改为long型,支持0~9分59.99秒,避免溢出 bit running = 0; // 计时运行标志 unsigned long record[10] = {0}; // 记录数组同步改为long型,保存分钟+秒信息 unsigned char record_count = 0; // 已存记录数(0~10) bit mode = 0; // 0=计时模式,1=查询模式 unsigned char query_index = 0; // 当前查询的记录索引(0~9) unsigned char display_buffer[8]; // 显示缓冲区 // 按键扫描相关 unsigned char key_prev = 0; bit key_scan_flag = 0; // 10ms扫描标志 // 定时器0中断服务程序(10ms) void timer0_isr(void) interrupt 1 { TH0 = 0xD8; // 12MHz,10ms初值 TL0 = 0xF0; if (running) { count++; // 取消99.99秒清零,累计为分钟,最大支持9分59.99秒 if (count >= 5999) {} } key_scan_flag = 1; // 通知主循环扫描按键 } // 定时器0初始化 void timer_init() { TMOD &= 0xF0; TMOD |= 0x01; // 定时器0,模式1 TH0 = 0xD8; TL0 = 0xF0; ET0 = 1; EA = 1; TR0 = 1; } // 更新显示缓冲区(核心修改:分钟位加分隔符,分·秒清晰区分) void update_display() { unsigned char i; unsigned char buf[8]; unsigned char minute; // 分钟(1位,0~9) unsigned int sec_total; // 剩余秒数(0~5999,对应00.00~59.99秒) unsigned int int_part, frac_part; // 秒的整数部分、小数部分 unsigned char tens, ones, tenths, hundredths; unsigned long val; for (i = 0; i < 8; i++) buf[i] = 0x00; // 先全灭 if (mode == 0) { // 计时模式 // 第1、2位:已存记录数(两位,不足补零) buf[0] = seg_code[record_count / 10]; buf[1] = seg_code[record_count % 10]; buf[2] = 0x00; // 第3位空白 // 计算分钟和剩余秒数 minute = count / 6000; // 6000个0.01秒=60秒=1分钟,取个位分钟 sec_total = count % 6000; // 剩余秒数(0~5999) // 拆分剩余秒数:整数部分(秒)=sec_total/100,小数部分=sec_total%100 int_part = sec_total / 100; frac_part = sec_total % 100; tens = int_part / 10; // 秒的十位(0~5) ones = int_part % 10; // 秒的个位(0~9) tenths = frac_part / 10; // 秒的十分位 hundredths = frac_part % 10;// 秒的百分位 // 第4~8位:分钟(1位+分隔符)+ 秒(99.99格式)【核心修改】 buf[3] = seg_code[minute] | 0x80; // 分钟位+右侧小数点(分隔符),分·秒 buf[4] = seg_code[tens]; // 第5位:秒十位 buf[5] = seg_code[ones] | 0x80; // 第6位:秒个位+小数点 buf[6] = seg_code[tenths]; // 第7位:秒十分位 buf[7] = seg_code[hundredths]; // 第8位:秒百分位 } else { // 查询模式【同步修改分钟位分隔符】 if (record_count > 0) { // 显示当前记录序号(从1开始) buf[0] = seg_code[(query_index + 1) / 10]; buf[1] = seg_code[(query_index + 1) % 10]; buf[2] = 0x00; // 显示记录值(同步支持分钟+分隔符) val = record[query_index]; minute = val / 6000; // 记录的分钟 sec_total = val % 6000; // 记录的剩余秒数 // 拆分剩余秒数 int_part = sec_total / 100; frac_part = sec_total % 100; tens = int_part / 10; ones = int_part % 10; tenths = frac_part / 10; hundredths = frac_part % 10; buf[3] = seg_code[minute] | 0x80; // 分钟位+分隔符(和计时模式一致) buf[4] = seg_code[tens]; buf[5] = seg_code[ones] | 0x80; buf[6] = seg_code[tenths]; buf[7] = seg_code[hundredths]; } else { // 如果没有记录,返回计时模式 mode = 0; } } // 复制到全局显示缓冲区 for (i = 0; i < 8; i++) display_buffer[i] = buf[i]; } // 动态扫描显示(位选低电平有效,无修改) void display() { unsigned char i; unsigned int j; for (i = 0; i < 8; i++) { PB8255 = 0xFF; // 消隐,关闭所有位 PA8255 = display_buffer[i]; // 送段码 PB8255 = ~(1 << i); // 选中第i位(i=0对应最左边) // 短暂延时 for (j = 0; j < 100; j++); } } // 按键扫描 void scan_key() { unsigned char key_current, key_down; unsigned char i; key_current = ~PC8255 & 0x3F; // 读PC0~PC5,取反得按下为1 key_down = key_current & (key_current ^ key_prev); key_prev = key_current; // 处理按键事件 if (key_down & KEY_START_PAUSE) { if (mode == 0) { running = !running; // 计时模式下开始/暂停 } } // 复位按键 if (key_down & KEY_RESET) { running = 0; // 停止计时 count = 0; // 清零当前值(分钟+秒都清零) if (mode == 1) { // 如果在查询模式,退出查询 mode = 0; } else { // 在计时模式下,清除所有记录 record_count = 0; for (i = 0; i < 10; i++) { record[i] = 0; } } } // 保存按键 if (key_down & KEY_SAVE) { if (mode == 0) { // 计时模式下保存 if (record_count < 10) { record[record_count] = count; // 保存当前count(含分钟信息) record_count++; } } else { // 查询模式下清除所有记录 record_count = 0; mode = 0; // 退出查询 } } // 查询按键 - 修改为点击一次直接进入查询模式 if (key_down & KEY_QUERY) { if (mode == 0) { // 当前在计时模式 if (record_count > 0) { // 有记录可查 mode = 1; // 直接切换到查询模式 query_index = 0; // 指向第一个记录 } } else { // 当前在查询模式 mode = 0; // 退出查询,返回计时模式 } } // 上一个按键 if (key_down & KEY_PREV) { if (mode == 1 && record_count > 0) { // 查询模式下切换上一个记录 if (query_index == 0) query_index = record_count - 1; else query_index--; } } // 下一个按键 if (key_down & KEY_NEXT) { if (mode == 1 && record_count > 0) { // 查询模式下切换下一个记录 query_index = (query_index + 1) % record_count; } } } // 主函数(无修改) void main() { // 初始化8255:方式0,A口输出,B口输出,C口输入 COM8255 = 0x89; // 1000 1001B timer_init(); // 初始化定时器 // 初始状态 count = 0; running = 0; record_count = 0; mode = 0; key_prev = 0; while (1) { if (key_scan_flag) { // 每10ms扫描一次按键 key_scan_flag = 0; scan_key(); } update_display(); // 更新显示内容 display(); // 动态扫描显示 } } |