BKP(备份寄存器)
1. 什么是BKP?
备份寄存器是42个16位的寄存器,可用来存储84个字节的用户应用程序数据。他们处在备份域里,当VDD电 源被切断,他们仍然由VBAT维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会 被复位。
此外, BKP控制寄存器用来管理侵入检测和RTC校准功能。
复位后,对备份寄存器和RTC的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。执行以下 操作可以使能对备份寄存器和RTC的访问:
通过设置寄存器RCC_APB1ENR的PWREN和BKPEN位来打开电源和后备接口的时钟
电源控制寄存器(PWR_CR)的DBP位来使能对后备寄存器和RTC的访问。
用户数据存储容量:
20字节(中容量和小容量)/ 84字节(大容量和互联型)
2. BKP框图
小实验:读写BKP
实验目的 读写BKP
/------------------------RTC.h------------------------/
#include "sys.h"
void rtc_init(void);
uint16_t rtc_read_bkr(uint8_t bkrx);
void rtc_write_bkp(uint8_t bkrx,uint16_t data);
/------------------------RTC.c------------------------/
rtc初始化
RTC_HandleTypeDef rtc_handle = {0};
void rtc_init(void)
{
//备份寄存器和RTC的访问 1PWREN,BKPEN
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
//对DBPDBP位来使能 pwr
HAL_PWR_EnableBkUpAccess();
//RTC部分
rtc_handle.Instance = RTC; //寄存器选择
rtc_handle.Init.AsynchPrediv = 32767;
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
HAL_RTC_Init(&rtc_handle);
}
指定读取的寄存器
uint16_t rtc_read_bkr(uint8_t bkrx)
{
uint32_t data = 0; //接收返回值
data = HAL_RTCEx_BKUPRead(&rtc_handle,bkrx);//句柄,指定读哪个寄存器
return (uint16_t)data; //返回寄存器读出值
}
写的是哪个寄存器 写的数据是16位的数据
void rtc_write_bkp(uint8_t bkrx,uint16_t data)
{
//句柄 哪个寄存器 写数据
HAL_RTCEx_BKUPWrite(&rtc_handle,bkrx,data);
}
/---------------------------------main.c---------------------------------/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "rtc.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
rtc_init();
// led1_on();
// led1_off();
printf("helloworld!\r\n");
rtc_write_bkp(1,0xA5A5);
printf("读出来的值为:%X\r\n",rtc_read_bkr(1));
while(1)
{
}
}
十四、RTC
1. 什么是RTC?
实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历 的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置 和时间维持不变。
复位后,对备份寄存器和RTC的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。执行以下 操作可以使能对备份寄存器和RTC的访问:
- 通过设置寄存器RCC_APB1ENR的PWREN和BKPEN位来打开电源和后备接口的时钟
- 电源控制寄存器(PWR_CR)的DBP位来使能对后备寄存器和RTC的访问。
32位的可编程计数器,可对应Unix时间戳的秒计数器。
Unix时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
20位的可编程预分频器,可适配不同频率的输入时钟。
可选择三种RTC时钟源:
- HSE时钟除以128(通常为8MHz/128)
- LSE振荡器时钟(通常为32.768KHz)
- LSI振荡器时钟(40KHz)
2. RTC框图
3. 寄存器及库函数
1.主要功能函数
设置日历参数
HAL_RTC_GetDate(&hrtc,&RTC_DateStruct,RTC_FORMAT_BIN);
HAL_RTC_GetTime(&hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN);
获取日历参数
HAL_RTC_SetTime(&hrtc, &RTC_Time, RTC_FORMAT_BIN);
HAL_RTC_SetDate(&hrtc, &RTC_Date, RTC_FORMAT_BIN);
其中,第一个参数为RTC结构体,第二个参数为Time和Date结构体,第三个参数可设置为:RTC_FORMAT_BIN或RTC_FORMAT_BCD
周期性唤醒中断
void RTC_WKUP_IRQHandler(void)
原文链接:https://blog.csdn.net/weixin_44064233/article/details/108326975
只有在电源控制寄存器(PWR_CR)中的DBP位置’1’
位15 RTCEN:RTC时钟使能 (RTC clock enable)
RTCSEL[1:0]:RTC时钟源选择 (RTC clock source selection)
位9:800:无时钟; 01:LSE振荡器作为RTC时钟; 10:LSI振荡器作为RTC时钟; 11:HSE振荡器在128分频后作为RTC时钟。
不打开没有意义
LSEON:外部低速振荡器使能由软件置’1’或清’0’
0:外部32kHz振荡器关闭; 1:外部32kHz振荡器开启。
16.4.1 RTC控制寄存器高位(RTC_CRH)
RTC_CNT、RTC_ALR或RTC_PRL
等到为1是才能操作
16.4.3 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)—20位的寄存器0~3低 0~15
仅当RTOFF值为’1’时允许进行写操作
16.4.4 RTC预分频器余数寄存器(RTC_DIVH / RTC_DIVL)
与预分配装载寄存器差不多
HAL_RTC_Init
HAL_RTC_GetTime
HAL_RTC_GetDate
HAL_RTC_SetDate
HAL_RTC_SetTime
HAL_RTC_SetAlarm_IT
__HAL_RTC_ALARM_GET_FLAG .h里面
获取时间是将cnt读出来
4. RTC驱动步骤
注意事项:
必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、 RTC_ALR寄存器
对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的 RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器
小实验1:读写RTC时间实验
/-----------------rtc.h------------------------------/
#include "sys.h"
#include "time.h"
#include "uart1.h"
void rtc_init(void);
uint16_t rtc_read_bkr(uint8_t bkrx);
void rtc_write_bkp(uint8_t bkrx,uint16_t data);
void rtc_set_time(struct tm time_data);
void rtc_get_time(void);
/-----------------rtc.c------------------------------/
rtc初始化
RTC_HandleTypeDef rtc_handle = {0};
//rtc初始化
void rtc_init(void)
{
//备份寄存器和RTC的访问 1PWREN,BKPEN
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
//对DBPDBP位来使能 pwr
HAL_PWR_EnableBkUpAccess();
//RTC部分
rtc_handle.Instance = RTC; //寄存器选择
rtc_handle.Init.AsynchPrediv = 32767; //溢出预分频系数
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;//侵入检测--
HAL_RTC_Init(&rtc_handle);
}
底层代码
使能RTC,设置时钟源, 中断配置
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
//使能RTC
__HAL_RCC_RTC_ENABLE();
RCC_OscInitTypeDef osc_initstruct = {0};
RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
osc_initstruct.OscillatorType= RCC_OSCILLATORTYPE_LSE;//选择振荡器
osc_initstruct.LSEState= RCC_LSE_ON;//选择lse的状态
osc_initstruct.PLL.PLLState= RCC_PLL_NONE;//有没有用到锁相环
//配置振荡器 设置时钟源 句柄 选择哪一路
HAL_RCC_OscConfig(&osc_initstruct);
periphclk_initstruct.PeriphClockSelection =RCC_PERIPHCLK_RTC;//配置rtc外设
periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//时钟选择
HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC对三路进行选项
}
指定读取的寄存器
uint16_t rtc_read_bkr(uint8_t bkrx)
{
uint32_t data = 0;//接收返回值
data = HAL_RTCEx_BKUPRead(&rtc_handle,bkrx);//句柄,指定读哪个寄存器
return (uint16_t)data;//返回寄存器读出值
}
指定写的是哪个寄存器
写的数据是16位的数据
void rtc_write_bkp(uint8_t bkrx,uint16_t data)
{
//句柄 哪个寄存器 写的数据
HAL_RTCEx_BKUPWrite(&rtc_handle,bkrx,data);
}
获取时间 两个函数
void rtc_get_time(void)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};
HAL_RTC_GetTime(&rtc_handle, &rtc_time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&rtc_handle, &rtc_date,RTC_FORMAT_BIN);
//HAL库中年是两位的实验需要另外加2000 0X%设置格式为两位
printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n",rtc_date.Year + 2000,rtc_date.Month,rtc_date.Date,rtc_time.Hours,rtc_time.Minutes,
rtc_time.Seconds);
}
设置时间
如果单独设置年月日时分秒就会特别长,实验可以设置一个结构体,但是库函数中已经存在一个结构体可以用
void rtc_set_time(struct tm time_data)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};
rtc_time.Hours = time_data.tm_hour;
rtc_time.Minutes=time_data.tm_min;
rtc_time.Seconds=time_data.tm_sec;
HAL_RTC_SetTime(&rtc_handle, &rtc_time,RTC_FORMAT_BIN);
rtc_date.Year = time_data.tm_year;
rtc_date.Month = time_data.tm_mon;
rtc_date.Date = time_data.tm_mday;
HAL_RTC_SetDate(&rtc_handle, &rtc_date,RTC_FORMAT_BIN);
//按要求对任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器的RTOFF状态位判断
while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle,RTC_FLAG_RTOFF));
}
/----------------------------main----------------------/
main
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "rtc.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
rtc_init();
// led1_on();
// led1_off();
printf("helloworld!\r\n");
//做一个记号,先读一下看看有没有找到记号
if(rtc_read_bkr(1) !=0xA5A5)
{
rtc_write_bkp(1,0xA5A5);
printf("读出来的值为:%X\r\n",rtc_read_bkr(1));
//获取时间,首先里面需要有时间
struct tm time_data;
time_data.tm_year = 24;
time_data.tm_mon =12;
time_data.tm_mday =7;
time_data.tm_hour =13;
time_data.tm_min =13;
time_data.tm_sec =9;
rtc_set_time(time_data);
}
while(1)
{
rtc_get_time();
delay_ms(1000);
}
}
出现的问题
如果这里错了会只等待不中断
小实验2:RTC闹钟实验
/--------------------------rtc--------------------------/
rtc初始化
void rtc_init(void)
{
//备份寄存器和RTC的访问 1PWREN,BKPEN
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
//对DBPDBP位来使能 pwr
HAL_PWR_EnableBkUpAccess();
//RTC部分
rtc_handle.Instance = RTC; //寄存器选择
rtc_handle.Init.AsynchPrediv = 32767; //溢出预分频系数
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;//侵入检测--
HAL_RTC_Init(&rtc_handle);
}
底层代码
使能RTC,设置时钟源, 中断配置
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
//使能RTC
__HAL_RCC_RTC_ENABLE();
RCC_OscInitTypeDef osc_initstruct = {0};
RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
osc_initstruct.OscillatorType= RCC_OSCILLATORTYPE_LSE;//选择振荡器
osc_initstruct.LSEState= RCC_LSE_ON;//选择lse的状态
osc_initstruct.PLL.PLLState= RCC_PLL_NONE;//有没有用到锁相环
//配置振荡器 设置时钟源 句柄 选择哪一路
HAL_RCC_OscConfig(&osc_initstruct);
periphclk_initstruct.PeriphClockSelection =RCC_PERIPHCLK_RTC;//配置rtc外设
periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//时钟选择
HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC对三路进行选项
}
中断函数
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&rtc_handle);//公共处理函数 传进一个参数 有很多的处理函数
}
RTC只支持一个闹钟
中断的内容
闹钟响了之后的内容
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
printf("ring ring ring\r\n");
}
指定读取的寄存器
uint16_t rtc_read_bkr(uint8_t bkrx)
{
uint32_t data = 0;//接收返回值
data = HAL_RTCEx_BKUPRead(&rtc_handle,bkrx);//句柄,指定读哪个寄存器
return (uint16_t)data;//返回寄存器读出值
}
指定写的是哪个寄存器
写的数据是16位的数据
void rtc_write_bkr(uint8_t bkrx,uint16_t data)
{
//句柄 哪个寄存器 写的数据
HAL_RTCEx_BKUPWrite(&rtc_handle,bkrx,data);
}
获取时间 两个函数
void rtc_get_time(void)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};
HAL_RTC_GetTime(&rtc_handle, &rtc_time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&rtc_handle, &rtc_date,RTC_FORMAT_BIN);
//HAL库中年是两位的实验需要另外加2000 0X%设置格式为两位
printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n",rtc_date.Year + 2000,rtc_date.Month,rtc_date.Date,rtc_time.Hours,rtc_time.Minutes,
rtc_time.Seconds);
}
设置时间
如果单独设置年月日时分秒就会特别长,实验可以设置一个结构体,但是库函数中已经存在一个结构体可以用
void rtc_set_time(struct tm time_data)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};
rtc_time.Hours = time_data.tm_hour;
rtc_time.Minutes=time_data.tm_min;
rtc_time.Seconds=time_data.tm_sec;
HAL_RTC_SetTime(&rtc_handle, &rtc_time,RTC_FORMAT_BIN);
rtc_date.Year = time_data.tm_year - 2000;
rtc_date.Month = time_data.tm_mon;
rtc_date.Date = time_data.tm_mday;
HAL_RTC_SetDate(&rtc_handle, &rtc_date,RTC_FORMAT_BIN);
//按要求对任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器的RTOFF状态位判断
while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle,RTC_FLAG_RTOFF));
}
//设置闹钟函数
void rtc_set_alarm(struct tm alarm_data)
{
RTC_AlarmTypeDef alarm = {0};//f1系列只有一个alarm
alarm.Alarm = RTC_ALARM_A;
alarm.AlarmTime.Hours = alarm_data.tm_hour;
alarm.AlarmTime.Minutes = alarm_data.tm_min;
alarm.AlarmTime.Seconds = alarm_data.tm_sec;
HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm,RTC_FORMAT_BIN);//设置闹钟并可以产生中断
}
/--------------------------main.c------------------------/
main
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "rtc.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
rtc_init();
// led1_on();
// led1_off();
printf("helloworld!\r\n");
//做一个记号,先读一下看看有没有找到记号
if(rtc_read_bkr(1) !=0xA5A5)
{
rtc_write_bkr(1,0xA5A5);
printf("读出来的值为:%X\r\n",rtc_read_bkr(1));
//获取时间,首先里面需要有时间
struct tm time_data,alarm_data;
time_data.tm_year = 2024;
time_data.tm_mon =12;
time_data.tm_mday =7;
time_data.tm_hour =13;
time_data.tm_min =13;
time_data.tm_sec =9;
rtc_set_time(time_data);
//闹钟设置
alarm_data.tm_hour =13;
alarm_data.tm_min =13;
alarm_data.tm_sec =12;
rtc_set_alarm(alarm_data);
}
while(1)
{
rtc_get_time();
delay_ms(1000);
}
}
为什么
项目:实时时钟
项目需求
1. OLED屏幕显示当前时间、日期、闹钟等信息;
2. 正常模式下,按下 KEY1 ,进入时间设置模式,此时按下 KEY2 则可以循环跳转修改秒、分、时、日、 月、年;
3. 时间设置模式下,KEY3 增加数值,KEY4 减小数值,再次按下 KEY1 则退出时间设置模式,并保存修改 后的时间;
4. 正常模式下,按下 KEY2,进入闹钟设置模式,此时按下 KEY2 则可以循环跳转修改秒、分、时;
5. 闹钟设置模式下,KEY3 增加数值,KEY4 减小数值,再次按下 KEY1 则退出闹钟设置模式,并保存修改 后的闹钟;
6. 到达闹钟时间后,蜂鸣器响起。按下 KEY3 或 KEY4 停止蜂鸣器。
/-------------------------rtc.h--------------------------------/
rtc
#include "sys.h"
#include "time.h"
#include "uart1.h"
void rtc_init(void);
uint16_t rtc_read_bkr(uint8_t bkrx);
void rtc_write_bkr(uint8_t bkrx,uint16_t data);
void rtc_set_time(struct tm time_data);
void rtc_get_time(void);
void rtc_set_alarm(struct tm alarm_data);
/-------------------------rtc.c--------------------------------/
rtc初始化
RTC_HandleTypeDef rtc_handle = {0};
//rtc初始化
void rtc_init(void)
{
//备份寄存器和RTC的访问 1PWREN,BKPEN
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
//对DBPDBP位来使能 pwr
HAL_PWR_EnableBkUpAccess();
//RTC部分
rtc_handle.Instance = RTC; //寄存器选择
rtc_handle.Init.AsynchPrediv = 32767; //溢出预分频系数
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;//侵入检测--
HAL_RTC_Init(&rtc_handle);
}
底层代码
使能RTC,设置时钟源, 中断配置
//底层代码 使能RTC,设置时钟源, 中断配置
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
//使能RTC
__HAL_RCC_RTC_ENABLE();
RCC_OscInitTypeDef osc_initstruct = {0};
RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
osc_initstruct.OscillatorType= RCC_OSCILLATORTYPE_LSE;//选择振荡器
osc_initstruct.LSEState= RCC_LSE_ON;//选择lse的状态
osc_initstruct.PLL.PLLState= RCC_PLL_NONE;//有没有用到锁相环
HAL_RCC_OscConfig(&osc_initstruct);
//配置振荡器 设置时钟源 句柄 选择哪一路
periphclk_initstruct.PeriphClockSelection =RCC_PERIPHCLK_RTC;//配置rtc外设
periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//时钟选择
HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC对三路进行选项
//设置中断优先级
HAL_NVIC_SetPriority(RTC_Alarm_IRQn,2,2);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
中断函数
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&rtc_handle);//公共处理函数 传进一个参数 有很多的处理函数
}
RTC只支持一个闹钟 中断的内容
闹钟响了之后的内容
//RTC只支持一个闹钟 中断的内容 闹钟响了之后的内容
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
beep_on();
}
指定读取的寄存器
uint16_t rtc_read_bkr(uint8_t bkrx)
{
uint32_t data = 0;//接收返回值
data = HAL_RTCEx_BKUPRead(&rtc_handle,bkrx);//句柄,指定读哪个寄存器
return (uint16_t)data;//返回寄存器读出值
}
指定写的是哪个寄存器
写的数据是16位的数据
void rtc_write_bkr(uint8_t bkrx,uint16_t data)
{
//句柄 哪个寄存器 写的数据
HAL_RTCEx_BKUPWrite(&rtc_handle,bkrx,data);
}
获取时间 两个函数
void rtc_get_time(void)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};
HAL_RTC_GetTime(&rtc_handle, &rtc_time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&rtc_handle, &rtc_date,RTC_FORMAT_BIN);
time_data[0] = rtc_date.Year;
time_data[1] = rtc_date.Month;
time_data[2] = rtc_date.Date;
time_data[3] = rtc_time.Hours;
time_data[4] = rtc_time.Minutes;
time_data[5] = rtc_time.Seconds;
//HAL库中年是两位的实验需要另外加2000 0X%设置格式为两位
// printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n",rtc_date.Year + 2000,rtc_date.Month,rtc_date.Date,rtc_time.Hours,rtc_time.Minutes,
// rtc_time.Seconds);
}
设置时间
如果单独设置年月日时分秒就会特别长,实验可以设置一个结构体,但是库函数中已经存在一个结构体可以用
void rtc_set_time(uint8_t *time_data)
{
RTC_TimeTypeDef rtc_time = {0};
RTC_DateTypeDef rtc_date = {0};
rtc_time.Hours = time_data[3];
rtc_time.Minutes=time_data[4];
rtc_time.Seconds=time_data[5];
HAL_RTC_SetTime(&rtc_handle, &rtc_time,RTC_FORMAT_BIN);
rtc_date.Year = time_data[0];//最大是256 传2000就溢出了
rtc_date.Month = time_data[1];
rtc_date.Date = time_data[2];
HAL_RTC_SetDate(&rtc_handle, &rtc_date,RTC_FORMAT_BIN);
//按要求对任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器的RTOFF状态位判断
while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle,RTC_FLAG_RTOFF));
}
读取闹钟的参数
void rtc_get_alarm(uint8_t *alarm_data)
{
RTC_AlarmTypeDef alarm = {0};
// 中断通道f1系列只有一个闹钟 位 rtc句柄,闹钟,指定闹钟,参数
HAL_RTC_GetAlarm(&rtc_handle, &alarm,RTC_ALARM_A,RTC_FORMAT_BIN);
//接收读出的数据
alarm_data[0] = alarm.AlarmTime.Hours;
alarm_data[1] = alarm.AlarmTime.Minutes;
alarm_data[2] = alarm.AlarmTime.Seconds;
}
设置闹钟函数
void rtc_set_alarm(uint8_t *alarm_data)
{
RTC_AlarmTypeDef alarm = {0};//f1系列只有一个alarm
alarm.Alarm = RTC_ALARM_A;//闹钟A
alarm.AlarmTime.Hours = alarm_data[0];
alarm.AlarmTime.Minutes = alarm_data[1];
alarm.AlarmTime.Seconds = alarm_data[2];
HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm,RTC_FORMAT_BIN);//设置闹钟并可以产生中断
}
/-----------------------beep.c---------------------------------------/
beep
#include "beep.h"
#include "sys.h"
//初始化GPIO函数
void beep_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOB时钟
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_13; // 蜂鸣器对应的引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
HAL_GPIO_Init(GPIOC, &gpio_initstruct);
//关闭蜂鸣器
beep_off();
}
//打开蜂鸣器的函数
void beep_on(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 拉低蜂鸣器引脚,打开蜂鸣器
}
//关闭蜂鸣器的函数
void beep_off(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 拉高蜂鸣器引脚,关闭蜂鸣器
}
/------------------------------key.c------------------------------------/
key
#include "beep.h"
#include "sys.h"
//初始化GPIO函数
void beep_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOB时钟
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_13; // 蜂鸣器对应的引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
HAL_GPIO_Init(GPIOC, &gpio_initstruct);
//关闭蜂鸣器
beep_off();
}
//打开蜂鸣器的函数
void beep_on(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 拉低蜂鸣器引脚,打开蜂鸣器
}
//关闭蜂鸣器的函数
void beep_off(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 拉高蜂鸣器引脚,关闭蜂鸣器
}
/---------------------------key.c---------------------------------------/
#include "key.h"
#include "delay.h"
//初始化GPIO
void key_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;//K0,K1,K2,K3
gpio_initstruct.Mode = GPIO_MODE_INPUT;//推完输入
gpio_initstruct.Pull = GPIO_PULLUP;//由原理图可知默认高电平,按下就低电平
gpio_initstruct.Speed= GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_initstruct);
}
//按键扫描函数
uint8_t key_scan(void)
{
//检测按件是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)//输出用 GPIO_PIN_RESET 低电平
{
//消抖
delay_ms(10);
//再将判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
//如果确定按键按下,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) ==GPIO_PIN_RESET);
//返回按键值--按下返回1,
return KEY_SET;
}
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)//输出用 GPIO_PIN_RESET 低电平
{
//消抖
delay_ms(10);
//再将判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
//如果确定按键按下,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) ==GPIO_PIN_RESET);
//返回按键值--按下返回1,
return KEY_SHIFT;
}
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_2) == GPIO_PIN_RESET)//输出用 GPIO_PIN_RESET 低电平
{
//消抖
delay_ms(10);
//再将判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_2) == GPIO_PIN_RESET){
//如果确定按键按下,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_2) ==GPIO_PIN_RESET);
//返回按键值--按下返回1,
return KEY_UP;
}
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3) == GPIO_PIN_RESET)//输出用 GPIO_PIN_RESET 低电平
{
//消抖
delay_ms(10);
//再将判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3) == GPIO_PIN_RESET){
//如果确定按键按下,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3) ==GPIO_PIN_RESET);
//返回按键值--按下返回1,
return KEY_DOWN;
}
//返回默认值
}
return 0;//松开返回0
}
oled
#include "oled.h"
#include "delay.h"
#include "font.h"
void oled_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
OLED_I2C_SCL_CLK();
OLED_I2C_SDA_CLK();
gpio_initstruct.Pin = OLED_I2C_SCL_PIN;
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_I2C_SCL_PORT, &gpio_initstruct);
gpio_initstruct.Pin = OLED_I2C_SDA_PIN;
HAL_GPIO_Init(OLED_I2C_SDA_PORT, &gpio_initstruct);
}
void oled_i2c_start(void)
{
OLED_SCL_SET();
OLED_SDA_SET();
OLED_SDA_RESET();
OLED_SCL_RESET();
}
void oled_i2c_stop(void)
{
OLED_SCL_SET();
OLED_SDA_RESET();
OLED_SDA_SET();
}
void oled_i2c_ack(void)
{
OLED_SCL_SET();
OLED_SCL_RESET();
}
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i, tmp;
tmp = data;
for(i = 0; i < 8; i++)
{
if((tmp & 0x80) == 0x80)
OLED_SDA_SET();
else
OLED_SDA_RESET();
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
void oled_write_cmd(uint8_t cmd)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x00);
oled_i2c_ack();
oled_i2c_write_byte(cmd);
oled_i2c_ack();
oled_i2c_stop();
}
void oled_write_data(uint8_t data)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x40);
oled_i2c_ack();
oled_i2c_write_byte(data);
oled_i2c_ack();
oled_i2c_stop();
}
void oled_init(void)
{
oled_gpio_init();
delay_ms(100);
oled_write_cmd(0xAE); //设置显示开启/关闭,0xAE关闭,0xAF开启
oled_write_cmd(0xD5); //设置显示时钟分频比/振荡器频率
oled_write_cmd(0x80); //0x00~0xFF
oled_write_cmd(0xA8); //设置多路复用率
oled_write_cmd(0x3F); //0x0E~0x3F
oled_write_cmd(0xD3); //设置显示偏移
oled_write_cmd(0x00); //0x00~0x7F
oled_write_cmd(0x40); //设置显示开始行,0x40~0x7F
oled_write_cmd(0xA1); //设置左右方向,0xA1正常,0xA0左右反置
oled_write_cmd(0xC8); //设置上下方向,0xC8正常,0xC0上下反置
oled_write_cmd(0xDA); //设置COM引脚硬件配置
oled_write_cmd(0x12);
oled_write_cmd(0x81); //设置对比度
oled_write_cmd(0xCF); //0x00~0xFF
oled_write_cmd(0xD9); //设置预充电周期
oled_write_cmd(0xF1);
oled_write_cmd(0xDB); //设置VCOMH取消选择级别
oled_write_cmd(0x30);
oled_write_cmd(0xA4); //设置整个显示打开/关闭
oled_write_cmd(0xA6); //设置正常/反色显示,0xA6正常,0xA7反色
oled_write_cmd(0x8D); //设置充电泵
oled_write_cmd(0x14);
oled_write_cmd(0xAF); //开启显示
oled_fill(0x00);
}
void oled_set_cursor(uint8_t x, uint8_t y)
{
oled_write_cmd(0xB0 + y);
oled_write_cmd((x & 0x0F) | 0x00);
oled_write_cmd(((x & 0xF0) >> 4) | 0x10);
}
void oled_fill(uint8_t data)
{
uint8_t i, j;
for(i = 0; i < 8; i++)
{
oled_set_cursor(0, i);
for(j = 0; j < 128; j++)
oled_write_data(data);
}
}
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)
{
uint8_t i, j, page;
num = num - ' ';
page = size / 8;
if(size % 8)
page++;
for(j = 0; j < page; j++)
{
oled_set_cursor(x, y + j);
for(i = size / 2 * j; i < size /2 * (j + 1); i++)
{
if(size == 12)
oled_write_data(ascii_6X12[num][i]);
else if(size == 16)
oled_write_data(ascii_8X16[num][i]);
else if(size == 24)
oled_write_data(ascii_12X24[num][i]);
}
}
}
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size)
{
while(*p != '\0')
{
oled_show_char(x, y, *p, size);
x += size/2;
p++;
}
}
//void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size)
//{
// uint16_t i, j;
// for(j = 0; j < size/8; j++)
// {
// oled_set_cursor(x, y + j);
// for(i = size *j; i < size * (j + 1); i++)
// {
// if(size == 16)
// oled_write_data(chinese_16x16[N][i]);
// else if(size == 24)
// oled_write_data(chinese_24x24[N][i]);
// }
// }
//}
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N)
{
uint16_t i, j;
for(j = 0; j < 2; j++)
{
oled_set_cursor(x, y + j);
for(i = 16 * j; i < 16 * (j + 1); i++)
oled_write_data(chinese_time[N][i]);
}
}
//布局显示函数
void oled_show_init(void)
{
oled_fill(0x00);
oled_show_string(8,0,"2000",16); //2024
oled_show_chinese(40,0,0); //年
oled_show_string(56,0,"00",16); //07
oled_show_chinese(72,0,1); //月
oled_show_string(88,0,"00",16); //03
oled_show_chinese(104, 0,2); //日
oled_show_string(26, 2, "00", 16); //12
oled_show_char(45, 2, ':', 16); //:
oled_show_string(56, 2, "00", 16); //35
oled_show_char(75, 2, ':', 16); //:
oled_show_string(86, 2, "00", 16); //20
oled_show_chinese(10, 4, 3); //闹
oled_show_chinese(26, 4, 4); //钟
oled_show_char(42, 4, ':', 16); //:
oled_show_string(26, 6, "00", 16); //12
oled_show_char(45, 6, ':', 16); //:
oled_show_string(56, 6, "00", 16); //35
oled_show_char(75, 6, ':', 16); //:
oled_show_string(86, 6, "00", 16); //20
}
//清空两个字符 先清空上半部分然后下半部分
void oled_clear_2char(uint8_t x,uint8_t y)
{
uint8_t i = 0;
oled_set_cursor(x,y);//上半部分清空
for(i = 0;i < 16; i++)
oled_write_data(0x00);
oled_set_cursor(x,y+1);//下半部分清空
for(i = 0;i < 16;i++)
oled_write_data(0x00);
}
//填坑 年 24对 10取模
void oled_show_year(uint8_t num, uint8_t display_flag)
{
if(display_flag)//显示
{
//显示年份坑位 24对 10取模 将其高位地位取出来
oled_show_char(24,0,num/10 + '0',16);
oled_show_char(32,0,num%10 + '0',16);
}
else
//不显示年份的坑位
oled_clear_2char(24,0);
}
//月
void oled_show_month(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(56, 0, num/10 + '0', 16);
oled_show_char(64, 0, num%10 + '0', 16);
}
else
oled_clear_2char(56, 0);
}
//日
void oled_show_day(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(88, 0, num/10 + '0', 16);
oled_show_char(96, 0, num%10 + '0', 16);
}
else
oled_clear_2char(88, 0);
}
//时
void oled_show_hour(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(26, 2, num/10 + '0', 16);
oled_show_char(34, 2, num%10 + '0', 16);
}
else
oled_clear_2char(26, 2);
}
//分
void oled_show_minute(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(56, 2, num/10 + '0', 16);
oled_show_char(64, 2, num%10 + '0', 16);
}
else
oled_clear_2char(56, 2);
}
//秒
void oled_show_second(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(86, 2, num/10 + '0', 16);
oled_show_char(94, 2, num%10 + '0', 16);
}
else
oled_clear_2char(86, 2);
}
//闹钟:时
void oled_show_alarm_hour(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(26, 6, num/10 + '0', 16);
oled_show_char(34, 6, num%10 + '0', 16);
}
else
oled_clear_2char(26, 6);
}
//闹钟:分
void oled_show_alarm_minute(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(56, 6, num/10 + '0', 16);
oled_show_char(64, 6, num%10 + '0', 16);
}
else
oled_clear_2char(56, 6);
}
//闹钟:秒
void oled_show_alarm_second(uint8_t num, uint8_t display_flag)
{
if(display_flag)
{
oled_show_char(86, 6, num/10 + '0', 16);
oled_show_char(94, 6, num%10 + '0', 16);
}
else
oled_clear_2char(86, 6);
}
//显示元素
void oled_show_element(uint8_t num,uint8_t display_flag,uint8_t element)
{
switch(element)
{
case TIME_YEAR :oled_show_year(num,display_flag); break;
case TIME_MONTH :oled_show_month(num,display_flag);break;
case TIME_DAY :oled_show_day(num,display_flag);break;
case TIME_HOUR :oled_show_hour(num,display_flag);break;
case TIME_MINUTE:oled_show_minute(num,display_flag);break;
case TIME_SECOND:oled_show_second(num,display_flag);break;
case ALARM_HOUR :oled_show_alarm_hour(num,display_flag);break;
case ALARM_MINUTE:oled_show_alarm_minute(num,display_flag);break;
case ALARM_SECOND:oled_show_alarm_second(num,display_flag);break;
default : break;
}
}
//时间显示,在while中不停的获取rtc的时间
void oled_show_time_alarm(uint8_t *time,uint8_t *alarm)
{
oled_show_year(time[0],1);
oled_show_month(time[1],1);
oled_show_day(time[2],1);
oled_show_hour(time[3],1);
oled_show_minute(time[4],1);
oled_show_second(time[5],1);
oled_show_alarm_hour(alarm[0],1);
oled_show_alarm_minute(alarm[1],1);
oled_show_alarm_second(alarm[2],1);
}
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp)
{
uint8_t i, j;
for(j = 0; j < height; j++)
{
oled_set_cursor(x, y + j);
for(i = 0; i < width; i++)
oled_write_data(bmp[width * j + i]);
}
}
/-----------------------------led.c-------------------------------------/
led
#include "led.h"
#include "sys.h"
//初始化GPIO函数
void led_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Pull = GPIO_PULLUP;//上拉
gpio_initstruct.Speed= GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&gpio_initstruct);
//关闭LED
led1_off();
}
//点亮LED的函数
void led1_on(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
//熄灭LED1的函数
void led1_off(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
//翻转LED1状态的函数
void led1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
void led3_on(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}
//熄灭LED1的函数
void led3_off(void)
{
//由电路图可知,将GPIO拉低
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}
//翻转LED1状态的函数
void led3_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
main
/--------------------------main----------------------------------------/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "beep.h"
#include "key.h"
#include "oled.h"
#include "rtc.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
uart1_init(115200);
beep_init();
key_init();
oled_init();
rtc_init();
// //oled_clear_2char(86,2);
// oled_show_year(24,1);
// oled_show_element(16, ON,TIME_HOUR);//该函数显示或者不显示
printf("helloworld!\r\n");
uint8_t time_data[6] = {24,7,4,16,8,30};
uint8_t alarm_data[3] = {16,8,40};
uint8_t set_time_shift = TIME_SECOND;//显示修改的坑过,刚开始是秒开始改
uint8_t set_alarm_shift = ALARM_SECOND;
uint8_t set_time_flag = 0;
uint8_t set_alarm_flag = 0;
oled_show_init();
//写入一个记号 无记号做记号,有就不需要
if(rtc_read_bkr(1) != 0xA5A5)
{
rtc_write_bkr(1,0xA5A5);
rtc_set_time(time_data);
rtc_set_alarm(alarm_data);
}
while(1)
{ //获取时钟及闹钟
rtc_get_time(time_data);
rtc_get_alarm(alarm_data);
//并在oled屏幕显示
oled_show_time_alarm(time_data,alarm_data);
//按键扫描 有四个结果
switch(key_scan())
{
case KEY_SET:
//进入时间设置模式, 判断什么时候按下 参数判断结束条件
set_time_flag = 1;
set_time_shift = TIME_SECOND;
while(set_time_flag)
{
//闪动要修改的坑位 年月日时分秒
oled_show_element(time_data[set_time_shift],OFF,set_time_shift);//传入一个数组,然后关掉,元素
delay_ms(100);
oled_show_element(time_data[set_time_shift],ON,set_time_shift);//传入一个数组,然后打开,元素
delay_ms(100);
switch(key_scan())
{
case KEY_SET:
//退出时间设置模式
set_time_flag = 0;
set_time_shift = TIME_SECOND;//挪回起始位置
//保存修改后的时间
rtc_set_time(time_data);
break;
case KEY_SHIFT:
//跳转到下一个需要修改的数据元素
if(set_time_shift-- <= TIME_YEAR)
set_time_shift = TIME_SECOND;
break;
case KEY_UP:
//增加数值
if(set_time_shift == TIME_SECOND || set_time_shift == TIME_MINUTE) //分秒
if(time_data[set_time_shift] < 59)
time_data[set_time_shift]++;
if(set_time_shift == TIME_HOUR) //小时
if(time_data[set_time_shift] < 23)
time_data[set_time_shift]++;
if(set_time_shift == TIME_DAY) //日
if(time_data[set_time_shift] < 31)
time_data[set_time_shift]++;
if(set_time_shift == TIME_DAY) //月
if(time_data[set_time_shift] < 12)
time_data[set_time_shift]++;
if(set_time_shift == TIME_DAY) //年
if(time_data[set_time_shift] < 99)
time_data[set_time_shift]++;
break;
case KEY_DOWN:
//减少数值
if(time_data[set_time_shift] > 0)
time_data[set_time_shift]--;
break;
default:
break;
}
}
break;
case KEY_SHIFT: //为什么会交替
//进入闹钟设置模式
set_alarm_flag=1;
while(set_alarm_flag)
{
//闪动需要修改的坑位
oled_show_element(alarm_data[set_alarm_shift - ALARM_HOUR],OFF,set_alarm_shift);//闹钟是从8开始,如果所以刚开始是8的话就越界了,因为他只有3个数所以要对alarm_hour进行偏移
delay_ms(100);
oled_show_element(alarm_data[set_alarm_shift - ALARM_HOUR],ON,set_alarm_shift);
delay_ms(100);
switch(key_scan())
{
case KEY_SET:
//退出闹钟模式
set_alarm_flag = 0;
set_alarm_shift = ALARM_SECOND;
//保存修改后的闹钟
rtc_set_alarm(alarm_data);
break;
case KEY_SHIFT:
//跳转到下一个需要修改的元素
if(set_alarm_shift-- <= ALARM_HOUR)
set_alarm_shift = ALARM_SECOND;
break;
case KEY_UP:
//增加数值
if(alarm_data[set_alarm_shift - ALARM_HOUR] < 59)
alarm_data[set_alarm_shift - ALARM_HOUR]++;
break;
case KEY_DOWN:
if(alarm_data[set_alarm_shift - ALARM_HOUR] > 0)
alarm_data[set_alarm_shift - ALARM_HOUR]--;
break;
}
}
break;
case KEY_UP:
case KEY_DOWN:
//停止蜂鸣器
beep_off();
break;
default:
break;
}
}
}