#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=249,1Hz计数频率 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-12,ST年从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); }