#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "lcd.h"
#include "rtc.h"
#include "exti.h"
#include "key.h"
#include "beep.h"
#include "adc.h"
#include "math.h"
#include "alarm.h" // 包含闹钟功能的头文件
#include <string.h>
//===================== 宏定义部分 =====================
// 时钟指针长度定义(像素)
#define SECOND_HAND_LENGTH 100 // 秒针长度
#define MINUTE_HAND_LENGTH 75 // 分针长度
#define HOUR_HAND_LENGTH 50 // 时针长度
// 圆周率定义
#ifndef PI
#define PI 3.14159265358979323846
#endif
#define UART_BUF_SIZE 32 // 增大缓冲区大小
//===================== 全局变量部分 =====================
u8 alarmSetting; // 当前设置的闹钟编号(1-3)
u8 alarmSettingMode; // 闹钟设置模式(0-设置小时,1-设置分钟)
u8 alarmBeepCount; // 闹钟蜂鸣器计数(用于控制蜂鸣频率)
// 串口接收相关变量
u8 uart_buf[UART_BUF_SIZE]; // 串口接收缓冲区(最多存储10字节)
u8 uart_len = 0; // 接收数据长度
u8 uart_receive_finish = 0; // 接收完成标志
// 系统状态变量
u8 set; // 未明确使用的标志位
int sign; // 整点报时标志
int choice=0; // 菜单选择项索引
int th,tm,ts; // 时分秒(用于绘制时钟)
int dy,dm,dd; // 年月日
int w; // 星期
//===================== 函数声明部分 =====================
u8 calculate_weekday(int year, int month, int day);
//===================== 函数定义部分 =====================
/**
* @brief 计算星期几(蔡勒公式)
* @param year 年份(0-99)
* @param month 月份(1-12)++
* @param day 日期(1-31)
* @return 星期几(1-7,1表示星期一)
*/
u8 calculate_weekday(int year, int month, int day) {
// 所有变量声明放在函数开头
int c, y, w;
if (month < 3) {
month += 12;
year--;
}
c = year / 100;
y = year % 100;
w = c / 4 - 2 * c + y + y / 4 + (13 * (month + 1)) / 5 + day - 1;
w = (w % 7 + 7) % 7; // 转换为0-6(0表示星期日)
return w + 1; // 转换为1-7(1表示星期一)
}
/**
* @brief 解析串口命令并执行相应操作
* @param cmd 命令缓冲区指针
*/
void parse_command(u8 *cmd)
{
switch(cmd[0])
{
// 设置系统时间命令(格式:Hxx:yy)
case 'H':
{
int hour, minute;
if(sscanf((char*)&cmd[1], "%d:%d", &hour, &minute) == 2)
{
if(hour >= 0 && hour < 24 && minute >= 0 && minute < 60)
{
RTC_Set_Time(hour, minute, 0, RTC_H12_AM);
printf("Time set to: %02d:%02d:00\r\n", hour, minute);
}
else
{
printf("Invalid time! Range: 00:00-23:59\r\n");
}
}
else
{
printf("Invalid format! Use Hxx:yy (e.g., H14:30)\r\n");
}
break;
}
// 设置闹钟时间命令(格式:AxxxHH:MM)
case 'A':
{
int alarm_idx, hour, minute;
if(sscanf((char*)&cmd[1], "%d%d:%d", &alarm_idx, &hour, &minute) == 3)
{
if(alarm_idx >= 1 && alarm_idx <= MAX_ALARMS &&
hour >= 0 && hour < 24 && minute >= 0 && minute < 60)
{
alarms[alarm_idx-1].hour = hour;
alarms[alarm_idx-1].minute = minute;
printf("Alarm %d set to: %02d:%02d\r\n", alarm_idx, hour, minute);
}
else
{
printf("Invalid alarm or time! Alarm: 1-%d, Time: 00:00-23:59\r\n", MAX_ALARMS);
}
}
else
{
printf("Invalid format! Use AxxxHH:MM (e.g., A108:30)\r\n");
}
break;
}
// 启用闹钟命令(格式:Exxx)
case 'E':
{
int alarm_idx;
if(sscanf((char*)&cmd[1], "%d", &alarm_idx) == 1)
{
if(alarm_idx >= 1 && alarm_idx <= MAX_ALARMS)
{
alarms[alarm_idx-1].enable = 1;
printf("Alarm %d enabled\r\n", alarm_idx);
}
else
{
printf("Invalid alarm! Range: 1-%d\r\n", MAX_ALARMS);
}
}
else
{
printf("Invalid format! Use Exxx (e.g., E1)\r\n");
}
break;
}
// 禁用闹钟命令(格式:Dxxx)
case 'D':
{
int alarm_idx;
if(sscanf((char*)&cmd[1], "%d", &alarm_idx) == 1)
{
if(alarm_idx >= 1 && alarm_idx <= MAX_ALARMS)
{
alarms[alarm_idx-1].enable = 0;
alarms[alarm_idx-1].alarmState = 0;
printf("Alarm %d disabled\r\n", alarm_idx);
}
else
{
printf("Invalid alarm! Range: 1-%d\r\n", MAX_ALARMS);
}
}
else
{
printf("Invalid format! Use Dxxx (e.g., D1)\r\n");
}
break;
}
// 设置日期命令(格式:Yyy-mm-dd)
case 'Y':
{
int year, month, day;
u8 max_day;
u8 week;
const u8 month_days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
//printf("Received Y command: %s\r\n", cmd);
// 尝试解析两种格式:Yyy-mm-dd 或 YYYYY-mm-dd
if(sscanf((char*)&cmd[1], "%d-%d-%d", &year, &month, &day) == 3)
{
// 如果解析出的年份是四位数(如2025),转换为两位数(如25)
if(year >= 2000) year -= 2000;
if(year < 0 || year > 99) {
printf("Invalid year! Range: 00-99\r\n");
break;
}
if(month < 1 || month > 12) {
printf("Invalid month! Range: 01-12\r\n");
break;
}
max_day = month_days[month];
if(month == 2) {
if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
max_day = 29;
}
}
if(day < 1 || day > max_day) {
printf("Invalid day! Max day for %02d is %d\r\n", month, max_day);
break;
}
week = calculate_weekday(year, month, day);
RTC_Set_Date(year, month, day, week);
dy = year;
dm = month;
dd = day;
w = week;
printf("Date set to: 20%02d-%02d-%02d, Week: %d\r\n", year, month, day, week);
}
else
{
printf("sscanf failed, cmd[1]: %s\r\n", &cmd[1]);
printf("Invalid format! Use Yyy-mm-dd (e.g., Y25-06-20)\r\n");
}
break;
}
// 未知命令处理
default:
printf("Unknown command: %s\r\n", cmd);
printf("Supported commands:\r\n");
printf("Hxx:yy - Set time (e.g., H14:30)\r\n");
printf("AxxxHH:MM - Set alarm (e.g., A1 08:30)\r\n");
printf("Exxx - Enable alarm (e.g., E1)\r\n");
printf("Dxxx - Disable alarm (e.g., D1)\r\n");
printf("Yyy-mm-dd - Set date (e.g., Y23-06-20)\r\n");
break;
}
}
/**
* @brief 绘制时钟指针
* @param hour 小时
* @param minute 分钟
* @param second 秒
*/
void draw_clock_hands(int hour, int minute, int second) {
// 计算指针角度(弧度制)
// 注意:0度对应12点方向,顺时针为正方向
float second_angle = (second * 6.0) * PI / 180.0; // 秒针:每秒6度
float minute_angle = (minute * 6.0 + second * 0.1) * PI / 180.0; // 分针:每分钟6度,每秒0.1度
float hour_angle = ((hour % 12) * 30.0 + minute * 0.5) * PI / 180.0; // 时针:每小时30度,每分钟0.5度
// 计算指针端点坐标(中心点为(240, 180))
// 注意:LCD坐标Y轴向下为正,需要取反以正确显示
int sec_x = 240 + SECOND_HAND_LENGTH * sin(second_angle);
int sec_y = 180 - SECOND_HAND_LENGTH * cos(second_angle);
int min_x = 240 + MINUTE_HAND_LENGTH * sin(minute_angle);
int min_y = 180 - MINUTE_HAND_LENGTH * cos(minute_angle);
int hour_x = 240 + HOUR_HAND_LENGTH * sin(hour_angle);
int hour_y = 180 - HOUR_HAND_LENGTH * cos(hour_angle);
// 绘制指针(时针黑色、分针蓝色、秒针红色)
POINT_COLOR = BLACK; // 设置时针颜色为黑色
LCD_DrawLine(240, 180, hour_x, hour_y); // 绘制时针
POINT_COLOR = BLUE; // 设置分针颜色为蓝色
LCD_DrawLine(240, 180, min_x, min_y); // 绘制分针
POINT_COLOR = RED; // 设置秒针颜色为红色
LCD_DrawLine(240, 180, sec_x, sec_y); // 绘制秒针
}
/**
* @brief 主函数
*/
int main(void)
{
RTC_TimeTypeDef RTC_TimeStruct; // RTC时间结构体
RTC_DateTypeDef RTC_DateStruct; // RTC日期结构体
short temp;
u8 tbuf[40]; // 临时字符串缓冲区
u16 t=0; // 计时变量
int i; // 循环变量
// 系统初始化
set=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置系统中断优先级分组2
delay_init(168); // 初始化延时函数
uart_init(115200); // 初始化串口,波特率115200
LCD_Init(); // 初始化LCD
My_RTC_Init(); // 初始化RTC(实时时钟)
EXTIX_Init(); // 初始化外部中断
BEEP_Init(); // 初始化蜂鸣器
Adc_Init(); // 初始化温度采集ADC
RTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits,0); // 配置WAKE UP中断,1秒中断一次
BEEP=0; // 蜂鸣器初始状态关闭
// 闹钟初始化
for(i=0; i<MAX_ALARMS; i++){
alarms[i].enable = 0; // 禁用所有闹钟
alarms[i].hour = 7; // 默认闹钟时间为7:00
alarms[i].minute = 0;
alarms[i].alarmState = 0; // 闹钟状态初始化为未触发
}
alarmSetting = 0;
alarmSettingMode = 0;
alarmBeepCount = 0;
// 初始化LCD显示
POINT_COLOR=BLACK; // 恢复字体颜色为黑色
// 绘制时钟背景
LCD_DrawRectangle(90, 30, 390, 330); // 绘制外框
LCD_DrawRectangle(120, 60, 360, 300); // 绘制内框
// 定义时钟绘制参数
#define SIDE_LENGTH 300 // 时钟边长
#define MARGIN 10 // 边距
// =========绘制时钟刻度(1-12小时标记)
LCD_ShowString(235, 30+MARGIN, 10, 16, 16, "12"); // 12点
LCD_ShowString(390-MARGIN-16, 180, 5, 16, 16, "3"); // 3点
LCD_ShowString(240, 330-MARGIN-16, 5, 16, 16, "6"); // 6点
LCD_ShowString(90+MARGIN, 180, 5, 16, 16, "9"); // 9点
LCD_ShowString(315, 30+MARGIN, 5, 16, 16, "1"); // 1点
LCD_ShowString(390-MARGIN-16, 105, 5, 16, 16, "2"); // 2点
LCD_ShowString(390-MARGIN-16, 255, 5, 16, 16, "4"); // 4点
LCD_ShowString(315, 330-MARGIN-16, 5, 16, 16, "5"); // 5点
LCD_ShowString(165, 330-MARGIN-16, 5, 16, 16, "7"); // 7点
LCD_ShowString(90+MARGIN, 255, 5, 16, 16, "8"); // 8点
LCD_ShowString(90+MARGIN, 105, 10, 16, 16, "10"); // 10点
LCD_ShowString(165, 30+MARGIN, 10, 16, 16, "11"); // 11点
//--------------------串口操作提示
LCD_ShowString(160, 600, 600, 16, 16, "SetTime: H18:00");
LCD_ShowString(160, 620, 600, 16, 16, "SetDate: Y2025-06-20"); // 使用Y前缀
LCD_ShowString(160, 640, 600, 16, 16, "SetAlarm: A1 08:00");
LCD_ShowString(160, 660, 600, 16, 16, "Enable/Disable: E1/D1");
//============主循环
while(1)
{
t++;
if((t%10)==0) // 每100ms刷新一次显示(t++一次为10ms,10次为100ms)
{
// ======刷新时钟指针
POINT_COLOR=BLACK;
LCD_Fill(125, 65, 355, 295, WHITE); // 清除指针区域
draw_clock_hands(th, tm, ts); // 绘制时钟指针
// ======显示时间和日期
RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct); // 读取RTC时间
RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct); // 读取RTC日期
th=RTC_TimeStruct.RTC_Hours;
tm=RTC_TimeStruct.RTC_Minutes;
ts=RTC_TimeStruct.RTC_Seconds;
dy=RTC_DateStruct.RTC_Year;
dm=RTC_DateStruct.RTC_Month;
dd=RTC_DateStruct.RTC_Date;
w=RTC_DateStruct.RTC_WeekDay;
POINT_COLOR=BLUE;
sprintf((char*)tbuf,"20%02d-%02d-%02d",RTC_DateStruct.RTC_Year,RTC_DateStruct.RTC_Month,RTC_DateStruct.RTC_Date);
LCD_ShowString(160, 340, 200, 24, 24, tbuf); // 显示日期
//=======显示周
switch(RTC_DateStruct.RTC_WeekDay) {
case 1: LCD_ShowString(160, 370, 200, 24, 24, "Monday "); break;
case 2: LCD_ShowString(160, 370, 200, 24, 24, "Tuesday "); break;
case 3: LCD_ShowString(160, 370, 200, 24, 24, "Wednesday"); break;
case 4: LCD_ShowString(160, 370, 200, 24, 24, "Thursday "); break;
case 5: LCD_ShowString(160, 370, 200, 24, 24, "Friday "); break;
case 6: LCD_ShowString(160, 370, 200, 24, 24, "Saturday "); break;
case 7: LCD_ShowString(160, 370, 200, 24, 24, "Sunday "); break;
}
sprintf((char*)tbuf,"%02d:%02d:%02d",RTC_TimeStruct.RTC_Hours,RTC_TimeStruct.RTC_Minutes,RTC_TimeStruct.RTC_Seconds);
LCD_ShowString(160, 400, 200, 24, 24, tbuf); // 显示时间
//======== 整点报时功能
if(RTC_TimeStruct.RTC_Minutes==0&&RTC_TimeStruct.RTC_Seconds==0){sign=1;}
if(RTC_TimeStruct.RTC_Minutes==0&&RTC_TimeStruct.RTC_Seconds==10){sign=10;}
if(sign==1){BEEP=!BEEP;delay_ms(20);BEEP=!BEEP;}//整点时蜂鸣器短鸣一次
// =============读取并显示温度
temp = Get_Temprate(); //获取内部温度传感器温度值
sprintf((char*)tbuf,"Temperature: %.2fC",(float)temp/100); //显示温度
LCD_ShowString(160, 430, 200, 24 , 24 , tbuf);
}
// =============闹钟状态显示与处理
for(i=0; i<MAX_ALARMS; i++){
char alarmBuf[40];
u16 textColor = BLUE; // 默认显示颜色为蓝色
// 构建闹钟状态显示字符串
if(alarms[i].enable){
if(alarms[i].alarmState == 1){ // 闹钟触发中
sprintf(alarmBuf, "Alarm%d: %02d:%02d ON ", i+1, alarms[i].hour, alarms[i].minute);
textColor = RED; // 触发中的闹钟显示为红色
} else {
sprintf(alarmBuf, "Alarm%d: %02d:%02d ON ", i+1, alarms[i].hour, alarms[i].minute);
}
} else {
sprintf(alarmBuf, "Alarm%d: %02d:%02d OFF", i+1, alarms[i].hour, alarms[i].minute);
}
// 在LCD上显示闹钟状态
POINT_COLOR = textColor;
LCD_ShowString(160, 460 + i*30, 200, 24, 24, (u8*)alarmBuf);
}
// ======闹钟触发检测
if(alarms[0].enable && th == alarms[0].hour && tm == alarms[0].minute && alarms[0].alarmState == 0){
alarms[0].alarmState = 1;
alarmBeepCount = 0;
BEEP = 1; // 启动蜂鸣器
}
if(alarms[1].enable && th == alarms[1].hour && tm == alarms[1].minute && alarms[1].alarmState == 0){
alarms[1].alarmState = 1;
alarmBeepCount = 0;
BEEP = 1; // 启动蜂鸣器
}
if(alarms[2].enable && th == alarms[2].hour && tm == alarms[2].minute && alarms[2].alarmState == 0){
alarms[2].alarmState = 1;
alarmBeepCount = 0;
BEEP = 1; // 启动蜂鸣器
}
// 蜂鸣器控制(闹钟触发时)
if(alarms[0].alarmState == 1 || alarms[1].alarmState == 1 || alarms[2].alarmState == 1){
alarmBeepCount++;
// 每500ms切换蜂鸣器状态(20次*10ms=200ms)
if(alarmBeepCount % 20 == 0){
BEEP = !BEEP;
}
// 10秒后自动停止蜂鸣(200次*10ms=2000ms=2秒)
if(alarmBeepCount >= 200){
for(i=0; i<MAX_ALARMS; i++){
if(alarms[i].alarmState == 1){
alarms[i].alarmState = 2;
}
}
BEEP = 0; // 确保蜂鸣器停止
}
}
// ===============选择菜单栏显示
switch(choice){
case 0: LCD_ShowString(160,560,200,24,24," "); break;
// 时分设置
case 1: LCD_ShowString(160,560,200,24,24,"SetHour "); break; // 设置小时
case 2: LCD_ShowString(160,560,200,24,24,"SetMinute "); break;
//年月日周设置
case 3: LCD_ShowString(160,560,200,24,24,"SetYear "); break; // 设置年份
case 4: LCD_ShowString(160,560,200,24,24,"SetMonth "); break; // 修正拼写错误:Mounth -> Month
case 5: LCD_ShowString(160,560,200,24,24,"SetDay "); break; // 设置日期
// 闹钟1设置
case 6: LCD_ShowString(160,560,200,24,24,"SetAlarm1 Hour "); break;
case 7: LCD_ShowString(160,560,200,24,24,"SetAlarm1 Minute "); break;
case 8: LCD_ShowString(160,560,200,24,24,"Enable Alarm1 "); break;
// 闹钟2设置
case 9: LCD_ShowString(160,560,200,24,24,"SetAlarm2 Hour "); break;
case 10: LCD_ShowString(160,560,200,24,24,"SetAlarm2 Minute "); break;
case 11: LCD_ShowString(160,560,200,24,24,"Enable Alarm2 "); break;
// 闹钟3设置
case 12: LCD_ShowString(160,560,200,24,24,"SetAlarm3 Hour "); break;
case 13: LCD_ShowString(160,560,200,24,24,"SetAlarm3 Minute "); break;
case 14: LCD_ShowString(160,560,200,24,24,"Enable Alarm3 "); break;
}
// =============处理串口命令
if(uart_receive_finish)
{
uart_receive_finish = 0;
parse_command(uart_buf); // 解析并执行串口命令
}
}
}详细解释一下
最新发布