STM32RTC秒中断--基于HAL库(第九届蓝桥杯嵌入式省赛)
文章目录
- 前言
- 一、CubeMX配置(第九届真题完整版)
- 二、代码相关定义、声明
-
- 1.函数声明
- 2.宏定义
- 3.变量定义
- 三、主要函数
-
- 1.按键扫描
- 2.Setting模式
- 3.RTC秒中断
- 4.Main函数
- 四、实验结果
- 五、源码(转载请注明出处)
- 总结
前言
相关说明:
开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第九届蓝桥杯嵌入式真题
题目难点:在保证按键反应高效的情况下解决LED灯闪烁与LCD显示冲突
CubeMX配置、主要函数代码及说明:
一、CubeMX配置(第九届真题完整版)
1.使能外部高速时钟:
2.配置时钟树:
3.GPIO:
4.RTC(除了图中配置,其他均默认即可):
下面部分可以不用配置,后面会在代码里面配置(中断忽略日期、小时、分钟,不忽略秒)
5.TIM3(PWM输出):
6.TIM6(250ms中断):
8.I2C:
二、代码相关定义、声明
1.函数声明
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//Tim6中断函数void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc);//RTC闹钟中断函数void EEPROM_WriteBuff(uint8_t addr,uint8_t *writeBuff,uint32_t numToWrite);//EEPROM写数据函数void EEPROM_ReadBuff(uint8_t addr,uint8_t *readBuff,uint32_t numToRead);//EEPROM读数据函数void Save_Addr_Change(); //EEPROM读取地址改变void Get_Time(); //得到当前时间void Set_Alarm(); //设置闹钟时间void Show_Now_Time(); //显示当前时间void KEY_Scan() ; //按键扫描void PWM_LED(); //调整PWM和Tim6void Setting_Time(); //设置电子定时器时间(Setting模式)void Time_Change_Mode(); //电子定时器模式改变void Time_Rule(uint8_t *Set_Time_buff,uint8_t Set_Time_dex);//时,分,秒时间调整规则
2.宏定义
#define LED_GPIO_PORTGPIOC#define LED1_GPIO_PIN GPIO_PIN_8#define LED2_GPIO_PIN GPIO_PIN_9#define LED3_GPIO_PIN GPIO_PIN_10#define LED4_GPIO_PIN GPIO_PIN_11#define LED5_GPIO_PIN GPIO_PIN_12#define LED6_GPIO_PIN GPIO_PIN_13#define LED7_GPIO_PIN GPIO_PIN_14#define LED8_GPIO_PIN GPIO_PIN_15#define ONGPIO_PIN_RESET#define OFFGPIO_PIN_SET#define LED1(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)#define LED2(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)#define LED3(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)#define LED4(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)#define LED5(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED5_GPIO_PIN,a)#define LED6(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED6_GPIO_PIN,a)#define LED7(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED7_GPIO_PIN,a)#define LED8(a)HAL_GPIO_WritePin(LED_GPIO_PORT,LED8_GPIO_PIN,a)#define LED1_TOGGLE() HAL_GPIO_TogglePin(LED_GPIO_PORT,LED1_GPIO_PIN)#define KEY1_GPIO_PORTGPIOB#define KEY1_GPIO_PIN GPIO_PIN_0#define KEY2_GPIO_PORT GPIOB#define KEY2_GPIO_PINGPIO_PIN_1#define KEY3_GPIO_PORT GPIOB#define KEY3_GPIO_PINGPIO_PIN_2#define KEY4_GPIO_PORT GPIOA#define KEY4_GPIO_PINGPIO_PIN_0
3.变量定义
uint8_tSave_Addr;//存储地址5个 0 8 16 24 32uint8_tClock_Statue=2;//定时器当前状态uint8_tClock_Statue_old=2;//定时器上一状态uint8_t linshi=0;//临时修改时间标志位uint16_t Ypos;//LCD Y坐标uint8_t Xpos;//LCD X坐标uint16_t Ypos_old=14*16;//初始Y坐标(时间第一位数据坐标)uint8_t Xpos_old=5*24;//初始X坐标(时间第一位数据坐标)uint8_t Ypos_step=16;//LCD中每个字符宽度uint8_t Xpos_step=24;//LCD中每个字符高度uint16_t Ypos_end=6*16;//末尾Y坐标(时间最后一位数据坐标)uint16_t Xpos_end=9*24;//末尾X坐标(时间最后一位数据坐标)RTC_TimeTypeDefSet_Time_Struct;//RTC设置时间结构体RTC_TimeTypeDefGet_Time_Struct;//RTC得到时间结构体RTC_DateTypeDefGet_Date_Struct;//RTC得到日期结构体RTC_TimeTypeDef Now_Time_Struct;//RTC当前时间结构体(暂停需重新初始化RTC,时间会被配置时的初始值覆盖,用于在暂停之前保存时间)RTC_AlarmTypeDefAlarm_Struct;//RTC闹钟结构体uint8_t writeBuff[6];//发送数据数组uint8_t readBuff[6];//读取数据数组char str[30];//str用于转字符串
三、主要函数
1.按键扫描
void KEY_Scan() {uint8_t Press_Time=0;if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){Save_Addr+=8;//EEPROM地址改变Save_Addr%=40;//到指定地址后重置Save_Addr_Change();EEPROM_ReadBuff(Save_Addr,readBuff,sizeof(readBuff));//读时间Set_Time(readBuff[0]*10+readBuff[1],readBuff[2]*10+readBuff[3],readBuff[4]*10+readBuff[5]);//修改定时器时间Get_Time();Set_Alarm();Show_Now_Time();while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);}}else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(100);//100msPress_Time++;}if(Press_Time<8)//短按{if(Clock_Statue!=3 && Clock_Statue!=0){Now_Time_Struct.Hours=Get_Time_Struct.Hours;Now_Time_Struct.Minutes=Get_Time_Struct.Minutes;Now_Time_Struct.Seconds=Get_Time_Struct.Seconds;HAL_RTC_DeInit(&hrtc);}Setting_Time();if(!linshi && Clock_Statue!=0)//如果不是零时设置则配置为Setting之前的时间{HAL_RTC_Init(&hrtc);Set_Time(Now_Time_Struct.Hours,Now_Time_Struct.Minutes,Now_Time_Struct.Seconds);Get_Time();Set_Alarm();Show_Now_Time();}linshi=0;}}}else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(100);//100msPress_Time++;}if(Press_Time>8)//长按{Clock_Statue=0;//取消定时器运行HAL_RTC_DeInit(&hrtc);PWM_LED();Clock_Statue_old=0;}else//短按{if(Clock_Statue_old==2)//暂停/恢复切换{Clock_Statue=3;//暂停PWM_LED();Clock_Statue_old=3;Now_Time_Struct.Hours=Get_Time_Struct.Hours;//保存暂停时的时间Now_Time_Struct.Minutes=Get_Time_Struct.Minutes;Now_Time_Struct.Seconds=Get_Time_Struct.Seconds;HAL_RTC_DeInit(&hrtc);}else if(Clock_Statue_old==3){Clock_Statue=2;//继续PWM_LED();Clock_Statue_old=2;HAL_RTC_Init(&hrtc);Set_Time(Now_Time_Struct.Hours,Now_Time_Struct.Minutes,Now_Time_Struct.Seconds);//设定定时器为暂停前的时间Set_Alarm();//开启秒中断Show_Now_Time();//显示当前时间}}Time_Change_Mode();}}}
2.Setting模式
void Setting_Time(){uint8_t Press_Time=0,Set_Time_buff[3],Set_Time_dex=0;if(Clock_Statue!=0)//如果定时器没有被中止{Clock_Statue_old=Clock_Statue;Clock_Statue=1;Time_Change_Mode();//设置模式}Ypos=Ypos_old;Xpos=Xpos_old;EEPROM_ReadBuff(Save_Addr,readBuff,sizeof(readBuff));//从EEPROM中读取时间Set_Time_buff[0]=readBuff[0]*10+readBuff[1];Set_Time_buff[1]=readBuff[2]*10+readBuff[3];Set_Time_buff[2]=readBuff[4]*10+readBuff[5];LCD_SetBackColor(White);//显示从EEPROM中读取的时间LCD_SetTextColor(Blue);LCD_DisplayChar(Xpos_old,Ypos_old-3*Ypos_step,Set_Time_buff[1]/10+48);LCD_DisplayChar(Xpos_old,Ypos_old-4*Ypos_step,Set_Time_buff[1]%10+48);LCD_DisplayChar(Xpos_old,Ypos_old-6*Ypos_step,Set_Time_buff[2]/10+48);LCD_DisplayChar(Xpos_old,Ypos_old-7*Ypos_step,Set_Time_buff[2]%10+48);LCD_SetBackColor(Blue);//待修改部分LCD_SetTextColor(White);LCD_DisplayChar(Xpos_old,Ypos_old-0*Ypos_step,Set_Time_buff[0]/10+48);LCD_DisplayChar(Xpos_old,Ypos_old-1*Ypos_step,Set_Time_buff[0]%10+48);while(1){LCD_DisplayChar(Xpos,Ypos,Set_Time_buff[Set_Time_dex]/10+48);LCD_DisplayChar(Xpos,Ypos-Ypos_step,Set_Time_buff[Set_Time_dex]%10+48);if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(100);//100msPress_Time++;}if(Press_Time>8)//长按退出编辑模式{writeBuff[0]=Set_Time_buff[0]/10;writeBuff[1]=Set_Time_buff[0]%10;writeBuff[2]=Set_Time_buff[1]/10;writeBuff[3]=Set_Time_buff[1]%10;writeBuff[4]=Set_Time_buff[2]/10;writeBuff[5]=Set_Time_buff[2]%10;EEPROM_WriteBuff(Save_Addr,writeBuff,sizeof(writeBuff));//储存时间至指定地址//Set_Time(Set_Time_buff[0],Set_Time_buff[1],Set_Time_buff[2]);//更新时间LCD_SetBackColor(White);//恢复颜色LCD_SetTextColor(Blue);LCD_DisplayChar(Xpos,Ypos,Set_Time_buff[Set_Time_dex]/10+48);LCD_DisplayChar(Xpos,Ypos-Ypos_step,Set_Time_buff[Set_Time_dex]%10+48);Clock_Statue=Clock_Statue_old;//状态恢复Time_Change_Mode();//修改状态break;//退出循环}else//短按切换至下一数字{Press_Time=0;//长按标志清零LCD_SetBackColor(White);//当前数字颜色恢复LCD_SetTextColor(Blue);LCD_DisplayChar(Xpos,Ypos,Set_Time_buff[Set_Time_dex]/10+48);LCD_DisplayChar(Xpos,Ypos-Ypos_step,Set_Time_buff[Set_Time_dex]%10+48);LCD_SetBackColor(Blue);//下一数字高亮LCD_SetTextColor(White);Ypos-=3*Ypos_step;//纵坐标减少三步if(Ypos<Ypos_end)//下一个无数字 回头{Ypos=Ypos_old;}Set_Time_dex++;Set_Time_dex%=3;LCD_DisplayChar(Xpos,Ypos,Set_Time_buff[Set_Time_dex]/10+48);LCD_DisplayChar(Xpos,Ypos-Ypos_step,Set_Time_buff[Set_Time_dex]%10+48);}}}//KEY2else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)//++{HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(100);//100msPress_Time++;if(Press_Time>8)//长按快速递增{if(Press_Time==255)Press_Time=9;Set_Time_buff[Set_Time_dex]++;Time_Rule(Set_Time_buff,Set_Time_dex);LCD_DisplayChar(Xpos,Ypos,Set_Time_buff[Set_Time_dex]/10+48);LCD_DisplayChar(Xpos,Ypos-Ypos_step,Set_Time_buff[Set_Time_dex]%10+48);}}if(Press_Time<=8)//短按递增一次{Set_Time_buff[Set_Time_dex]++;Time_Rule(Set_Time_buff,Set_Time_dex);}Press_Time=0;//长按标志清零}}//KEY3else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET && Clock_Statue!=0)//零时不保存{HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET){while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);linshi=1;HAL_RTC_Init(&hrtc);Set_Time(Set_Time_buff[0],Set_Time_buff[1],Set_Time_buff[2]);//更新时间Get_Time();Set_Alarm();//开启秒中断LCD_SetBackColor(White);//恢复颜色LCD_SetTextColor(Blue);Show_Now_Time();//显示当前时间Clock_Statue=Clock_Statue_old;//状态恢复Time_Change_Mode();//修改状态break;//退出循环}}//KEY4}}
3.RTC秒中断
void Get_Time(){HAL_RTC_GetTime(&hrtc,&Get_Time_Struct,RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc,&Get_Date_Struct,RTC_FORMAT_BIN); }void Set_Alarm(){Alarm_Struct.AlarmTime.Hours=Get_Time_Struct.Hours;Alarm_Struct.AlarmTime.Minutes=Get_Time_Struct.Minutes;Alarm_Struct.AlarmTime.Seconds=Get_Time_Struct.Seconds+1;//下一秒进入中断if(Alarm_Struct.AlarmTime.Seconds==60)Alarm_Struct.AlarmTime.Seconds=0;HAL_RTC_SetAlarm_IT(&hrtc,&Alarm_Struct,RTC_FORMAT_BIN);//设置闹钟}void Show_Now_Time(){sprintf(str," %02d:%02d:%02d ",Get_Time_Struct.Hours,Get_Time_Struct.Minutes,Get_Time_Struct.Seconds);LCD_DisplayStringLine(Line5,(unsigned char*)str);}
实际应用:
1.初始化(让秒中断从一开始就等待执行)(主循环之前)
EEPROM_ReadBuff(Save_Addr,readBuff,sizeof(readBuff));//读取EEPROM内时间Set_Time(readBuff[0]*10+readBuff[1],readBuff[2]*10+readBuff[3],readBuff[4]*10+readBuff[5]);//更新定时器时间Get_Time();//获取时间/*时钟参数配置*/Alarm_Struct.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS|RTC_ALARMMASK_MINUTES; Alarm_Struct.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;Alarm_Struct.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;Alarm_Struct.AlarmDateWeekDay = 0x1;Alarm_Struct.Alarm=RTC_ALARM_A;/*开启秒中断*/Set_Alarm();
2.按键切换初始时间
切换之后要读取切换后的时间,并重新设定闹钟。
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET){Save_Addr+=8;//EEPROM地址改变Save_Addr%=40;//到指定地址后重置Save_Addr_Change();EEPROM_ReadBuff(Save_Addr,readBuff,sizeof(readBuff));//读时间Set_Time(readBuff[0]*10+readBuff[1],readBuff[2]*10+readBuff[3],readBuff[4]*10+readBuff[5]);//修改定时器时间Get_Time();//得到当前时间Set_Alarm();//设置下一秒的中断Show_Now_Time();//显示当前时间(如果没有这句,第一次显示的时间将是中断后的,也就是初始时间+1秒)while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);}}
3.秒中断回调函数(中断函数)
在秒中断服务函数中一共做三件事:获取当前时间;根据时间设置下一秒的闹钟;更新LCD显示;
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc){/*恭喜你发现本篇博客至宝--RTC秒中断三件套!!! */Get_Time();//得到当前时间(放在回调函数的目的是读取中断后的时间)Set_Alarm();//设置下一秒的闹钟(如此反复)Show_Now_Time();//显示当前时间//}
4.Main函数
int main(void){ /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); I2CInit(); MX_RTC_Init(); MX_TIM6_Init(); MX_TIM3_Init(); /* USER CODE BEGIN 2 */TIM6->CNT=0;HAL_TIM_Base_Start_IT(&htim6);//开启定时器HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);//开启PWMLCD_Init();//LCD初始化EEPROM_ReadBuff(Save_Addr,readBuff,sizeof(readBuff));//读取EEPROM内时间Set_Time(readBuff[0]*10+readBuff[1],readBuff[2]*10+readBuff[3],readBuff[4]*10+readBuff[5]);//更新定时器时间Get_Time();//获取时间Alarm_Struct.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS//时钟参数配置 |RTC_ALARMMASK_MINUTES; Alarm_Struct.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; Alarm_Struct.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; Alarm_Struct.AlarmDateWeekDay = 0x1;Alarm_Struct.Alarm=RTC_ALARM_A;Set_Alarm();LCD_Clear(Blue);LCD_SetBackColor(Blue);LCD_DisplayStringLine(Line0,(unsigned char*)" ");LCD_SetBackColor(White);LCD_SetTextColor(Blue);LCD_DisplayStringLine(Line1,(unsigned char*)" ");LCD_DisplayStringLine(Line2,(unsigned char*)" No1");LCD_DisplayStringLine(Line3,(unsigned char*)" ");LCD_DisplayStringLine(Line4,(unsigned char*)" ");LCD_DisplayStringLine(Line5,(unsigned char*)" ");LCD_DisplayStringLine(Line6,(unsigned char*)" ");LCD_DisplayStringLine(Line7,(unsigned char*)"Running ");LCD_DisplayStringLine(Line8,(unsigned char*)" ");LCD_SetBackColor(Blue);LCD_DisplayStringLine(Line9,(unsigned char*)" "); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */LCD_SetBackColor(White);LCD_SetTextColor(Blue);sprintf(str," %02d:%02d:%02d ",Get_Time_Struct.Hours,Get_Time_Struct.Minutes,Get_Time_Struct.Seconds);LCD_DisplayStringLine(Line5,(unsigned char*)str); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */KEY_Scan();} /* USER CODE END 3 */}
四、实验结果
五、源码(转载请注明出处)
总结
以上就是全部内容,如有错误请批评指正。