#include <reg51.h> // 51单片机寄存器定义
#include <intrins.h> // 包含_nop_()等内部函数
#include <string.h> // 包含字符串操作函数
// 类型定义,简化代码书写
#define uint unsigned int
#define uchar unsigned char
// 引脚定义 - 明确各引脚功能和连接外设
sbit RS = P2^6; // LCD1602数据/命令选择端 (高电平:数据, 低电平:命令)
sbit RW = P2^5; // LCD1602读/写选择端 (高电平:读, 低电平:写)
sbit E = P2^7; // LCD1602使能信号 (下降沿有效)
sbit shezhi_key = P3^0; // 设置按键
sbit add_key = P3^1; // 增加按键
sbit sub_key = P3^2; // 减少按键
sbit naozhong_key = P3^3; // 闹钟功能按键
sbit beep = P2^5; // 蜂鸣器控制 (低电平触发发声)
sbit alarm_led = P2^2; // 报警LED指示灯 (低电平点亮)
sbit DQ1 = P3^7; // DS18B20温度传感器数据引脚
// 串口通信相关变量
unsigned char xdata uart_buf[32]; // 串口接收缓冲区,xdata表示外部数据存储
unsigned char xdata uart_cnt = 0; // 串口接收字节计数器
bit uart_cmd_ready = 0; // 命令接收完成标志位 (1:接收完成)
// 温度相关变量
bit temp_sfwfs = 0; // 温度显示方式标志 (0:摄氏度, 1:华氏度)
unsigned char temp_zs = 0; // 温度整数部分
unsigned char temp_xs = 0; // 温度小数部分(0-5)
unsigned int temp_update_count = 0; // 温度更新计数器
bit ng; // 温度正负标志位 (1:负温度, 0:正温度)
// 温度小数转换表,用于将DS18B20的16进制小数转换为十进制
uchar code df_Table[]= {0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};
uchar CurrentT = 0; // 当前温度整数部分
uchar Temp_Value[]={0x00,0x00}; // 存储从DS18B20读取的温度原始值(高8位和低8位)
uchar Display_Digit[]={0,0,0,0};// 用于显示的温度数字分解 (百位、十位、个位、小数位)
bit DS18B20_IS_OK = 1; // DS18B20传感器状态标志 (1:正常, 0:故障)
// 时间日期变量
unsigned int year = 2025; // 年份
unsigned char month = 8; // 月份
unsigned char day = 26; // 日期
unsigned char week = 2; // 星期 (1=周一...7=周日)
unsigned char hour = 0; // 小时
unsigned char minute = 0; // 分钟
unsigned char second = 0; // 秒钟
bit UP = 0; // 显示更新标志位 (1:需要更新显示)
// 闹钟相关变量
unsigned char alarm_hour = 9; // 闹钟小时设置
unsigned char alarm_minute = 20;// 闹钟分钟设置
bit alarm_flag = 0; // 闹钟显示标志 (0:显示正常时间, 1:显示闹钟时间)
bit alarm_trigger = 0; // 闹钟触发标志 (1:到达闹钟时间)
bit alarm_enabled = 1; // 闹钟使能标志 (1:开启, 0:关闭)
bit alarm_en; // 闹钟使能状态备份(用于串口通信)
// 设置相关变量
unsigned char setn = 0; // 设置项标识 (0:无设置, 1-6:时间日期设置, 8-9:闹钟设置)
bit set_flag = 0; // 设置模式标志位 (1:进入设置模式)
// 每月天数表(平年),2月为28天
unsigned char dayss_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
// 整点报时相关
bit hourly_chime_trigger = 0; // 整点报时触发标志
unsigned char last_hour = 0; // 上一小时记录,用于检测整点
// 温度异常判断参数
bit temp_abnormal = 0; // 温度异常标志
#define TEMP_HIGH_LIMIT 30 // 温度上限阈值(摄氏度)
#define TEMP_LOW_LIMIT 10 // 温度下限阈值(摄氏度)
/**
* 毫秒级延时函数
* @param ms 延时时长(毫秒)
* 注: 基于12MHz晶振校准
*/
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 112; j++); // 循环次数对应约1ms延时
}
/**
* 微秒级延时函数
* @param us 延时时长(微秒)
* 注: 基于12MHz晶振校准
*/
void delay_us(unsigned char us)
{
unsigned char i;
for(i = 0; i < us; i++) {
// 8个_nop_()指令约为1us(12MHz下一个机器周期为1us)
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
}
/**
* 短延时函数
* @param x 延时计数
*/
void Delay(uint x)
{
while(--x);
}
/**
* LCD1602写命令函数
* @param cmd 要写入的命令
*/
void sentcmd(unsigned char cmd)
{
P0 = cmd; // 将命令字送到P0口(数据总线)
RS = 0; // 命令模式(RS=0)
RW = 0; // 写模式(RW=0)
E = 1; // 产生使能高电平
_nop_();_nop_(); // 短暂延时,确保信号稳定
E = 0; // 产生下降沿,锁存命令
RW = 1; // 恢复RW为高电平
delay_ms(1); // 等待命令执行完成
}
/**
* LCD1602写数据函数
* @param dat 要写入的数据
*/
void sentdata(unsigned char dat)
{
P0 = dat; // 将数据送到P0口(数据总线)
RS = 1; // 数据模式(RS=1)
RW = 0; // 写模式(RW=0)
E = 1; // 产生使能高电平
_nop_();_nop_(); // 短暂延时,确保信号稳定
E = 0; // 产生下降沿,锁存数据
RW = 1; // 恢复RW为高电平
delay_ms(1); // 等待数据写入完成
}
/**
* 在LCD指定位置写入数据
* @param x 列位置(0-15)
* @param y 行位置(0-1)
* @param Data 要写入的数据
*/
void W_lcd(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0){
sentcmd(0x80 + x); // 第一行起始地址为0x80
} else {
sentcmd(0xc0 + x); // 第二行起始地址为0xc0
}
sentdata(Data); // 写入数据
}
/**
* LCD1602初始化函数
* 初始化显示模式、清除屏幕、设置光标等
*/
void lcd_init()
{
delay_ms(20); // 等待LCD上电稳定
sentcmd(0x38); // 8位数据接口,2行显示,5×8点阵字符
delay_ms(5);
sentcmd(0x38); // 重复设置,确保命令生效
delay_us(100);
sentcmd(0x38);
sentcmd(0x01); // 清屏命令
delay_ms(2);
sentcmd(0x0c); // 开显示,关闭光标
delay_ms(1);
sentcmd(0x06); // 字符不动,地址指针自动加1
delay_ms(1);
}
/**
* 在LCD上显示字符串
* @param str 要显示的字符串
* @param start_addr 起始地址(0x80开始为第一行,0xc0开始为第二行)
*/
void lcd_display_str(unsigned char *str, unsigned char start_addr)
{
unsigned char i;
sentcmd(start_addr); // 设置显示起始地址
delay_ms(1);
// 循环显示字符,最多16个字符(一行)
for(i = 0; i < 16 && str[i] != '\0'; i++)
{
sentdata(str[i]);
delay_ms(1);
}
}
/**
* 清空LCD指定行
* @param line_addr 行地址(0x80:第一行,0xc0:第二行)
*/
void lcd_clear_line(unsigned char line_addr)
{
unsigned char i;
sentcmd(line_addr);
for(i = 0; i < 16; i++)
{
sentdata(' '); // 用空格填充整行
}
}
/**
* 判断闰年
* @param y 年份
* @return 1:闰年,0:平年
*/
bit is_leap_year(unsigned int y)
{
// 闰年规则:能被4整除且不能被100整除,或能被400整除
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
/**
* 获取当月天数
* @param y 年份
* @param m 月份
* @return 当月天数
*/
unsigned char get_days_in_month(unsigned int y, unsigned char m)
{
if(m == 2 && is_leap_year(y))
return 29; // 闰年2月有29天
else
return dayss_in_month[m-1]; // 其他情况使用预定义表
}
/**
* 温度异常报警处理
* 当温度不在正常范围时,蜂鸣器和LED快速闪烁报警
*/
void temp_alarm()
{
unsigned int i;
// 快速闪烁200次
for(i = 0; i < 200; i++)
{
beep = 0; // 蜂鸣器响
alarm_led = 0; // LED亮
delay_ms(1); // 持续1ms
beep = 1; // 蜂鸣器停
alarm_led = 1; // LED灭
delay_ms(1); // 间隔1ms
}
temp_abnormal = 0; // 清除异常标志
alarm_led = 1; // 确保LED最终关闭
beep = 1; // 确保蜂鸣器最终关闭
}
/**
* 闹钟响铃处理
* 到达设定时间时,蜂鸣器响铃,可通过任意按键停止
*/
void alarm_broadcast()
{
unsigned int count = 0;
unsigned int loop;
// 响铃循环,直到按键或超时
while(1)
{
// 蜂鸣器快速响200次
for(loop = 0; loop < 200; loop++)
{
beep = 0; // 蜂鸣器响
delay_ms(1); // 持续1ms
beep = 1; // 蜂鸣器停
delay_ms(1); // 间隔1ms
// 检测按键,任何按键都可停止闹钟
if(shezhi_key == 0 || add_key == 0 || sub_key == 0 || naozhong_key == 0)
{
delay_ms(10); // 消抖
if(shezhi_key == 0 || add_key == 0 || sub_key == 0 || naozhong_key == 0)
{
beep = 1; // 停止蜂鸣器
// 等待按键释放
while(shezhi_key == 0 || add_key == 0 || sub_key == 0 || naozhong_key == 0);
alarm_trigger = 0; // 清除闹钟触发标志
return;
}
}
}
count++;
// 约30秒后自动停止(每次循环约400ms,75次≈30秒)
if(count >= 75)
{
break;
}
}
beep = 1; // 确保蜂鸣器停止
alarm_trigger = 0; // 清除闹钟触发标志
}
/**
* 整点报时处理
* 每到整点时,蜂鸣器根据当前小时数报时相应次数
*/
void hourly_chime()
{
unsigned char i, count;
unsigned int j;
// 计算报时次数(1-12次)
count = hour % 12;
if(count == 0) count = 12; // 0点和12点报12次
for(i = 0; i < count; i++) // 循环报时
{
// 蜂鸣器响200次
for(j = 0; j < 200; j++)
{
beep = 0; // 蜂鸣器响
delay_ms(1); // 持续1ms
beep = 1; // 蜂鸣器停
delay_ms(1); // 间隔1ms
}
// 每次报时间隔500ms
delay_ms(500);
}
hourly_chime_trigger = 0; // 清除整点报时标志
}
/**
* 将数字转换为两位字符串
* @param num 要转换的数字(0-99)
* @param str 存储转换结果的缓冲区
*/
void num_to_str(unsigned char num, unsigned char *str)
{
str[0] = num / 10 + '0'; // 十位数字
str[1] = num % 10 + '0'; // 个位数字
str[2] = '\0'; // 字符串结束符
}
/**
* 按键处理函数
* 处理所有按键的短按和长按操作
*/
void key_process()
{
// 闹钟键处理:短按切换显示模式,长按切换闹钟开关
if(naozhong_key == 0)
{
unsigned int press_time = 0;
// 等待按键释放,同时计时
while(naozhong_key == 0)
{
delay_ms(10);
press_time++;
if(press_time > 50) // 长按500ms以上
{
alarm_enabled = !alarm_enabled; // 切换闹钟开关状态
alarm_en = alarm_enabled; // 同步到串口变量
UP = 1; // 设置显示更新标志
break;
}
}
// 短按(小于500ms)切换显示模式
if(press_time <= 50)
{
alarm_flag = !alarm_flag; // 切换闹钟显示状态
set_flag = 0; // 退出设置模式
setn = 0; // 清除设置项
sentcmd(0x0c); // 关闭光标
lcd_clear_line(0xC0); // 清空第二行
UP = 1; // 设置显示更新标志
}
}
// 设置键处理:短按进入/切换设置项,长按退出设置
if(shezhi_key == 0)
{
unsigned int press_time = 0;
// 等待按键释放,同时计时
while(shezhi_key == 0)
{
delay_ms(10);
press_time++;
// 长按500ms以上,退出设置模式
if(press_time > 50)
{
set_flag = 0;
setn = 0;
sentcmd(0x0c); // 关闭光标
UP = 1;
// 等待按键释放
while(shezhi_key == 0);
return;
}
}
// 短按(500ms以内)处理
if(press_time > 0 && press_time <= 50)
{
if(set_flag == 0) // 未在设置模式,进入设置
{
set_flag = 1;
// 根据当前显示模式选择初始设置项
setn = (alarm_flag == 1) ? 8 : 1;
sentcmd(0x0e); // 打开光标(闪烁)
}
else // 已在设置模式,切换设置项
{
if(alarm_flag == 1) // 闹钟设置模式
{
setn++;
if(setn > 9) setn = 8; // 在8和9之间循环
}
else // 时间设置模式
{
setn = (setn % 6) + 1; // 在1-6之间循环
}
}
UP = 1; // 设置显示更新标志
}
}
// 加键处理:在设置模式下增加数值
if(add_key == 0 && set_flag == 1)
{
delay_ms(10); // 按键消抖
if(add_key == 0)
{
if(alarm_flag == 0) // 时间设置模式
{
switch(setn)
{
case 1: second = (second + 1) % 60; break; // 秒加1
case 2: minute = (minute + 1) % 60; break; // 分加1
case 3: hour = (hour + 1) % 24; break; // 时加1
case 4: // 日期加1,处理进位
day++;
if(day > get_days_in_month(year, month))
{
day = 1;
month++;
if(month > 12)
{
month = 1;
year++;
}
}
week = (week % 7) + 1; break; // 星期加1
case 5: // 月份加1,处理进位
month = (month % 12) + 1;
// 调整日期(如从31号到小月)
if(day > get_days_in_month(year, month))
day = get_days_in_month(year, month);
break;
case 6: // 年份加1
year++;
// 调整日期(如闰年变平年)
if(day > get_days_in_month(year, month))
day = get_days_in_month(year, month);
break;
}
}
else // 闹钟设置模式
{
switch(setn)
{
case 8: alarm_minute = (alarm_minute + 1) % 60; break; // 闹钟分钟加1
case 9: alarm_hour = (alarm_hour + 1) % 24; break; // 闹钟小时加1
}
}
UP = 1; // 设置显示更新标志
while(add_key == 0); // 等待按键释放
}
}
// 减键处理:在设置模式下减少数值
if(sub_key == 0 && set_flag == 1)
{
delay_ms(10); // 按键消抖
if(sub_key == 0)
{
if(alarm_flag == 0) // 时间设置模式
{
switch(setn)
{
case 1: // 秒减1
second = (second == 0) ? 59 : second - 1;
break;
case 2: // 分减1
minute = (minute == 0) ? 59 : minute - 1;
break;
case 3: // 时减1
hour = (hour == 0) ? 23 : hour - 1;
break;
case 4: // 日期减1,处理借位
if(day == 1) {
month--;
if(month == 0)
{
month = 12;
year--;
}
day = get_days_in_month(year, month);
}
else day--;
// 星期减1
week = (week == 1) ? 7 : week - 1;
break;
case 5: // 月份减1,处理借位
month = (month == 1) ? 12 : month - 1;
// 调整日期
if(day > get_days_in_month(year, month))
day = get_days_in_month(year, month);
break;
case 6: // 年份减1
year--;
// 调整日期
if(day > get_days_in_month(year, month))
day = get_days_in_month(year, month);
break;
}
}
else // 闹钟设置模式
{
switch(setn)
{
case 8: // 闹钟分钟减1
alarm_minute = (alarm_minute == 0) ? 59 : alarm_minute - 1;
break;
case 9: // 闹钟小时减1
alarm_hour = (alarm_hour == 0) ? 23 : alarm_hour - 1;
break;
}
}
UP = 1; // 设置显示更新标志
while(sub_key == 0); // 等待按键释放
}
}
}
/**
* 初始化DS18B20温度传感器
* @return 初始化状态(1:失败,0:成功)
*/
uchar Init_DS18B20()
{
uchar status;
DQ1 = 1; // 初始拉高总线
Delay(8); // 延时约8us
DQ1 = 0; // 拉低总线,发送复位脉冲
Delay(90); // 延时约90us
DQ1 = 1; // 释放总线
Delay(8); // 等待8us
status = DQ1; // 读取存在脉冲(0:成功,1:失败)
Delay(100); // 延时约100us
DQ1 = 1; // 拉高总线
return status;
}
/**
* 从DS18B20读取一个字节数据
* @return 读取到的字节
*/
uchar ReadOneByte()
{
uchar i,dat=0;
DQ1 = 1; // 初始拉高总线
_nop_();
for(i=0;i<8;i++) // 循环8次,读取8位
{
DQ1 = 0; // 拉低总线,产生读时序
dat >>= 1; // 数据右移一位
DQ1 = 1; // 释放总线
_nop_();_nop_();
if(DQ1) // 读取总线状态
dat |= 0X80; // 最高位置1
Delay(30); // 延时约30us
DQ1 = 1; // 拉高总线
}
return dat;
}
/**
* 向DS18B20写入一个字节数据
* @param dat 要写入的字节
*/
void WriteOneByte(uchar dat)
{
uchar i;
for(i=0;i<8;i++) // 循环8次,写入8位
{
DQ1 = 0; // 拉低总线,产生写时序
DQ1 = dat & 0x01; // 写入最低位
Delay(5); // 延时约5us
DQ1 = 1; // 拉高总线
dat >>= 1; // 数据右移一位
}
}
/**
* 读取DS18B20温度数据
*/
void Read_Temperature()
{ EA=0; // 关闭总中断,防止干扰温度读取
if(Init_DS18B20()==1)
DS18B20_IS_OK=0; // 传感器初始化失败
else
{
WriteOneByte(0xcc); // 发送跳过ROM命令
WriteOneByte(0x44); // 发送温度转换命令
Init_DS18B20(); // 重新初始化
WriteOneByte(0xcc); // 跳过ROM命令
WriteOneByte(0xbe); // 读取温度命令
Temp_Value[0] = ReadOneByte(); // 读取温度低8位
Temp_Value[1] = ReadOneByte(); // 读取温度高8位
DS18B20_IS_OK=1; // 传感器正常
}
EA=1; // 恢复总中断
}
/**
* 处理温度数据,转换为十进制并判断是否异常
*/
void Display_Temperature()
{
uchar t = 150;
ng = 0; // 默认温度为正
// 判断温度是否为负数(DS18B20的符号位判断)
if((Temp_Value[1]&0xf8)==0xf8)
{
// 负数处理:取补码
Temp_Value[1] = ~Temp_Value[1];
Temp_Value[0] = ~Temp_Value[0]+1;
if(Temp_Value[0]==0x00)
Temp_Value[1]++;
ng = 1; // 标记为负温度
}
else{
ng = 0; // 标记为正温度
}
// 处理温度小数部分
Display_Digit[0] = df_Table[Temp_Value[0]&0x0f];
// 处理温度整数部分
CurrentT = ((Temp_Value[0]&0xf0)>>4) | ((Temp_Value[1]&0x07)<<4);
// 分解温度数字,便于显示
Display_Digit[3] = CurrentT/100; // 百位
Display_Digit[2] = CurrentT%100/10; // 十位
Display_Digit[1] = CurrentT%10; // 个位
// 温度异常判断:负温度或超出范围
if(ng || CurrentT > TEMP_HIGH_LIMIT || CurrentT < TEMP_LOW_LIMIT)
{
temp_abnormal = 1;
}
else
{
temp_abnormal = 0; // 温度正常
}
}
/**
* 在LCD上显示温度状态
*/
void show_temp_status()
{
if(!DS18B20_IS_OK)
{
W_lcd(15, 1, 'E'); // 传感器错误显示"E"
}
else if(temp_abnormal)
{
W_lcd(15, 1, '!'); // 温度异常显示"!"
}
else
{
W_lcd(15, 1, 'C'); // 温度正常显示"C"(摄氏度)
}
}
/**
* 初始化串口通信
* 配置为9600波特率,8位数据,1位停止位,无校验
*/
void UART_Init()
{
SCON = 0x50; // 串口工作方式1(8位UART),允许接收
TMOD |= 0x20; // 定时器1工作方式2(8位自动重装)
TH1 = 0xFD; // 9600波特率初值(12MHz晶振)
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
EA = 1; // 开总中断
ES = 1; // 开串口中断
}
/**
* 串口发送一个字符
* @param dat 要发送的字符
*/
void UART_SendChar(unsigned char dat)
{
SBUF = dat; // 将数据放入发送缓冲区
while(!TI); // 等待发送完成
TI = 0; // 清除发送完成标志
}
/**
* 串口发送字符串
* @param str 要发送的字符串(常量字符串)
*/
void UART_SendString(unsigned char code *str) reentrant
{
while(*str != '\0') // 循环发送直到字符串结束
{
UART_SendChar(*str++);
}
}
/**
* 串口发送温度数据
* 格式: T:+xx.xC\r\n 或 T:-xx.xC\r\n
*/
void UART_SendTemperature()
{
UART_SendString("T:");
if(ng == 1)
UART_SendChar('-'); // 负温度符号
UART_SendChar(Display_Digit[2] + '0'); // 十位
UART_SendChar(Display_Digit[1] + '0'); // 个位
UART_SendChar('.'); // 小数点
UART_SendChar(Display_Digit[0] + '0'); // 小数位
UART_SendString("C\r\n"); // 单位和换行
}
/**
* 解析串口命令
* 目前仅处理温度查询命令
*/
void ParseUARTCommand()
{
// 处理温度查询命令
if(strncmp(uart_buf, "GETTEMP", 7) == 0)
{
UART_SendTemperature();
}
}
//串口中断服务函数
//处理串口接收数据
void UART_ISR() interrupt 4
{
unsigned char ch;
if(RI) // 接收中断标志
{
RI = 0; // 清除接收标志
ch = SBUF; // 读取接收数据
// 检测回车换行符,判断命令结束
if(ch == '\r' || ch == '\n')
{
if(uart_cnt > 0)
{
uart_buf[uart_cnt] = '\0'; // 添加字符串结束符
uart_cmd_ready = 1; // 设置命令就绪标志
uart_cnt = 0; // 重置计数器
}
}
// 接收数据并存储到缓冲区
else if(uart_cnt < sizeof(uart_buf)-1)
{
uart_buf[uart_cnt++] = ch;
}
}
}
//初始化定时器0
//配置为16位定时器,1ms中断一次
void timer0_init()
{
TMOD &= 0xF0; // 清除定时器0配置
TMOD |= 0x01; // 定时器0工作方式1(16位)
TH0 = 0xFC; // 初值设置(1ms溢出)
TL0 = 0x66;
ET0 = 1; // 允许定时器0中断
EA = 1; // 允许总中断
TR0 = 1; // 启动定时器0
}
// 定时器0中断服务程序
//1ms触发一次,用于时间计数和定时任务
void timer0_isr() interrupt 1
{
static unsigned int count = 0; // 1ms计数器
static unsigned int uart_report_cnt = 0; // 串口上报计数器
TH0 = 0xFC; // 重装载初值(1ms)
TL0 = 0x66;
count++;
if(count >= 1000) // 每1000ms(1秒)执行一次
{
count = 0;
temp_update_count++;
// 每1秒设置显示更新标志
if(temp_update_count >= 1)
{
temp_update_count = 0;
UP = 1;
}
if(set_flag == 0) // 非设置模式才更新时间
{
second++;
if(second >= 60) // 秒进位
{
second = 0;
minute++;
if(minute >= 60) // 分进位
{
minute = 0;
hour++;
// 检测整点,触发报时
if(hour != last_hour)
{
last_hour = hour;
if(minute == 0 && second == 0)
{
hourly_chime_trigger = 1;
}
}
if(hour >= 24) // 时进位
{
hour = 0;
day++;
week++;
if(week > 7) week = 1; // 星期循环
// 日期进位处理
if(day > get_days_in_month(year, month))
{
day = 1;
month++;
if(month > 12) // 月进位
{
month = 1;
year++; // 年进位
}
}
}
}
}
// 检测是否到达闹钟时间
if(alarm_enabled && hour == alarm_hour && minute == alarm_minute && second == 0)
{
alarm_trigger = 1;
}
UP = 1; // 设置显示更新标志
}
}
// 每3秒通过串口上报一次温度
uart_report_cnt++;
if(uart_report_cnt >= 3000) // 3秒(3000ms)
{
uart_report_cnt = 0;
UART_SendTemperature(); // 发送温度数据
}
}
//主函数
//系统初始化和主循环
void main()
{ unsigned char i=0;
unsigned char str_date[17]; // 日期显示字符串
unsigned char str_time[17]; // 时间/闹钟显示字符串
unsigned char buf[4]; // 数字转换缓冲区
unsigned char week_str[4]; // 星期字符串
// 初始化IO口为高电平
P0 = 0xFF;
P2 = 0xFF;
P3 = 0xFF;
lcd_init(); // 初始化LCD1602
timer0_init(); // 初始化定时器0
UART_Init(); // 初始化串口通信
beep = 1; // 蜂鸣器初始不响
alarm_led = 1; // 报警LED初始关闭
last_hour = hour;
alarm_en = alarm_enabled; // 同步闹钟使能状态
// 初始化温度传感器(多次读取确保稳定)
Read_Temperature();
delay_ms(500);
Read_Temperature();
delay_ms(500);
Read_Temperature();
delay_ms(500);
// 主循环
while(1)
{
key_process(); // 按键处理
// 闹钟触发处理
if(alarm_trigger)
{
alarm_broadcast();
}
// 整点报时处理
if(hourly_chime_trigger)
{
hourly_chime();
}
// 温度异常报警处理
if(temp_abnormal)
{
temp_alarm();
}
// 处理串口命令
if(uart_cmd_ready)
{
ParseUARTCommand();
uart_cmd_ready = 0;
}
if(UP) // 需要更新显示
{
UP = 0;
memset(str_time, 0, sizeof(str_time)); // 清空时间字符串
// 显示时间/闹钟
if(alarm_flag == 0) // 正常模式:显示时间和温度
{
// 构建时间字符串 HH:MM:SS
num_to_str(hour, buf);
strcpy(str_time, buf);
strcat(str_time, ":");
num_to_str(minute, buf);
strcat(str_time, buf);
strcat(str_time, ":");
num_to_str(second, buf);
strcat(str_time, buf);
// 每3秒更新一次温度
i++;
if(i>2)
{
Read_Temperature(); // 读取温度
if(DS18B20_IS_OK)
{
Display_Temperature(); // 处理温度数据
}
i=0;
}
// 显示温度符号(正/负)
if(ng==1)
W_lcd(10,1,'-');
else
W_lcd(10,1,' ');
// 显示温度值
W_lcd(11,1,Display_Digit[2]+'0'); // 十位
W_lcd(12,1,Display_Digit[1]+'0'); // 个位
W_lcd(13,1,'.'); // 小数点
W_lcd(14,1,Display_Digit[0]+'0'); // 小数位
// 显示温度状态
show_temp_status();
}
else // 闹钟模式:显示闹钟时间
{
strcpy(str_time, "N Z:");
num_to_str(alarm_hour, buf);
strcat(str_time, buf);
strcat(str_time, ":");
num_to_str(alarm_minute, buf);
strcat(str_time, buf);
// 显示闹钟开关状态
strcat(str_time, " ");
if(alarm_enabled)
strcat(str_time, "ON ZJ"); // 闹钟开启
else
strcat(str_time, "OFF"); // 闹钟关闭
}
// 构建日期字符串
num_to_str(year % 100, buf);
strcpy(str_date, "20");
strcat(str_date, buf);
strcat(str_date, "/");
num_to_str(month, buf);
strcat(str_date, buf);
strcat(str_date, "/");
num_to_str(day, buf);
strcat(str_date, buf);
strcat(str_date, " ");
// 转换星期为字符串
switch(week)
{
case 1: strcpy(week_str, "Mon"); break;
case 2: strcpy(week_str, "Tue"); break;
case 3: strcpy(week_str, "Wed"); break;
case 4: strcpy(week_str, "Thu"); break;
case 5: strcpy(week_str, "Fri"); break;
case 6: strcpy(week_str, "Sat"); break;
case 7: strcpy(week_str, "Sun"); break;
default: strcpy(week_str, "---"); break;
}
strcat(str_date, week_str);
// 输出到LCD显示
lcd_display_str(str_date, 0x80); // 第一行显示日期
lcd_display_str(str_time, 0xC0); // 第二行显示时间/闹钟
// 设置光标位置(设置模式)
if(setn != 0)
{
if(alarm_flag == 0) // 时间设置模式
{
switch(setn)
{
case 1: sentcmd(0xC0 + 7); break; // 秒位置
case 2: sentcmd(0xC0 + 4); break; // 分位置
case 3: sentcmd(0xC0 + 1); break; // 时位置
case 4: sentcmd(0x80 + 9); break; // 日位置
case 5: sentcmd(0x80 + 6); break; // 月位置
case 6: sentcmd(0x80 + 3); break; // 年位置
}
}
else // 闹钟设置模式
{
switch(setn)
{
case 8: sentcmd(0xC0 + 8); break; // 闹钟分钟位置
case 9: sentcmd(0xC0 + 5); break; // 闹钟小时位置
}
}
}
}
delay_ms(10); // 主循环延时,降低CPU占用
}
}添加代码 通过上位机串口助手修改时间和闹钟
最新发布