STM32F1基于标准库工程RTC秒钟断+定时闹钟功能示例

该示例展示了如何在STM32F1系列微控制器上使用标准库配置RTC秒中断和闹钟功能。通过RTC_Config()函数设置RTC时钟源、预分频器和中断,RTC_Get()和RTC_Set()函数用于获取和设置时间。在主程序中,RTC_IRQHandler()处理秒中断,RTCAlarm_IRQHandler()处理闹钟中断,同时提供了中断标志清除和时间更新。
摘要由CSDN通过智能技术生成

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
以下是基于HAL库的STM32F1系列使用RTC闹钟唤醒停止模式的代码示例: ```c #include "stm32f1xx_hal.h" RTC_HandleTypeDef hrtc; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_RTC_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_RTC_Init(); while (1) { // 进入低功耗模式 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后继续执行 } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } void MX_RTC_Init(void) { RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; RTC_AlarmTypeDef sAlarm; hrtc.Instance = RTC; hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } sTime.Hours = 0x00; sTime.Minutes = 0x00; sTime.Seconds = 0x00; sTime.H12 = RTC_HOURFORMAT_24; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } sAlarm.AlarmTime.Hours = 0x00; sAlarm.AlarmTime.Minutes = 0x01; sAlarm.AlarmTime.Seconds = 0x00; sAlarm.AlarmTime.H12 = RTC_HOURFORMAT_24; sAlarm.AlarmMask = RTC_ALARMMASK_NONE; sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; sAlarm.AlarmDateWeekDay = 0x1; sAlarm.Alarm = RTC_ALARM_A; if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } if (HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x32F2) != HAL_OK) { Error_Handler(); } if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x32F2) { Error_Handler(); } } void Error_Handler(void) { while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { } #endif void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) { // RTC闹钟中断回调函数 // 在这里执行唤醒后的操作 // ... } ``` 这段代码初始化了RTC模块,并设置了闹钟的时间为每分钟的第一秒。在主循环中,通过调用`HAL_PWR_EnterSTOPMode()`函数进入低功耗模式,等待RTC闹钟中断唤醒。在`HAL_RTC_AlarmAEventCallback()`函数中,可以执行唤醒后的操作。请根据实际需求修改代码。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值