STM32F4 裸机RTC驱动,寄存器操作
扫描二维码
随时随地手机看文章
STM32F4裸机RTC驱动,寄存器操作。
/************************************************************************************************************* * 文件名 : RTC.c * 功能 : STM32F4 RTC驱动 * 作者 : cp1300@139.com * 创建时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 详细: 要正确读取 RTC 日历寄存器(RTC_SSR、RTC_TR 和 RTC_DR),APB1 时钟频率 (fPCLK1) 必须等于或大于 fRTCCLK RTC 时钟频率的七倍。这可以确保同步机制行为的安全性。 如果 APB1 时钟频率低于 RTC 时钟频率的七倍,则软件必须分两次读取日历时间寄存器和 日期寄存器。这样,当两次读取的 RTC_TR 结果相同时,才能确保数据正确。 否则必须执 行第三次读访问。任何情况下,APB1 的时钟频率都不能低于 RTC 的时钟频率。 RTC->BKP0R:用于标示是否进行了初始化RTC,请不要再使用了。 注意:初始化时会使用调试串口打印信息 2017-01-15:修改全局时间结构体名称为g_timer,并且增加系统命令支持 *************************************************************************************************************/ #include "rtc.h" #include "system.h" volatile tm g_timer; //全局系统时钟 //秒中断回调函数 static void (*RTC_SerIntCallBack)(void) ; //秒中断回调函数 //内部函数申明 u8 RTC_GetWeek(u16 year,u8 month,u8 day); //获取2000-2099年之间的日期对应的星期 u32 RTC_DECtoBCD( u8 DEC); //将数字转换为压缩BCD格式,最大支持99 u32 RTC_BCDtoDEC(u8 BCD); //将压缩BCD转为DEC,最大支持99 /************************************************************************************************************************* * 函数 : bool RTC_WaitSynchro(void) * 功能 : 等待RSF同步 * 参数 : 无 * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : *************************************************************************************************************************/ bool RTC_WaitSynchro(void) { u32 retry=0XFFFFF; //执行以下步骤解锁RTC寄存器写保护 RTC->WPR = 0xCA; //写入KEY后关闭RTC寄存器写保护 RTC->WPR = 0x53; RTC->ISR &= ~(1<ISR&(1<WPR= 0xff; //随便写入一个值(不是KEY)使能RTC寄存器写保护 return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_InitMode(void) * 功能 : RTC进入初始化模式 * 参数 : 无 * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : *************************************************************************************************************************/ bool RTC_InitMode(void) { u32 retry=0X10000; if(RTC->ISR & (1<ISR |= 1<ISR&(1<WPR = 0xCA; //写入KEY后关闭RTC寄存器写保护 RTC->WPR = 0x53; if(RTC_InitMode() == FALSE)return FALSE;//进入RTC初始化模式失败 temp = (u32)RTC_DECtoBCD(hour) << 16; //小时 temp |= (u32)RTC_DECtoBCD(min) << 8; //分钟 temp |= (u32)RTC_DECtoBCD(sec) << 0; //秒钟 RTC->TR=temp; //设置时间 RTC->ISR&=~(1<WPR = 0xFF; //开启写保护 return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_SetDate(u16 year,u8 month,u8 date) * 功能 : RTC日期设置 * 参数 : year,month,date:年(2000~2099),月(1~12),日(1~31) * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : 会自动退出写保护 *************************************************************************************************************************/ bool RTC_SetDate(u16 year,u8 month,u8 date) { u32 temp; u8 week; //执行以下步骤解锁RTC寄存器写保护 RTC->WPR = 0xCA; //写入KEY后关闭RTC寄存器写保护 RTC->WPR = 0x53; if(RTC_InitMode() == FALSE)return FALSE;//进入RTC初始化模式失败 if(year < 2000) year = 2000; //限制年 if(year > 2099) year = 2099; //限制年 if(month < 1) month = 1; //限制月 if(month > 12) month = 12; //限制月 if(date < 1) date = 1; //限制日 if(date > 31) date = 31; //限制日 week = RTC_GetWeek(year,month,date); //计算设置的日期对应的星期 year -= 2000; //年转换为0-99 temp = (u32)RTC_DECtoBCD(year)<<16; //年 temp |= week<<13; //星期 temp |= (u32)RTC_DECtoBCD(month)<<8; //月份 temp |= (u32)RTC_DECtoBCD(date)<DR=temp; //写入 RTC->ISR&=~(1<WPR = 0xFF; //开启写保护 return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_GetTime(u8 *hour,u8 *min,u8 *sec) * 功能 : 获取RTC时间 * 参数 : *hour,*min,*sec:小时,分钟,秒钟 * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : 24小时制 *************************************************************************************************************************/ bool RTC_GetTime(u8 *hour,u8 *min,u8 *sec) { u32 temp; u32 retry = 5250*3; u8 ampm; while((RTC_WaitSynchro() == FALSE) && (retry!=0)) //等待同步 { nop;nop;nop;nop;nop;nop; retry --; } if(retry==0) return FALSE; temp=RTC->TR; *hour=RTC_BCDtoDEC((temp>>16)&0X3F); *min=RTC_BCDtoDEC((temp>>8)&0X7F); *sec=RTC_BCDtoDEC(temp&0X7F); ampm=temp>>22; if(ampm!=0) //使用的是12小时制,并且是下午,则小时加上12小时 { *hour += 12; } return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_GetDate(u16 *year,u8 *month,u8 *date,u8 *week) * 功能 : 获取RTC日期 * 参数 : *year,*mon,*date:年,月,日;*week:星期 * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : *************************************************************************************************************************/ bool RTC_GetDate(u16 *year,u8 *month,u8 *date,u8 *week) { u32 retry = 5250*3; u32 temp; while((RTC_WaitSynchro() == FALSE) && (retry!=0)) //等待同步 { nop;nop;nop;nop;nop;nop; retry --; } if(retry==0) return FALSE; temp=RTC->DR; *year=RTC_BCDtoDEC((temp>>16)&0XFF)+2000; *month=RTC_BCDtoDEC((temp>>8)&0X1F); *date=RTC_BCDtoDEC(temp&0X3F); *week=(temp>>13)&0X07; return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_Get(void) * 功能 : 更新RTC时间 * 参数 : 无 * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : APB时钟必须大于RTC时钟7倍,通常会远大于,一般APB少说也得1MHZ以上 时间写入到了全局缓冲区 timer 中 *************************************************************************************************************************/ bool RTC_Get(void) { u32 temp; u32 retry = 5250*3; u8 ampm; while((RTC_WaitSynchro() == FALSE) && (retry!=0)) //等待同步 { nop;nop;nop;nop;nop;nop; retry --; } if(retry==0) return FALSE; //获取日期 temp=RTC->DR; g_timer.year=RTC_BCDtoDEC((temp>>16)&0XFF)+2000; g_timer.month=RTC_BCDtoDEC((temp>>8)&0X1F); g_timer.date=RTC_BCDtoDEC(temp&0X3F); g_timer.week=(temp>>13)&0X07; //获取时间 temp=RTC->TR; g_timer.hour=RTC_BCDtoDEC((temp>>16)&0X3F); g_timer.min=RTC_BCDtoDEC((temp>>8)&0X7F); g_timer.sec=RTC_BCDtoDEC(temp&0X7F); ampm=temp>>22; if(ampm!=0) //使用的是12小时制,并且是下午,则小时加上12小时 { g_timer.hour += 12; } return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_SetWakeUp(bool isEnableInt, u16 WakeSecs) * 功能 : RTC唤醒中断设置 * 参数 : isEnableInt:是否开启唤醒中断;WakeSecs:唤醒周期,单位秒 * 返回 : TRUE:开启成功;FALSE:开启失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-02 * 最后修改时间 : 2017-07-02 * 说明 : *************************************************************************************************************************/ bool RTC_SetWakeUp(bool isEnableInt, u16 WakeSecs) { u32 temp = RTC->CR; u32 retry = 500; //关闭RTC寄存器写保护 RTC->WPR=0xCA; RTC->WPR=0x53; if(WakeSecs==0) WakeSecs = 1; //最少1秒 temp |= 1<<10; //唤醒定时器使能 if(isEnableInt) //需要开启唤醒中断 { temp |= 1<<14; //使能唤醒定时器中断 SYS_EXTI_SetEdge(EXTI_RTC_WAKE_UP, EXTI_POS_EDGE); //设置RTC唤醒中断上升沿触发 SYS_EXTI_IntEnable(EXTI_RTC_WAKE_UP, TRUE); //RTC唤醒中断EXTI开关 SYS_EXTI_ClearInt(EXTI_RTC_WAKE_UP); //清除中断标记 SYS_NVIC_IntEnable(IRQ_RTC_WKUP, TRUE); //RTC唤醒中断 NVIC全局中断使能 } else { temp &= ~(1<ISR &= ~(1<WUTR != (WakeSecs-1)) { //在 RTC_CR 寄存器中的 WUTE 位置 0 后,当唤醒定时器值可更改时,由硬件将该位置 1。 //仅当 RTC_ISR 中的 WUTWF 置 1 时才可对该寄存器执行写操作。 RTC->CR &= ~(1<ISR & (1 << 2)) == 0) && retry) { Delay_US(1); retry --; } if(retry==0) { uart_printf("设置RTC唤醒定时器失败!"); return FALSE; } RTC->WUTR = WakeSecs-1; //重新设置唤醒自动重载值 } RTC->CR = temp; //设置配置寄存器 RTC->WPR=0xFF; //开启写保护 return TRUE; } /************************************************************************************************************************* * 函数 : bool RTC_Init(bool SerIntEnable) * 功能 : RTC初始化 * 参数 : SerIntEnable:TRUE:开启秒中断;FALSE:关闭秒中断 * 返回 : TRUE:成功,FALSE:失败 * 依赖 : 底层宏定义 * 作者 : cp1300@139.com * 时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 说明 : 使用备份区0存放标志来区别是否进行过初始化 RTC->BKP0R=0x5A5AA5A5 *************************************************************************************************************************/ bool RTC_Init(bool SerIntEnable) { u32 retry=0XFFFFFFF; RCC->APB1ENR|=1<CR|=1<BKP0R != 0x5A5AA5A5) //没有进行过配置 { info_printf("RTC没有初始化,开始初始化...rn"); RCC->BDCR|=1<BDCR&0X02)==0))//等待LSE准备好 { retry--; nop;nop;nop; } if(retry==0) { info_printf("RTC时钟初始化失败了!rn"); return FALSE; //LSE 开启失败. } RCC->BDCR|=1<BDCR|=1<WPR=0xCA; RTC->WPR=0x53; if(RTC_InitMode() == FALSE)//进入RTC初始化模式 { info_printf("RTC进入初始化模式失败了!rn"); return FALSE; } //将32768 进行128x256分频 得到1秒的 ck_spre频率,用于产生秒唤醒中断 RTC->PRER=255; //RTC同步分频系数(0~7FFF),必须先设置同步分频,再设置异步分频,Frtc=Fclks/((Sprec+1)*(Asprec+1)) RTC->PRER|=127<CR&=~(1<ISR&=~(1<WPR=0xFF; //使能RTC寄存器写保护 RTC_SetTime(6,6,6); //设置初始时间 RTC_SetDate(2017,6,6); //设置初始日期 RTC->BKP0R = 0x5A5AA5A5; //写入备份区标记,标示已经初始化过RTC info_printf("初始化RTC成功!rn"); //RTC_Set_AlarmA(7,0,0,10); //设置闹钟时间 } //RTC_Set_WakeUp(4,0); if(RTC_Get()==FALSE) //更新一次时间 { info_printf("初始获取时间失败!rn"); } RTC_SetWakeUp(SerIntEnable, 1);//配置WAKE UP中断,1秒钟中断一次 return TRUE; } //设置秒中断回调函数 void RTC_SetWkupCallBack(void (*pCallBack)(void)) { RTC_SerIntCallBack = pCallBack; } //RTC闹钟中断服务函数 void RTC_Alarm_IRQHandler(void) { if(RTC->ISR&(1<ISR&=~(1<PR|=1<ISR&(1<ISR&=~(1<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--; temp2%=7; if(temp2==0)temp2=7; return temp2; } //将数字转换为压缩BCD格式,最大支持99 u32 RTC_DECtoBCD( u8 DEC) { return ((u8)(DEC/10)<>4)*10+(BCD&0x0f); } //使能系统命令行 #if SYS_CMD_EN_ #include "sys_cmd.h" #include "string.h" const SYS_CMD_TYPE CMD_GET_TIME = {"TIME?", CMD_GetTime, "tt获取系统时间", TRUE}; const SYS_CMD_TYPE CMD_GET_DATE = {"DATE?", CMD_GetDate, "tt获取系统日期", TRUE}; const SYS_CMD_TYPE CMD_SET_TIME = {"TIME=", CMD_SetTime, "tt设置系统时间 如(12:32:54):TIME=12 32 54", TRUE}; const SYS_CMD_TYPE CMD_SET_DATE = {"DATE=", CMD_SetDate, "tt设置系统日期 如(2014 6 8):TIME=2014 6 8", TRUE}; //获取时间 void CMD_GetTime(SYS_CMD_HANDLE *pHandle,char *pStr) { RTC_Get(); //更新时间 pHandle->DataPrintf("[获取时间成功]:%02d:%02d:%02drn",g_timer.hour, g_timer.min, g_timer.sec); } //获取日期 void CMD_GetDate(SYS_CMD_HANDLE *pHandle,char *pStr) { RTC_Get(); //更新时间 pHandle->DataPrintf("[获取日期成功]:%04d-%02d-%02drn",g_timer.year, g_timer.month, g_timer.date); } //设置时间 void CMD_SetTime(SYS_CMD_HANDLE *pHandle,char *pStr) { u8 hour,min,sec; u8 len; char *p; u8 num; len = strlen(pStr); //获取长度 if(isStrNumAndSpc(pStr, len, 2) == FALSE) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } //小时 p = strstr(pStr," "); //搜索空格 if(p == NULL) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } num = p - pStr; if((num > 2) || (num == 0)) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } hour = SYS_CMD_StringToDec(pStr, num); if(hour>23) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } //分钟 pStr = p+1; p = strstr(pStr," "); //搜索空格 if(p == NULL) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } num = p - pStr; if((num > 2) || (num == 0)) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } min = SYS_CMD_StringToDec(pStr, num); if(min>59) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } //秒钟 pStr = p+1; num = strlen(pStr); if((num > 2) || (num == 0)) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } sec = SYS_CMD_StringToDec(pStr, num); if(sec>59) { pHandle->DataPrintf("[时间设置错误]:格式不对或非法参数!rn"); return; } if(RTC_SetTime(hour, min, sec) == FALSE) { RTC_Get(); //更新时间 pHandle->DataPrintf("[时间设置失败]:%02d:%02d:%02drn",g_timer.hour, g_timer.min, g_timer.sec); } else { RTC_Get(); //更新时间 pHandle->DataPrintf("[时间设置成功]:%02d:%02d:%02drn",g_timer.hour, g_timer.min, g_timer.sec); } } //设置日期 void CMD_SetDate(SYS_CMD_HANDLE *pHandle,char *pStr) { u16 year; u8 month, date; u8 len; char *p; u8 num; len = strlen(pStr); //获取长度 if(isStrNumAndSpc(pStr, len, 2) == FALSE) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } //年 p = strstr(pStr," "); //搜索空格 if(p == NULL) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } num = p - pStr; if((num > 4) || (num == 0)) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } year = SYS_CMD_StringToDec(pStr, num); if(year>9999) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } //月 pStr = p+1; p = strstr(pStr," "); //搜索空格 if(p == NULL) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } num = p - pStr; if((num > 2) || (num == 0)) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } month = SYS_CMD_StringToDec(pStr, num); if(month>12) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } //日 pStr = p+1; num = strlen(pStr); if((num > 2) || (num == 0)) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } date = SYS_CMD_StringToDec(pStr, num); if(date>31) { pHandle->DataPrintf("[日期设置错误]:格式不对或非法参数!rn"); return; } if(RTC_SetDate(year, month, date) == FALSE) { RTC_Get(); //更新时间 pHandle->DataPrintf("[日期设置失败]:%04d-%02d-%02drn",g_timer.year, g_timer.month, g_timer.date); } else { RTC_Get(); //更新时间 pHandle->DataPrintf("[日期设置成功]:%04d-%02d-%02drn",g_timer.year, g_timer.month, g_timer.date); } } #endif //SYS_CMD_EN_
/************************************************************************************************************* * 文件名 : RTC.h * 功能 : STM32F4 RTC驱动 * 作者 : cp1300@139.com * 创建时间 : 2017-07-01 * 最后修改时间 : 2017-07-01 * 详细: 要正确读取 RTC 日历寄存器(RTC_SSR、RTC_TR 和 RTC_DR),APB1 时钟频率 (fPCLK1) 必须等于或大于 fRTCCLK RTC 时钟频率的七倍。这可以确保同步机制行为的安全性。 如果 APB1 时钟频率低于 RTC 时钟频率的七倍,则软件必须分两次读取日历时间寄存器和 日期寄存器。这样,当两次读取的 RTC_TR 结果相同时,才能确保数据正确。 否则必须执 行第三次读访问。任何情况下,APB1 的时钟频率都不能低于 RTC 的时钟频率。 RTC->BKP0R:用于标示是否进行了初始化RTC,请不要再使用了。 *************************************************************************************************************/ #ifndef __RTC_H_ #define __RTC_H_ #include "system.h" //时间结构体 typedef struct { u8 hour; //小时 u8 min; //分钟 u8 sec; //秒 u8 month; //月 u8 date; //日 u8 week; //星期 u16 year; //年 }tm; extern volatile tm g_timer; //函数接口 bool RTC_SetTime(u8 hour,u8 min,u8 sec); //RTC时间设置 bool RTC_SetDate(u16 year,u8 month,u8 date);//RTC日期设置 bool RTC_GetTime(u8 *hour,u8 *min,u8 *sec); //获取RTC时间 bool RTC_GetDate(u16 *year,u8 *month,u8 *date,u8 *week);//获取RTC日期 bool RTC_Get(void); //更新RTC时间 bool RTC_Init(bool SerIntEnable); //RTC初始化 bool RTC_SetWakeUp(bool isEnableInt, u16 WakeSecs); //RTC唤醒中断设置 void RTC_SetWkupCallBack(void (*pCallBack)(void));//设置秒中断回调函数 //使能系统命令行 #if SYS_CMD_EN_ #include "sys_cmd.h" #include "string.h" extern const SYS_CMD_TYPE CMD_GET_TIME; extern const SYS_CMD_TYPE CMD_GET_DATE; extern const SYS_CMD_TYPE CMD_SET_TIME; extern const SYS_CMD_TYPE CMD_SET_DATE; //获取时间 void CMD_GetTime(SYS_CMD_HANDLE *pHandle,char *pStr); //获取日期 void CMD_GetDate(SYS_CMD_HANDLE *pHandle,char *pStr); //设置时间 void CMD_SetTime(SYS_CMD_HANDLE *pHandle,char *pStr); //设置日期 void CMD_SetDate(SYS_CMD_HANDLE *pHandle,char *pStr); #endif //SYS_CMD_EN_ #endif //__RTC_H_
使用时需要一个秒中断回调函数,比如我的秒中断是用来唤醒一个线程,处理秒任务
RTC_Init(TRUE);
//任务2:负责后台(RTC 1秒唤醒一次) void TaskBack(void *pdata) { RTC_SetWkupCallBack(RTC_WkupCallBack); //设置RTC回调函数 while(1) { RTC_Get(); //更新系统时间 uart_printf("%04d-%02d-%02d %02d:%02d:%02drn",g_timer.year,g_timer.month,g_timer.date,g_timer.hour,g_timer.min,g_timer.sec); OSTimeDlyHMSM(0,0,0,100); IWDG_Feed(); OSTaskSuspend(BACK_TASK_Prio); //挂起后台进程 } }
//RTC秒中断回调函数 void RTC_WkupCallBack(void) { OSTaskResume(BACK_TASK_Prio); //唤醒后台进程 }