forked from gary/BCU
2
0
Fork 0
BCU/library/drv_stm32f4xx/drv_rtc.c

377 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "drv_rtc.h"
#include "drv_clk.h"
#include "bsp_task.h"
#include "kit_time.h"
#include "kit_data.h"
#include "kit_debug.h"
#define LSE_NOT_READY 0
#define RCC_BDCR_RTCEN_POS KIT_BIT_MASK_32(15)
#define RCC_BDCR_LSEDY_POS ((uint32_t)0x00000002)
#define RCC_BDCR_LSEON_POS KIT_BIT_MASK_32(0)
#define RCC_RTCCLKSource_LSE KIT_BIT_MASK_32(8)
#define RTC_FLAG_RTOFF ((uint16_t)0x0020) /*!< RTC Operation OFF flag */
/******************************************************************************************
1.写RTC寄存器前必须使能PWR_CR中的DBP,且必须等待操作完成才能清0 DBP
2.stm32f10x RTC 进行写操作前必须判断上一次操作是否完成(RTOFF)
3.写RTC_CNT、RTC_ALR或RTC_PRL寄存器时必须先置位CNF写完后必须清0才会执行写操作
4.读RTC_CNT、RTC_ALR或RTC_PRL寄存器时前必须清0RSF等其被硬件置位时表示同步完成可以读取
5.低速时钟起振时间1-5s
******************************************************************************************/
#define RTC_INIT_MASK ((uint32_t)0xFFFFFFFF)
bool drv_rtc_enable_init_mode(void)
{
uint32_t dly = 0;
// //对RTC寄存器操作前先要使能备份区
// PWR->CR |= PWR_CR_DBP;
//关闭RTC寄存器写保护
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
//进入RTC初始化模式
if((RTC->ISR &= RTC_ISR_INITF) == 0)
{
RTC->ISR = RTC_INIT_MASK;
//等待进入RTC初始化模式成功
while(((RTC->ISR & RTC_ISR_INITF)==0x00) && (dly++ < 100))
{
bsp_task_delay_ms(1);
}
}
return (dly < 100);
}
void drv_rtc_disable_init_mode(void)
{
RTC->ISR &= ~RTC_ISR_INIT; //退出RTC初始化模式
RTC->WPR = 0xFF; //使能RTC寄存器写保护
}
#define RCC_BDCR_RTCSEL_LSI (0x2U << 8) // RTCSEL[1:0] = 10, 选择 LSI 作为 RTC 时钟
//由于低速时钟起振较慢其他任务初始化后再初始化RTC
bool drv_rtc_init(void)
{
#if 0
bool res = false;
uint32_t dly = 0;
//if((RCC->BDCR & RCC_BDCR_RTCEN_POS) != RCC_BDCR_RTCEN_POS)
{
KIT_PRINTF("rtc init\r\n");
RCC->APB1ENR |= RCC_APB1Periph_PWR;
//使能允许写入RTC和后备寄存器
PWR->CR |= PWR_CR_DBP;
//开启LSE
RCC->BDCR |= RCC_BDCR_LSEON_POS;
while ((RCC->BDCR & RCC_BDCR_LSEDY_POS) == LSE_NOT_READY)
{
bsp_task_delay_ms(10);
if(dly++ > 500)
{
KIT_PRINTF("rtc fail \r\n");
return res;
}
}
KIT_PRINTF("rtc dly %d\r\n", dly);
RCC->BDCR |= (RCC_RTCCLKSource_LSE | RCC_BDCR_RTCEN_POS);
//RTC配置使用默认配置
//设置同步分频系数和异步分频系数默认为1Hz = 32768/(同步分频 + 1)/(异步分频 + 1)
res = drv_rtc_enable_init_mode();
if(res == true)
{
RTC->PRER = 0x001F03FF;
}
drv_rtc_disable_init_mode();
//PWR->CR &= ~PWR_CR_DBP_POS;
}
return res;
#endif
/* 1. 使能PWR和备份域访问 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // 开启PWR时钟
PWR->CR |= PWR_CR_DBP; // 允许访问备份域
/* 2. 启用LSI并等待稳定 */
RCC->CSR |= RCC_CSR_LSION; // 启动内部低速时钟LSI
while ((RCC->CSR & RCC_CSR_LSIRDY) == 0); // 等待LSI准备好
/* 3. 选择LSI作为RTC时钟 */
RCC->BDCR &= ~RCC_BDCR_RTCSEL; // 清除RTC时钟选择
RCC->BDCR |= RCC_BDCR_RTCSEL_LSI; // 选择LSI为RTC时钟
RCC->BDCR |= RCC_BDCR_RTCEN; // 使能RTC
/* 4. 解锁RTC寄存器 */
RTC->WPR = 0xCA; // 先写入第一解锁码
RTC->WPR = 0x53; // 再写入第二解锁码
RTC->ISR |= RTC_ISR_INIT; // 进入初始化模式
while ((RTC->ISR & RTC_ISR_INITF) == 0); // 等待初始化完成
/* 5. 设置RTC预分频器LSI约为40kHz */
RTC->PRER = (127 << 16) | (249); // Async=127, Sync=2491Hz计数频率
RTC->CR &= ~RTC_CR_FMT; // 设置为24小时格式
RTC->ISR &= ~RTC_ISR_INIT; // 退出初始化模式
/* 6. 锁定写保护 */
RTC->WPR = 0xFF;
}
//ampm:AM/PM,0=AM/24H,1=PM.
bool drv_rtc_set_time(uint8_t hour, uint8_t min, uint8_t sec)
{
bool res;
//关闭RTC寄存器写保护
res = drv_rtc_enable_init_mode();
if(res == true)
{
RTC->TR = ((uint32_t)kit_dec_to_bcd(hour) << 16) | ((uint32_t)kit_dec_to_bcd(min) << 8) | (kit_dec_to_bcd(sec));
}
drv_rtc_disable_init_mode();
return res;
}
//year 20xx, month 1-12, day 1-31
bool drv_rtc_set_date(uint16_t year, uint8_t month, uint8_t day)
{
bool res;
//关闭RTC寄存器写保护
res = drv_rtc_enable_init_mode();
if((res == true) && (year >= 2000))
{
RTC->DR = ((uint32_t)kit_dec_to_bcd(year - 2000) << 16) | ((uint32_t)kit_dec_to_bcd(month) << 8) | (kit_dec_to_bcd(day));
}
drv_rtc_disable_init_mode();
return res;
}
bool drv_rtc_wait_sync(void)
{
bool res = true;
uint32_t retry=0xFFFFF;
//关闭RTC寄存器写保护
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
RTC->ISR &= ~(1<<5); //清除RSF位
while(retry&&((RTC->ISR&(1<<5)) == 0x00))//等待影子寄存器同步
{
retry--;
}
if(retry == 0)
{
res = false;
}
RTC->WPR = 0xFF; //使能RTC寄存器写保护
return res;
}
bool drv_rtc_get_time(int32_t *hour, int32_t *min, int32_t *sec)
{
uint32_t temp = 0;
if(drv_rtc_wait_sync() == true)
{
temp = RTC->TR;
*hour = kit_dcb_to_dec((temp >> 16) & 0x3F);
*min = kit_dcb_to_dec((temp >> 8) & 0x7F);
*sec = kit_dcb_to_dec(temp & 0x7F);
return true;
}
else
{
*hour = *min = *sec = 0;
return false;
}
}
//year 20xx, month 1-12, day 1-31
bool drv_rtc_get_date(int32_t *year, int32_t *month, int32_t *day)
{
uint32_t temp = 0;
if(drv_rtc_wait_sync() == true)
{
temp = RTC->DR;
*year = kit_dcb_to_dec((temp >> 16) & 0xFF) + 2000;
*month = kit_dcb_to_dec((temp >> 8) & 0x1F);
*day = kit_dcb_to_dec(temp & 0x3F);
return true;
}
else
{
*year = 2000;
*month = *day = 1;
return false;
}
}
uint32_t drv_rtc_get_tick(void)
{
#if 0
struct tm tmp;
uint32_t res = 0;
tmp.tm_year = 0;
if((drv_rtc_get_time(&tmp.tm_hour, &tmp.tm_min, &tmp.tm_sec) == true)
&& (drv_rtc_get_date(&tmp.tm_year, &tmp.tm_mon, &tmp.tm_mday) == true))
{
tmp.tm_mon--;
tmp.tm_year = (tmp.tm_year > 2018) ? (tmp.tm_year - 1900) : 119;
res = mktime(&tmp);
//res = kit_time_get_stamp(2019, &tmp);
}
return res;
#endif
uint16_t year;
uint8_t month, date, hour, min, sec;
drv_get_date_time(&year, &month, &date, &hour, &min, &sec);
struct tm rtc_tm;
rtc_tm.tm_year = year - 1900;
rtc_tm.tm_mon = month - 1;
rtc_tm.tm_mday = date;
rtc_tm.tm_hour = hour;
rtc_tm.tm_min = min;
rtc_tm.tm_sec = sec;
rtc_tm.tm_isdst = -1; // 不使用夏令时
return kit_time_get_stamp(1970, &rtc_tm);
}
bool drv_rtc_set_tick(uint32_t tick)
{
struct tm *tmp;
bool res = false;
if(drv_rtc_get_tick() != tick)
{
tmp = kit_time_get_date(2019, tick);
//标准库月0-11 ST月为1-12ST年从2000开始只保留十位和各位
if((tmp->tm_year >= 119) && (drv_rtc_set_time(tmp->tm_hour, tmp->tm_min, tmp->tm_sec) == true)
&& (drv_rtc_set_date((tmp->tm_year + 1900), tmp->tm_mon + 1, tmp->tm_mday) == true))
{
res = true;
}
}
else
{
res = true;
}
return res;
}
uint16_t drv_rtc_get_ms(void)
{
uint32_t tmp = 0;
/*Get sub seconds values from the correspondent registers*/
tmp = (uint32_t)(RTC->SSR);
/* Read DR register to unfroze calendar registers */
(void) (RTC->DR);
tmp = (0x03FF - tmp) * 1000 / (0x03FF + 1);
return tmp;
}
bool drv_rtc_set_ms(uint16_t ms)
{
bool res = false;
uint32_t cur_ms, cnt = 0;
/* Disable the write protection for RTC registers */
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
cur_ms = drv_rtc_get_ms();
if(ms != cur_ms)
{
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
/* Check if a Shift is pending*/
while (((RTC->ISR & RTC_ISR_SHPF) != RESET) && (cnt++ < 1000));
/* Check if the Shift pending is completed or if there is no Shift operation at all*/
if ((RTC->ISR & RTC_ISR_SHPF) == RESET)
{
/* check if the reference clock detection is disabled */
if((RTC->CR & RTC_CR_REFCKON) == RESET)
{
if(cur_ms > ms)
{
cur_ms = (cur_ms - ms) * (0x3FF + 1) / 1000;
}
else
{
cur_ms = (1000 - ms + cur_ms) * (0x3FF + 1) / 1000;
}
RTC->SHIFTR = cur_ms;
res = drv_rtc_wait_sync();
}
}
/* Enable the write protection for RTC registers */
RTC->WPR = 0xFF;
}
return res;
}
// 设置日期时间year = 2000~2099
void drv_set_date_time(uint16_t year, uint8_t month, uint8_t date,uint8_t hour, uint8_t min, uint8_t sec)
{
if (year < 2000 || year > 2099) return; // 超出 RTC 范围
if (month == 0 || month > 12 || date == 0 || date > 31) return;
if (hour > 23 || min > 59 || sec > 59) return;
uint8_t yr = year - 2000;
// 解锁写保护
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
// 进入初始化模式
RTC->ISR |= RTC_ISR_INIT;
while ((RTC->ISR & RTC_ISR_INITF) == 0);
// 设置时间BCD格式
RTC->TR = ((hour / 10) << 20) | ((hour % 10) << 16) |
((min / 10) << 12) | ((min % 10) << 8) |
((sec / 10) << 4) | (sec % 10);
// 设置日期BCD格式
RTC->DR = ((yr / 10) << 20) | ((yr % 10) << 16) |
((month / 10) << 12) | ((month % 10) << 8) |
((date / 10) << 4) | (date % 10);
RTC->ISR &= ~RTC_ISR_INIT; // 退出初始化模式
// 上锁
RTC->WPR = 0xFF;
}
// 获取当前时间
void drv_get_date_time(uint16_t *year, uint8_t *month, uint8_t *date,uint8_t *hour, uint8_t *min, uint8_t *sec)
{
uint32_t tr = RTC->TR;
uint32_t dr = RTC->DR;
*hour = ((tr >> 20) & 0x3) * 10 + ((tr >> 16) & 0xF);
*min = ((tr >> 12) & 0x7) * 10 + ((tr >> 8) & 0xF);
*sec = ((tr >> 4) & 0x7) * 10 + (tr & 0xF);
uint8_t yr_l = ((dr >> 20) & 0xF) * 10 + ((dr >> 16) & 0xF);
*year = 2000 + yr_l;
*month = ((dr >> 12) & 0x1) * 10 + ((dr >> 8) & 0xF);
*date = ((dr >> 4) & 0x3) * 10 + (dr & 0xF);
}