欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

STM32F103ZET6--RTC时钟闹钟配置

程序员文章站 2022-07-10 16:02:56
/*******************************************************/*******************************************************RTC.H/********************************************************/********************************************************#ifndef __RTC_H#def...

/*******************************************************
/*******************************************************
RTC.H
/********************************************************
/********************************************************

#ifndef __RTC_H
#define __RTC_H
#include "sys.h"
//Mini STM32开发板
//RTC实时时钟 驱动代码
//正点原子@ALIENTEK
//2010/6/6

//时间结构体
typedef struct
{
    vu8 hour;
    vu8 min;
    vu8 sec;
    //公历日月年周
    vu16 w_year;
    vu8  w_month;
    vu8  w_date;
    vu8  week;
} _calendar_obj;
typedef struct
{
    vu8 year;
    vu8 month;
    vu8 day;
    vu8 hour;
    vu8 min;
    vu8 sec;
} _alr_obj;
extern _calendar_obj calendar;	//日历结构体

extern u8 const mon_table[12];	//月份日期数据表
void Disp_Time(u8 x, u8 y, u8 size); //在制定位置开始显示时间
void Disp_Week(u8 x, u8 y, u8 size, u8 lang); //在指定位置显示星期
u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;
u8 Is_Leap_Year(u16 year);//平年,闰年判断
//u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);         //更新时间
u8 RTC_Get_Week(u16 year, u8 month, u8 day);
u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec); //设置时间
u8 ALR_Set(u16 year, u8 month, u8 day, u8 hour, u8 min, u8 sec);
#endif

/**********************************************************
/**********************************************************
RTC.C文件
/**********************************************************
/**********************************************************

#include "rtc.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "beep.h"
//Mini STM32开发板
//RTC实时时钟 驱动代码
//正点原子@ALIENTEK
//2010/6/6

_calendar_obj calendar;//时钟结构体
_alr_obj ALR;
//static void RTC_NVIC_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寄存器
//}
static void RTC_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//先占优先级1位,从优先级3位
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;	//先占优先级0位,从优先级4位
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
    NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

    NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_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);
}

//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码

u8 RTC_Init(void)
{
    //检查是不是第一次配置时钟
    u8 temp = 0;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟
    PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问

    if (BKP_ReadBackupRegister(BKP_DR1) != 0x5051)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
    {
        BKP_DeInit();	//复位备份区域
        RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振

        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
        {
            temp++;
            delay_ms(10);
        }

        if(temp >= 250)return 1; //初始化时钟失败,晶振有问题

        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
        RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟

        RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
        RTC_WaitForSynchro();		//等待RTC寄存器同步
        RTC_ITConfig(RTC_IT_SEC | RTC_IT_ALR, ENABLE);		//使能RTC秒中断
        RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成

        RTC_EnterConfigMode();/// 允许配置
        RTC_SetPrescaler(32767); //设置RTC预分频的值
        RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成

        RTC_Set(2018, 4, 8, 21, 34, 50); //设置时间
        RTC_WaitForLastTask();
        RTC_WaitForSynchro();
        ALR_Set(2018, 4, 8, 21, 35, 0);
        RTC_ExitConfigMode(); //退出配置模式
        BKP_WriteBackupRegister(BKP_DR1, 0X5051);	//向指定的后备寄存器中写入用户程序数据
    }
    else//系统继续计时
    {

        RTC_WaitForSynchro();	//等待最近一次对RTC寄存器的写操作完成
        RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断
        RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    }

    RTC_NVIC_Config();//RCT中断分组设置
    RTC_Get();//更新时间
    return 0; //ok

}
//RTC时钟中断
//每秒触发一次
extern int count;
void RTC_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_SEC) != RESET)
    {
        RTC_Get();
        count++;
    }

    RTC_ClearITPendingBit(RTC_IT_OW | RTC_IT_SEC);
    RTC_WaitForLastTask();
}

//闹钟中断
void RTCAlarm_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
    {
        BEEP = 1;
    }

    EXTI_ClearITPendingBit(EXTI_Line17);
    RTC_WaitForLastTask();
    RTC_ClearITPendingBit(RTC_IT_ALR);
    RTC_WaitForLastTask();
}
//RTC时钟中断
//每秒触发一次
//extern u16 tcnt;
//void RTC_IRQHandler(void)
//{
//	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
//	{
//		RTC_Get();//更新时间
// 	}
//	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
//	{
//		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断
//	  RTC_Get();				//更新时间
//  	printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间
//
//  	}
//	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清闹钟中断
//	RTC_WaitForLastTask();
//}
//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{
    if(year % 4 == 0) //必须能被4整除
    {
        if(year % 100 == 0)
        {
            if(year % 400 == 0)return 1; //如果以00结尾,还要能被400整除
            else return 0;
        }
        else return 1;
    }
    else return 0;
}
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值:0,成功;其他:错误代码.
//月份数据表
u8 const table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; //月修正数据表
//平年的月份日期表
const u8 mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
    u16 t;
    u32 seccount = 0;

    if(syear < 1970 || syear > 2099)return 1;

    for(t = 1970; t < syear; t++)	//把所有年份的秒钟相加
    {
        if(Is_Leap_Year(t))seccount += 31622400; //闰年的秒钟数
        else seccount += 31536000;			 //平年的秒钟数
    }

    smon -= 1;

    for(t = 0; t < smon; t++)	 //把前面月份的秒钟数相加
    {
        seccount += (u32)mon_table[t] * 86400; //月份秒钟数相加

        if(Is_Leap_Year(syear) && t == 1)seccount += 86400; //闰年2月份增加一天的秒钟数
    }

    seccount += (u32)(sday - 1) * 86400; //把前面日期的秒钟数相加
    seccount += (u32)hour * 3600; //小时秒钟数
    seccount += (u32)min * 60;	 //分钟秒钟数
    seccount += sec; //最后的秒钟加上去

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟
    PWR_BackupAccessCmd(ENABLE);	//使能RTC和后备寄存器访问
    RTC_SetCounter(seccount);	//设置RTC计数器的值

    RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    return 0;
}

//初始化闹钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒
//返回值:0,成功;其他:错误代码.
//u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
//{
//	u16 t;
//	u32 seccount=0;
//	if(syear<1970||syear>2099)return 1;
//	for(t=1970;t<syear;t++)	//把所有年份的秒钟相加
//	{
//		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
//		else seccount+=31536000;			  //平年的秒钟数
//	}
//	smon-=1;
//	for(t=0;t<smon;t++)	   //把前面月份的秒钟数相加
//	{
//		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
//		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
//	}
//	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
//	seccount+=(u32)hour*3600;//小时秒钟数
//    seccount+=(u32)min*60;	 //分钟秒钟数
//	seccount+=sec;//最后的秒钟加上去
//	//设置时钟
//	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟
//	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问
//	//上面三步是必须的!
//
//	RTC_SetAlarm(seccount);
//
//	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
//
//	return 0;
//}

u8 ALR_Set(u16 year, u8 month, 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;
    }

    month -= 1;

    for(t = 0; t < month; t++)
    {
        seccount += (u32)mon_table[t] * 86400;

        if(Is_Leap_Year(year) && t == 1) seccount += 86400;
    }

    seccount += (u32)(day - 1) * 86400;
    seccount += (u32)hour * 3600;
    seccount += (u32)min * 60;
    seccount += sec;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_EnterConfigMode();
    RTC_SetAlarm(seccount);
    RTC_WaitForLastTask();
    return 0;
}
//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
    static u16 daycnt = 0;
    u32 timecount = 0;
    u32 temp = 0;
    u16 temp1 = 0;
    timecount = RTC_GetCounter();
    temp = timecount / 86400; //得到天数(秒钟数对应的)

    if(daycnt != temp) //超过一天了
    {
        daycnt = temp;
        temp1 = 1970;	//从1970年开始

        while(temp >= 365)
        {
            if(Is_Leap_Year(temp1))//是闰年
            {
                if(temp >= 366)temp -= 366; //闰年的秒钟数
                else
                {
                    temp1++;
                    break;
                }
            }
            else temp -= 365;	 //平年

            temp1++;
        }

        calendar.w_year = temp1; //得到年份
        temp1 = 0;

        while(temp >= 28) //超过了一个月
        {
            if(Is_Leap_Year(calendar.w_year) && temp1 == 1) //当年是不是闰年/2月份
            {
                if(temp >= 29)temp -= 29; //闰年的秒钟数
                else break;
            }
            else
            {
                if(temp >= mon_table[temp1])temp -= mon_table[temp1]; //平年
                else break;
            }

            temp1++;
        }

        calendar.w_month = temp1 + 1;	//得到月份
        calendar.w_date = temp + 1;  	//得到日期
    }

    temp = timecount % 86400;     		//得到秒钟数
    calendar.hour = temp / 3600;     	//小时
    calendar.min = (temp % 3600) / 60; 	//分钟
    calendar.sec = (temp % 3600) % 60; 	//秒钟
    calendar.week = RTC_Get_Week(calendar.w_year, calendar.w_month, calendar.w_date); //获取星期
    return 0;
}
//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日
//返回值:星期号
u8 RTC_Get_Week(u16 year, u8 month, u8 day)
{
    u16 temp2;
    u8 yearH, yearL;

    yearH = year / 100;
    yearL = year % 100;

    // 如果为21世纪,年份数加100
    if (yearH > 19)yearL += 100;

    // 所过闰年数只算1900年之后的
    temp2 = yearL + yearL / 4;
    temp2 = temp2 % 7;
    temp2 = temp2 + day + table_week[month - 1];

    if (yearL % 4 == 0 && month < 3)temp2--;

    return(temp2 % 7);
}

1.注意RTC时钟采用了RTC秒中断和闹钟中断,这是两个不同的中断服务函数
2.中断服务管理函数优先级要注意看
/**************************************************************
/**************************************************************
main.c
/**************************************************************
/**************************************************************

volatile int count = 0 ;
 int main(void)
 {	 
 	u8 t=0;	
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
 	LED_Init();			     //LED端口初始化
	LCD_Init();		 
	usmart_dev.init(SystemCoreClock/1000000);	//初始化USMART	
	RTC_Init();	  			//RTC初始化
    BEEP_Init();         	//初始化蜂鸣器端口
	POINT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(60,50,200,16,16,"WarShip STM32");	
	LCD_ShowString(60,70,200,16,16,"RTC TEST");	
	LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(60,110,200,16,16,"2015/1/14");		
	//显示时间
	POINT_COLOR=BLUE;//设置字体为蓝色
	LCD_ShowString(60,130,200,16,16,"    -  -  ");	   
	LCD_ShowString(60,162,200,16,16,"  :  :  ");		    
	while(1)
	{								    
		if(t!=calendar.sec)
		{
			t=calendar.sec;
			LCD_ShowNum(60,130,calendar.w_year,4,16);									  
			LCD_ShowNum(100,130,calendar.w_month,2,16);									  
			LCD_ShowNum(124,130,calendar.w_date,2,16);	 
			switch(calendar.week)
			{
				case 0:
					LCD_ShowString(60,148,200,16,16,"Sunday   ");
					break;
				case 1:
					LCD_ShowString(60,148,200,16,16,"Monday   ");
					break;
				case 2:
					LCD_ShowString(60,148,200,16,16,"Tuesday  ");
					break;
				case 3:
					LCD_ShowString(60,148,200,16,16,"Wednesday");
					break;
				case 4:
					LCD_ShowString(60,148,200,16,16,"Thursday ");
					break;
				case 5:
					LCD_ShowString(60,148,200,16,16,"Friday   ");
					break;
				case 6:
					LCD_ShowString(60,148,200,16,16,"Saturday ");
					break;  
			}
			LCD_ShowNum(60,162,calendar.hour,2,16);									  
			LCD_ShowNum(84,162,calendar.min,2,16);									  
			LCD_ShowNum(108,162,calendar.sec,2,16);
//			LED0=!LED0;
		}	
		delay_ms(10);		
		if(KEY0==0)
		{
			BEEP = 0;
		}
       if(count>20)            //只会亮10s,跟时钟是同步走的
		{
			BEEP = 0;
			count = 0 ;
		}		
	};  
 }

***实验现象采用LCD屏幕显示RTC时间,闹钟到时蜂鸣器发出声音***硬件采用正点原子战舰

本文地址:https://blog.csdn.net/My_bother/article/details/107408511