STM32F1基于标准库工程RTC秒钟断+定时闹钟功能示例
-
🔖使用正点原子标准库工程模板创建。
-
📍参考:
https://mp.weixin.qq.com/s/0oLpg9Kjlfo4Z9q0s6lZuQ
-
📋串口调试打印信息:
-
🚩本文仅针对
STM32F1
系列标准库函数使用。不适用于其他型号!!!
📗工程功能介绍
📜基于STM32F103,使用的是标准库,套用正点原子工程模板,使用STM32的RTC时钟系统中的秒中断和RTC 闹钟报警中断来执行相对应的代码。这里仅演示各中断功能响应执行的代码。可以参考此功能模块自行移植。
📓RTC秒中断和闹钟驱动
- 📝RTC.C
#include "rtc.h"
_calendar_obj calendar; //时钟结构体
const u8 mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static void RTC_Alarm_EXIT(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_ClearITPendingBit(EXTI_Line17);
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Line = EXTI_Line17;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
/**
* @brief Configure the RTC peripheral by selecting the clock source.
* @param None
* @retval None
*/
void RTC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 使能PWR和BKP时钟 */
/* Enable PWR and BKP clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* 使能对后备寄存器的访问 */
/* Allow access to BKP Domain */
PWR_BackupAccessCmd(ENABLE);
/* 复位BKP寄存器 */
/* Reset Backup Domain */
BKP_DeInit();
/* 将 RTC时钟设置为LSE这个32.768KHZ的晶振*/
/* Enable LSE */
RCC_LSEConfig(RCC_LSE_ON);
/* Wait till LSE is ready */
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{}
/* Select LSE as RTC Clock Source */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* 使能RTC Clock */
/* Enable RTC Clock */
RCC_RTCCLKCmd(ENABLE);
/* 等待同步 */
/* Wait for RTC registers synchronization */
RTC_WaitForSynchro();
/* 等待对RTC寄存器最后的写操作完成*/
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* 使能RTC秒中断 */
/* Enable the RTC Second */
RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒钟断和闹钟中断
RTC_ITConfig(RTC_IT_ALR, ENABLE);//使能秒钟断和闹钟中断
/* 等待对RTC寄存器最后的写操作完成*/
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* 配置了预分频值: 设置RTC时钟周期为1s */
/* Set RTC prescaler: set RTC period to 1sec */
RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
/* 等待对RTC寄存器最后的写操作完成*/
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* Enable the RTC Alarm Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;//RTC全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn; //闹钟中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //比RTC全局中断的优先级高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
RTC_Alarm_EXIT();
}
u8 Is_Leap_Year(u16 pyear)
{
if(pyear % 4 == 0) //首先需能被4整除
{
if(pyear % 100 == 0)
{
if(pyear % 400 == 0) return 1; //如果以00结尾,还要能被400整除
else return 0;
}
else
return 1;
}
else
return 0;
}
/*
得到当前的时间
成功返回0,错误返回其它
*/
u8 RTC_Get(void)
{
static u16 dayCount = 0;
u32 secCount = 0;
volatile u32 tmp = 0;
u16 tmp1 = 0;
secCount = RTC_GetCounter();
tmp = secCount / 86400; //得到天数
if(dayCount != tmp) //超过一天
{
dayCount = tmp;
tmp1 = 1970; //从1970年开始
while(tmp >= 365)
{
if(Is_Leap_Year(tmp1))//是闰年
{
if(tmp >= 366)
tmp -= 366; //减掉闰年的天数
else
{
// tmp1++;
break;
}
}
else
tmp -= 365; //平年
tmp1++;
}
calendar.w_year = tmp1; //得到年份
tmp1 = 0;
while(tmp >= 28) //超过一个月
{
if(Is_Leap_Year(calendar.w_year) && tmp1 == 1) //当年是闰年且轮循到2月
{
if(tmp >= 29)
tmp -= 29;
else
break;
}
else
{
if(tmp >= mon_table[tmp1]) //平年
tmp -= mon_table[tmp1];
else
break;
}
tmp1++;
}
calendar.w_month = tmp1 + 1; //得到月份,tmp1=0表示1月,所以要加1
calendar.w_date = tmp + 1; //得到日期,因为这一天还没过完,所以tmp只到其前一天,但是显示的时候要显示正常日期
}
tmp = secCount % 86400; //得到秒钟数
calendar.hour = tmp / 3600; //小时
calendar.min = (tmp % 3600) / 60; //分钟
calendar.sec = (tmp % 3600) % 60; //秒
return 0;
}
/*
*设置时钟
*把输入的时钟转换为秒钟
*以1970年1月1日为基准
*1970~2099年为合法年份
返回值:0,成功;其它:错误
*/
u8 RTC_Set(u16 year, u8 mon, u8 day, u8 hour, u8 min, u8 sec)
{
u16 t;
u32 secCount = 0;
if(year < 1970 || year > 2099)
return 1;//
for(t = 1970; t < year; t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))//闰年
secCount += 31622400; //闰年的秒钟数
else
secCount += 31536000;
}
mon -= 1; //先减掉一个月再算秒数(如现在是5月10日,则只需要算前4个月的天数,再加上10天,然后计算秒数)
for(t = 0; t < mon; t++)
{
secCount += (u32)mon_table[t] * 86400; //月份秒钟数相加
if(Is_Leap_Year(year) && t == 1)
secCount += 86400; //闰年,2月份增加一天的秒钟数
}
secCount += (u32)(day - 1) * 86400; //把前面日期的秒钟数相加(这一天还没过完,所以-1)
secCount += (u32)hour * 3600; //小时秒钟数
secCount += (u32)min * 60; //分钟秒钟数
secCount += sec;
// RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
// PWR_BackupAccessCmd(ENABLE);
RTC_SetCounter(secCount);//设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Get();//更新时间
return 0;
}
- 📝RTC.H
#ifndef __RTC_H
#define __RTC_H
#include "stm32f10x.h"
//时间结构体
typedef struct
{
vu8 hour;
vu8 min;
vu8 sec;
//公历年月日周
vu16 w_year;
vu8 w_month;
vu8 w_date;
vu8 week;
}_calendar_obj;
extern _calendar_obj calendar;
void RTC_Config(void);
void RTC_Init(void);
u8 RTC_Set(u16 year,u8 mon,u8 day,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);
#endif
📑main主程序
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h"
#include <stdio.h>
volatile unsigned char RTC_IT_SEC_flag = 0;
/* Private function prototypes -----------------------------------------------*/
void LEDInit(void);
/* Private functions ---------------------------------------------------------*/
/**
* @brief Configures LED GPIO.
* @retval None
*/
void LEDInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable the GPIO Clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
/* Configure the GPIO pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
int main(void)
{
volatile u8 t = 0;
delay_init(); //延时函数初始化
/* Configure RTC */
RTC_Config();
RTC_Set(2023, 6, 14, 12, 27, 5); //
RTC_EnterConfigMode();
RTC_SetAlarm(6 + RTC_GetCounter());//配置下次闹钟
RTC_WaitForLastTask();
RTC_ExitConfigMode();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LEDInit();
while(1)
{
/* Show Time */
if(t != calendar.sec)
// if(RTC_IT_SEC_flag)
{
t = calendar.sec;
printf("NowTime:%d 年 %d 月 %d 日 %d 时 %d 分 %d 秒 \r\n ", \
calendar.w_year, calendar.w_month, calendar.w_date, calendar.hour, calendar.min, calendar.sec);
// RTC_IT_SEC_flag = 0;
}
}
}
/**
* @brief This function handles RTC global interrupt request.
* @param None
* @retval None
*/
void RTC_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_SEC) != RESET) //秒钟中断
{
/* Clear the RTC Second interrupt */
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_Get();//更新时间
// RTC_IT_SEC_flag = 1;
/* Toggle LED */
GPIOE->ODR ^= GPIO_Pin_5;
printf("RTC_IT_SEC!\r\n");
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
}
}
void RTCAlarm_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_ALR) != RESET) //ALR中断
{
EXTI_ClearITPendingBit(EXTI_Line17);
RTC_WaitForLastTask();
printf("Alarm clock!\r\n");
/* Clear the RTC ALR interrupt */
RTC_ClearITPendingBit(RTC_IT_ALR);
PWR_BackupAccessCmd(ENABLE);
RTC_EnterConfigMode();
RTC_SetAlarm(6 + RTC_GetCounter());//配置下次闹钟
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
RTC_ExitConfigMode();
PWR_BackupAccessCmd(DISABLE);
}
RTC_ClearITPendingBit(RTC_IT_OW);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
}
📚工程源码
链接:https://pan.baidu.com/s/1Zl6vd0h_uHkYnrfIYxtmMQ
提取码:2xk3