基于STM32F103C8T6的RTC闹钟(采用LSI时钟源)

续上一个STM32F103C8T6RTC实时时钟!!!

        实现功能:增加RTC_Alarm闹钟中断功能,当RTC_ALR寄存器中的值等于RTC_CNT寄存器中的值时点亮PC13的LED灯。

1. 配置RTC的RTC_Alarm中断

void RTC_Alarm_Config(void)
{	
    NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}

2. 配置RTC的初始化和使能RTC_Alarm中断

void MyRTC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	
	PWR_BackupAccessCmd(ENABLE);
	
	RTC_Alarm_Config();  //配置中断和优先级
	
	if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A9)
	{
		RCC_LSICmd(ENABLE);
		while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
		RTC_SetPrescaler(40000 - 1); //LSI时钟频率为40Khz,
		//软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值
		//读取RTC寄存器状态时必须等待同步,配置寄存器状态时必须等上一个任务完成
		
		RTC_WaitForLastTask();
		RTC_WaitForSynchro();
		RTC_ITConfig(RTC_IT_ALR, ENABLE);  //使能RTC_IT_ALR闹钟中断
		
		RTC_WaitForLastTask();
		
		MyRTC_SetTime();  //设置初始值
		
		MyRTC_ALarm_SetTime();    //设置闹钟值
		
		BKP_WriteBackupRegister(BKP_DR1, 0xA5A9);
	}
	else
	{
		RCC_LSICmd(ENABLE);				//即使不是第一次配置,也需要再次开启LSI时钟
		while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
        RTC_ITConfig(RTC_IT_ALR, ENABLE);  // 使能RTC_IT_ALR闹钟中断
		RTC_WaitForLastTask();
        RTC_WaitForSynchro();
	}
	
}

3. 中断函数

void RTC_IRQHandler(void)
{
	if(RTC_GetITStatus(RTC_IT_ALR)== SET)//闹钟中断
	{
		LED1_ON(); // 到了闹钟设定的时间PC13的LED灯亮
		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	   
  	} 				  								 
	RTC_WaitForLastTask();
}

4. 设置闹钟时间

闹钟时间可以像RTC按键输入时间一样设置,这里就没写这个了

void MyRTC_ALarm_SetTime(void)
{
	 // 设置闹钟时间为2024年7月27日
    uint16_t alarm_year = 2024;
    uint8_t alarm_month = 7;
    uint8_t alarm_day = 27;
    uint8_t alarm_hour = 17;
    uint8_t alarm_minute = 1;
    uint8_t alarm_second = 55;
    
    struct tm alarm_time;
    time_t alarm_seconds;

    alarm_time.tm_year = alarm_year - 1900;
    alarm_time.tm_mon = alarm_month - 1;
    alarm_time.tm_mday = alarm_day;
    alarm_time.tm_hour = alarm_hour;
    alarm_time.tm_min = alarm_minute;
    alarm_time.tm_sec = alarm_second;

    // 将闹钟时间转换为秒
    alarm_seconds = mktime(&alarm_time) - 8 * 60 * 60; // 东八区调整

    // 设置闹钟时间
    RTC_SetAlarm(alarm_seconds);
    RTC_WaitForLastTask();
}

5. 附

RTC.c 

main.c和CSDN一致

#include "stm32f10x.h"                  // Device header
#include <time.h>
#include "OLED.h"
#include "LED.h"

typedef enum {
    YEAR,
    MONTH,
    DAY,
    HOUR,
    MINUTE,
    SECOND,
	UNIT_COUNT,
	NOS
} TimeUnit;

TimeUnit current_unit = NOS;

uint16_t MyRTC_Time[] = {2023, 7, 27, 16, 59, 55};	//定义全局的时间数组,数组内容分别为年、月、日、时、分、秒

void MyRTC_SetTime(void);				//函数声明
void MyRTC_ALarm_SetTime(void);
void RTC_Alarm_Config(void);

//如果LSE无法起振导致程序卡死在初始化函数中
//可将初始化函数替换为下述代码,使用LSI当作RTCCLK
//LSI无法由备用电源供电,故主电源掉电时,RTC走时会暂停

void MyRTC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	
	PWR_BackupAccessCmd(ENABLE);
	
	RTC_Alarm_Config();  //配置中断和优先级
	
	if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A9)
	{
		RCC_LSICmd(ENABLE);
		while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
		RTC_SetPrescaler(40000 - 1); //LSI时钟频率为40Khz,
		//软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值
		//读取RTC寄存器状态时必须等待同步,配置寄存器状态时必须等上一个任务完成
		
		RTC_WaitForLastTask();
		RTC_WaitForSynchro();
		RTC_ITConfig(RTC_IT_ALR, ENABLE);  //使能RTC_IT_ALR闹钟中断
		
		RTC_WaitForLastTask();
		
		MyRTC_SetTime();  //设置初始值
		
		MyRTC_ALarm_SetTime();    //设置闹钟值
		
		BKP_WriteBackupRegister(BKP_DR1, 0xA5A9);
	}
	else
	{
		RCC_LSICmd(ENABLE);				//即使不是第一次配置,也需要再次开启LSI时钟
		while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
        RTC_ITConfig(RTC_IT_ALR, ENABLE);  // 使能RTC_IT_ALR闹钟中断
		RTC_WaitForLastTask();
        RTC_WaitForSynchro();
	}
	
}


/**
  * 函    数:RTC设置时间
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,全局数组里时间值将刷新到RTC硬件电路
  */

void MyRTC_SetTime(void)
{
	time_t time_cnt;		//定义秒计数器数据类型
	struct tm time_date;	//定义日期时间数据类型
	
	time_date.tm_year = MyRTC_Time[0] - 1900;		//将数组的时间赋值给日期时间结构体
	time_date.tm_mon = MyRTC_Time[1] - 1;
	time_date.tm_mday = MyRTC_Time[2];
	time_date.tm_hour = MyRTC_Time[3];
	time_date.tm_min = MyRTC_Time[4];
	time_date.tm_sec = MyRTC_Time[5];
	
	time_cnt = mktime(&time_date) - 8 * 60 * 60;	//调用mktime函数,将日期时间转换为秒计数器格式
													//- 8 * 60 * 60为东八区的时区调整
	
	RTC_SetCounter(time_cnt);						//将秒计数器写入到RTC的CNT中
	RTC_WaitForLastTask();							//等待上一次操作完成
}



/**
  * 函    数:RTC读取时间
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,RTC硬件电路里时间值将刷新到全局数组
  */
void MyRTC_ReadTime(void)
{
	time_t time_cnt;		//定义秒计数器数据类型
	struct tm time_date;	//定义日期时间数据类型
	
	time_cnt = RTC_GetCounter() + 8 * 60 * 60;		//读取RTC的CNT,获取当前的秒计数器
													//+ 8 * 60 * 60为东八区的时区调整
	
	time_date = *localtime(&time_cnt);				//使用localtime函数,将秒计数器转换为日期时间格式
	
	MyRTC_Time[0] = time_date.tm_year + 1900;		//将日期时间结构体赋值给数组的时间
	MyRTC_Time[1] = time_date.tm_mon + 1;
	MyRTC_Time[2] = time_date.tm_mday;
	MyRTC_Time[3] = time_date.tm_hour;
	MyRTC_Time[4] = time_date.tm_min;
	MyRTC_Time[5] = time_date.tm_sec;
}


void Adjust_Time(uint8_t increment)
{   
    switch (current_unit) {
        case YEAR: MyRTC_Time[0] += increment ? 1 : -1; break;
        case MONTH: MyRTC_Time[1] = (MyRTC_Time[1] - 1 + (increment ? 1 : -1) + 12) % 12 + 1; break;
        case DAY: MyRTC_Time[2] = (MyRTC_Time[2] - 1 + (increment ? 1 : -1) + 31) % 31 + 1; break;
        case HOUR: MyRTC_Time[3] = (MyRTC_Time[3] + (increment ? 1 : -1) + 24) % 24; break;
        case MINUTE: MyRTC_Time[4] = (MyRTC_Time[4] + (increment ? 1 : -1) + 60) % 60; break;
        case SECOND: MyRTC_Time[5] = (MyRTC_Time[5] + (increment ? 1 : -1) + 60) % 60; break;
		case UNIT_COUNT:break;
		case NOS:break;
    }
    
    MyRTC_SetTime();
}

void Seleted_Time(void)
{
	switch (current_unit) {
        case YEAR:
		{
			OLED_ShowString(1,1,"    ");
		break;
		}
		case MONTH:
			OLED_ShowString(1,6,"  ");
		break;
		case DAY:
			OLED_ShowString(1,9,"  ");
		break;
		case HOUR:
			OLED_ShowString(2,1,"  ");
		break;
		case MINUTE:
			OLED_ShowString(2,4,"  ");
		break;
		case SECOND:
			OLED_ShowString(2,7,"  ");
		break;
		case UNIT_COUNT:
			break;	
		case NOS:break;
	}		
}

void RTC_Alarm_Config(void)
{	
    NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}

void RTC_IRQHandler(void)
{
	if(RTC_GetITStatus(RTC_IT_ALR)== SET)//闹钟中断
	{
		LED1_ON();
		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	   
  	} 				  								 
	RTC_WaitForLastTask();
}

void MyRTC_ALarm_SetTime(void)
{
	 // 设置闹钟时间为2024年7月27日
    uint16_t alarm_year = 2024;
    uint8_t alarm_month = 7;
    uint8_t alarm_day = 27;
    uint8_t alarm_hour = 17;
    uint8_t alarm_minute = 1;
    uint8_t alarm_second = 55;
    
    struct tm alarm_time;
    time_t alarm_seconds;

    alarm_time.tm_year = alarm_year - 1900;
    alarm_time.tm_mon = alarm_month - 1;
    alarm_time.tm_mday = alarm_day;
    alarm_time.tm_hour = alarm_hour;
    alarm_time.tm_min = alarm_minute;
    alarm_time.tm_sec = alarm_second;

    // 将闹钟时间转换为秒
    alarm_seconds = mktime(&alarm_time) - 8 * 60 * 60; // 东八区调整

    // 设置闹钟时间
    RTC_SetAlarm(alarm_seconds);
    RTC_WaitForLastTask();
}



 

当时间为2024.7.27 17:1:55时,PC13引脚接的灯亮

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值