- 打卡等级:偶尔看看I
- 打卡总天数:11
- 最近打卡:2026-04-10 01:15:45
已绑定手机
注册会员
- 积分
- 98
|
昨天帖子的数码管时钟,发现走时不准,误差很大,现在对定时器进行了修改,走时很精准,下载时选择12MHz频率。喜欢的朋友赶快复制拿走!!!
/*================================================================
功能:STC32四位数码管时钟(带冒号2秒闪烁和简化按键设置)
硬件连接:
- MCU: STC32G12K128
- 数码管: 4401AS(四位共阴时钟数码管)
- 段码: P2.0-P2.7 -> 数码管a,b,c,d,e,f,g,DP
- 位码: P3.0-P3.3 -> 数码管位选1-4
- 按键: P0.0-P0.3 -> 功能/加/减/确认键
注意:冒号通过第2位数码管的DP段以2秒周期闪烁
================================================================*/
#include "STC32G.h" // STC32头文件
#include "intrins.h"
/*============ 引脚定义 ============*/
#define SEG_PORT P2 // 段码端口(P2.0-P2.7 -> a,b,c,d,e,f,g,DP)
#define BIT_PORT P3 // 位码端口(P3.0-P3.3 -> 位选1-4)
// 按键引脚定义
sbit KEY_MODE = P0^0; // 功能/模式键
sbit KEY_ADD = P0^1; // 加键
sbit KEY_SUB = P0^2; // 减键
sbit KEY_OK = P0^3; // 确认键
/*============ 模式定义 ============*/
#define MODE_NORMAL 0 // 正常显示模式
#define MODE_SET_HOUR 1 // 设置小时模式
#define MODE_SET_MINUTE 2 // 设置分钟模式
/*============ 全局变量 ============*/
// 时间结构体
typedef struct {
unsigned char hour; // 小时 (0-23)
unsigned char minute; // 分钟 (0-59)
unsigned char second; // 秒钟 (0-59)
} TIME_TYPE;
TIME_TYPE time = {12, 0, 0}; // 初始时间 12:00:00
// 显示相关变量
unsigned char disp_buf[4] = {0}; // 显示缓冲区
bit colon_flag = 0; // 冒号显示标志
bit sec_led_on = 0; // 秒LED点亮标志
unsigned char display_mode = MODE_NORMAL; // 当前显示模式
bit blink_flag = 0; // 设置闪烁标志
unsigned int t1_counter = 0; // 定时器1计数器
unsigned int ms_counter = 0; // 毫秒计数器
// 按键相关
unsigned char key_state = 0; // 按键状态
unsigned int key_press_time = 0; // 按键按下时间
bit key_pressed = 0; // 按键按下标志
bit key_enable = 1; // 按键使能标志
unsigned int key_repeat_delay = 0; // 按键重复延时
/*============ 数码管编码表 ============*/
// 共阴数码管段码表 (0-9)
// 段码顺序:a b c d e f g DP (对应P2.0-P2.7)
const unsigned char code SEG_CODE[] = {
// 0 1 2 3 4 5 6 7 8 9
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
// 不显示
0x00
};
// 位选表
// 共阴数码管,位选低电平有效
// 假设P3.0-3.3分别控制第1-4位数码管
const unsigned char code BIT_CODE[] = {
0xFE, // 11111110 - 第1位数码管 (P3.0=0)
0xFD, // 11111101 - 第2位数码管 (P3.1=0)
0xFB, // 11111011 - 第3位数码管 (P3.2=0)
0xF7 // 11110111 - 第4位数码管 (P3.3=0)
};
/*============ 函数声明 ============*/
void System_Init(void);
void GPIO_Init(void);
void Timer0_Init(void);
void Timer1_Init(void);
void Update_Display_Buffer(void);
void Key_Scan(void);
void Key_Process(unsigned char key_val);
void Delay_ms(unsigned int ms);
/*============ 主函数 ============*/
void main(void)
{
// 系统初始化
System_Init();
// 主循环
while(1)
{
Key_Scan(); // 按键扫描
Delay_ms(10); // 增加主循环延时
}
}
/*============ 系统初始化 ============*/
void System_Init(void)
{
GPIO_Init(); // GPIO初始化
Timer0_Init(); // 定时器0初始化
Timer1_Init(); // 定时器1初始化
// 开全局中断
EA = 1;
// 初始化显示缓冲区
Update_Display_Buffer();
}
/*============ GPIO初始化 ============*/
void GPIO_Init(void)
{
// P2设置为准双向口(段码输出)
P2M0 = 0x00;
P2M1 = 0x00;
P2 = 0x00; // 初始输出低电平
// P3设置为准双向口(位码输出)
P3M0 = 0x00;
P3M1 = 0x00;
P3 = 0xFF; // 初始输出高电平(关闭所有位)
// P0设置为高阻输入(按键输入),启用内部上拉
P0M0 = 0x00;
P0M1 = 0x00;
P0 = 0xFF; // 初始化为高电平
}
/*============ 定时器0初始化 ============*/
// 用于数码管动态扫描,1ms定时
void Timer0_Init(void)
{
// 设置定时器0为模式0(16位自动重装载)
AUXR |= 0x80; // 定时器0为1T模式
TMOD &= 0xF0; // 清除定时器0模式位
// 计算1ms定时初值(假设系统时钟为12MHz)
// 1T模式下,1个机器周期 = 1/12MHz ≈ 0.08333μs
// 1ms = 1000μs,需要计数次数 = 1000 / 0.08333 ≈ 12000
TH0 = (65536 - 12000) / 256;
TL0 = (65536 - 12000) % 256;
// 使能定时器0中断
ET0 = 1;
// 启动定时器0
TR0 = 1;
}
/*============ 定时器1初始化 ============*/
// 用于时钟计时,1ms定时
void Timer1_Init(void)
{
// 设置定时器1为模式0(16位自动重装载)
AUXR |= 0x40; // 定时器1为1T模式
TMOD &= 0x0F; // 清除定时器1模式位
// 计算1ms定时初值(假设系统时钟为12MHz)
// 1T模式下,1个机器周期 = 1/12MHz ≈ 0.08333μs
// 1ms = 1000μs,需要计数次数 = 1000 / 0.08333 ≈ 12000
TH1 = (65536 - 12000) / 256;
TL1 = (65536 - 12000) % 256;
// 使能定时器1中断
ET1 = 1;
// 启动定时器1
TR1 = 1;
}
/*============ 定时器0中断服务函数 ============*/
// 处理数码管动态扫描
void Timer0_ISR(void) interrupt 1
{
static unsigned char scan_index = 0; // 扫描索引
unsigned char seg_data;
// 重装定时器初值
TH0 = (65536 - 12000) / 256;
TL0 = (65536 - 12000) % 256;
// 关闭当前显示位(消隐)
BIT_PORT = 0xFF;
// 获取当前位的段码
seg_data = SEG_CODE[disp_buf[scan_index]];
// 处理第2位的冒号(DP段)
if(scan_index == 1) // 第2位数码管
{
if(colon_flag) // 需要显示冒号
{
seg_data |= 0x80; // 点亮DP段
}
}
// 输出段码
SEG_PORT = seg_data;
// 输出位码
BIT_PORT = BIT_CODE[scan_index];
// 更新扫描索引
scan_index++;
if(scan_index >= 4)
{
scan_index = 0;
}
}
/*============ 定时器1中断服务函数 ============*/
// 处理时钟计时、冒号闪烁和设置闪烁
void Timer1_ISR(void) interrupt 3
{
static unsigned int colon_cnt = 0; // 冒号计数器
static unsigned int blink_cnt = 0; // 闪烁计数器
// 重装定时器初值
TH1 = (65536 - 12000) / 256;
TL1 = (65536 - 12000) % 256;
// 定时器1计数器 - 用于1秒计时
t1_counter++;
// 毫秒计数器 - 用于按键延时
ms_counter++;
// 1秒定时(1000 * 1ms = 1秒) - 修正为1000
if(t1_counter >= 1000)
{
t1_counter = 0;
// 仅在正常显示模式下更新时间
if(display_mode == MODE_NORMAL)
{
time.second++;
if(time.second >= 60)
{
time.second = 0;
time.minute++;
if(time.minute >= 60)
{
time.minute = 0;
time.hour++;
if(time.hour >= 24)
{
time.hour = 0;
}
}
}
}
// 更新显示缓冲区
Update_Display_Buffer();
}
// 冒号闪烁控制(2秒周期,亮1秒灭1秒)
// 1000ms = 1秒,所以计数到1000切换一次
colon_cnt++;
if(colon_cnt >= 1000) // 1000 * 1ms = 1秒
{
colon_cnt = 0;
sec_led_on = ~sec_led_on; // 切换冒号状态
// 在正常显示模式下更新冒号
if(display_mode == MODE_NORMAL)
{
colon_flag = sec_led_on;
}
}
// 设置时的数字闪烁控制(2Hz,亮0.25秒灭0.25秒)
// 250ms = 0.25秒
blink_cnt++;
if(blink_cnt >= 250) // 250 * 1ms = 250ms
{
blink_cnt = 0;
blink_flag = ~blink_flag; // 切换闪烁标志
// 更新显示缓冲区(当闪烁状态改变时)
Update_Display_Buffer();
}
// 按键延时控制
if(!key_enable)
{
key_repeat_delay++;
if(key_repeat_delay >= 300) // 300ms按键延时
{
key_repeat_delay = 0;
key_enable = 1; // 重新使能按键
}
}
}
/*============ 更新显示缓冲区 ============*/
void Update_Display_Buffer(void)
{
// 正常情况下显示小时和分钟
disp_buf[0] = time.hour / 10; // 小时十位
disp_buf[1] = time.hour % 10; // 小时个位
disp_buf[2] = time.minute / 10; // 分钟十位
disp_buf[3] = time.minute % 10; // 分钟个位
// 初始化冒号标志
colon_flag = 0;
// 根据当前模式处理显示
switch(display_mode)
{
case MODE_NORMAL: // 正常显示模式
// 正常显示冒号
colon_flag = sec_led_on;
// 处理小时十位为0的情况
if(disp_buf[0] == 0)
{
disp_buf[0] = 10; // 不显示
}
break;
case MODE_SET_HOUR: // 设置小时模式
// 设置模式下冒号常亮
colon_flag = 1;
if(blink_flag) // 闪烁时清除显示
{
// 整个小时一起闪烁
disp_buf[0] = 10; // 小时十位不显示
disp_buf[1] = 10; // 小时个位不显示
}
break;
case MODE_SET_MINUTE: // 设置分钟模式
// 设置模式下冒号常亮
colon_flag = 1;
if(blink_flag) // 闪烁时清除显示
{
// 整个分钟一起闪烁
disp_buf[2] = 10; // 分钟十位不显示
disp_buf[3] = 10; // 分钟个位不显示
}
break;
}
}
/*============ 按键扫描函数 ============*/
void Key_Scan(void)
{
unsigned char key_current = 0;
// 读取按键状态
if(KEY_MODE == 0) key_current |= 0x01;
if(KEY_ADD == 0) key_current |= 0x02;
if(KEY_SUB == 0) key_current |= 0x04;
if(KEY_OK == 0) key_current |= 0x08;
// 无按键按下
if(key_current == 0)
{
key_state = 0;
key_press_time = 0;
key_pressed = 0;
return;
}
// 有按键按下
switch(key_state)
{
case 0: // 第一次检测到按键
key_state = 1;
key_press_time = 0;
break;
case 1: // 按键消抖
key_press_time++;
if(key_press_time > 5) // 50ms消抖
{
key_state = 2;
key_press_time = 0;
}
break;
case 2: // 按键按下确认
if(key_enable) // 按键使能
{
Key_Process(key_current);
key_enable = 0; // 禁用按键
key_state = 3; // 等待释放
}
break;
case 3: // 等待释放
// 等待按键释放
break;
}
}
/*============ 按键处理函数 ============*/
void Key_Process(unsigned char key_val)
{
switch(key_val)
{
case 0x01: // 功能/模式键
if(display_mode == MODE_NORMAL)
{
display_mode = MODE_SET_HOUR; // 进入设置小时模式
}
else if(display_mode == MODE_SET_HOUR)
{
display_mode = MODE_SET_MINUTE; // 进入设置分钟模式
}
else if(display_mode == MODE_SET_MINUTE)
{
display_mode = MODE_NORMAL; // 返回正常显示模式
}
Update_Display_Buffer(); // 更新显示
break;
case 0x02: // 加键
switch(display_mode)
{
case MODE_SET_HOUR: // 设置小时
time.hour++;
if(time.hour >= 24)
time.hour = 0;
break;
case MODE_SET_MINUTE: // 设置分钟
time.minute++;
if(time.minute >= 60)
time.minute = 0;
break;
default:
break;
}
Update_Display_Buffer(); // 更新显示
break;
case 0x04: // 减键
switch(display_mode)
{
case MODE_SET_HOUR: // 设置小时
if(time.hour > 0)
time.hour--;
else
time.hour = 23;
break;
case MODE_SET_MINUTE: // 设置分钟
if(time.minute > 0)
time.minute--;
else
time.minute = 59;
break;
default:
break;
}
Update_Display_Buffer(); // 更新显示
break;
case 0x08: // 确认键
// 确认键功能简化
if(display_mode == MODE_SET_HOUR)
{
display_mode = MODE_SET_MINUTE; // 从设置小时跳到设置分钟
}
else if(display_mode == MODE_SET_MINUTE)
{
display_mode = MODE_NORMAL; // 返回正常显示
}
Update_Display_Buffer(); // 更新显示
break;
}
}
/*============ 延时函数 ============*/
void Delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 114; j++);
}
|
-
-
|