/** * @file rtc_rx8010.c * @author xxx * @date 2023-08-30 08:58:43 * @brief 用于实现RTC芯片RX8010的应用功能 * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. */ #include "rtc_rx8010.h" #include "i2cs.h" #include "delay.h" #define RTC_RX8010_SDA_PORT RTC_SDA_GPIO_Port #define RTC_RX8010_SDA_PIN RTC_SDA_Pin #define RTC_RX8010_SCL_PORT RTC_SCL_GPIO_Port #define RTC_RX8010_SCL_PIN RTC_SCL_Pin static i2c_t *rtc; static TIM_TypeDef *_timer_us; static void _delay_us(uint32_t us) { if (_timer_us != NULL) { delay_hardware_us(_timer_us, us); } else { delay_us(us); } } /* sec, min, hour, week, day, month, year */ // static uint8_t calendar[7] = {0, 0, 0, 1, 29, 2, 98}; /** * @brief 从RTC芯片的指定地址读取一个字节数据 * @param {uint8_t} *read_buf * @param {uint8_t} addr * @return {*} */ static BOOL rtc_read_byte(uint8_t *read_buf, uint8_t addr) { uint8_t *p = read_buf; /* 发送起始信号 */ rtc->interface.start(rtc); /* 发送从机地址 + 读写方向 */ rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */ if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 发送读地址 */ rtc->interface.write_byte(rtc, addr); if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 重新发送起始信号。前面的代码的目的向RTC传送地址,下面开始读取数据 */ rtc->interface.start(rtc); /* 发送从机地址 + 读写方向 */ rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */ if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 读取数据 */ *p = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */ /* 命令执行成功,发送I2C总线停止信号 */ rtc->interface.stop(rtc); return TRUE; } /** * @brief 从RTC芯片的指定地址读取若干数据 * @param {uint8_t} *read_buf * @param {uint8_t} addr * @param {int} size * @return {*} */ static BOOL rtc_read_bytes(uint8_t *read_buf, uint8_t addr, int size) { int i = 0; /* 发送起始信号 */ rtc->interface.start(rtc); /* 发送从机地址 + 读写方向 */ rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */ if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 发送读地址 */ rtc->interface.write_byte(rtc, addr); if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 重新发送起始信号。前面的代码的目的向RTC传送地址,下面开始读取数据 */ rtc->interface.start(rtc); /* 发送从机地址 + 读写方向 */ rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */ if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 循环读取数据,RTC芯片地址自动自增 */ for (i = 0; i < size; i++) { /* 每读完1个字节后,需要发送Ack, 最后一个字节需要发Nack */ if (i != (size - 1)) { read_buf[i] = rtc->interface.read_byte(rtc, TRUE); /* 读1个字节 */ } else { read_buf[i] = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */ } } /* 命令执行成功,发送I2C总线停止信号 */ rtc->interface.stop(rtc); return TRUE; } /** * @brief 向RTC芯片的指定地址写入一个数据 * @param {uint8_t} data * @param {uint8_t} addr * @return {*} * @note */ static BOOL rtc_write_byte(uint8_t data, uint8_t addr) { int retry = 0; /* 尝试与RTC芯片建立I2C通讯 */ for (retry = 0; retry < 100; retry++) { rtc->interface.start(rtc); /* 发送起始信号 */ rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */ if (rtc->interface.wait_ack(rtc) == TRUE) { break; } } if (retry == 100) { rtc->interface.stop(rtc); return FALSE; } /* 发送起始写地址 */ rtc->interface.write_byte(rtc, addr); if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 写入数据 */ rtc->interface.write_byte(rtc, data); if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } /* 命令执行成功,发送I2C总线停止信号 */ rtc->interface.stop(rtc); return TRUE; } /** * @brief 向RTC芯片的指定地址写入若干数据 * @param {uint8_t} *write_buf * @param {uint8_t} addr * @param {int} size * @return {*} * @note */ static BOOL rtc_write_bytes(uint8_t *write_buf, uint8_t addr, int size) { int i = 0; int retry = 0; for (i = 0; i < size; i++) { if (i == 0) { /* 尝试与RTC芯片建立I2C通讯 */ for (retry = 0; retry < 100; retry++) { rtc->interface.start(rtc); /* 发送起始信号 */ rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */ if (rtc->interface.wait_ack(rtc) == TRUE) { break; } } if (retry == 100) { rtc->interface.stop(rtc); return FALSE; } /* 发送起始写地址 */ rtc->interface.write_byte(rtc, addr); if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } } /* 循环写入数据,RTC芯片地址自动自增 */ rtc->interface.write_byte(rtc, write_buf[i]); if (rtc->interface.wait_ack(rtc) != TRUE) { rtc->interface.stop(rtc); return FALSE; } } /* 命令执行成功,发送I2C总线停止信号 */ rtc->interface.stop(rtc); return TRUE; } /** * @brief 假读RTC芯片,任意读地址,不判断RTC响应 * @return {*} * @note */ static void rtc_dummy_read(void) { rtc->interface.start(rtc); rtc->interface.write_byte(rtc, RTC_WR_ADDR); rtc->interface.write_byte(rtc, 0x20); rtc->interface.start(rtc); rtc->interface.write_byte(rtc, RTC_RD_ADDR); rtc->interface.read_byte(rtc, FALSE); rtc->interface.stop(rtc); } /** * @brief 用于检查VLF,寄存器地址:0x1e bit[1] * @return {uint8_t} 0 = VLF位为0,1 = VLF位为1 * @note */ static uint8_t rtc_check_vlf(void) { uint8_t flag_register = 1; uint8_t vlf = 0; rtc_read_byte(&flag_register, RTC_FLAG_ADDR); vlf = (flag_register & 0x02); if (vlf == 0) { return 0; } else { return 1; } } /** * @brief 等待VLF位清除,寄存器地址:0x1e bit[1] * @return {uint8_t} 0 = 清零成功,1 = 清零失败,2 = 无需清零 */ static uint8_t rtc_wait_vlf_clear(void) { uint8_t ret = 1; uint8_t i = 0; uint8_t vlf; for (i = 0; i < 10; i++) { vlf = rtc_check_vlf(); if (vlf == 0) { ret = ((i > 0) ? 0 : 2); return ret; } /* 清除VLF */ rtc_write_byte(0, RTC_FLAG_ADDR); } return ret; } /** * @brief 复位 * @return {BOOL} FALSE = 复位成功,TRUE = 复位失败 */ static BOOL rtc_soft_reset(void) { BOOL ret = FALSE; ret = rtc_write_byte(0x00, 0x1f); ret = rtc_write_byte(0x80, 0x1f); ret = rtc_write_byte(0xd3, 0x60); ret = rtc_write_byte(0x03, 0x66); ret = rtc_write_byte(0x02, 0x6b); ret = rtc_write_byte(0x01, 0x6b); if (ret == 0) { _delay_us(2000); } return ret; } /** * @brief 时钟寄存器初始化函数 * @return {BOOL} TRUE = 初始化成功,FALSE = 初始化失败 */ static BOOL rtc_clock_reginit(void) { BOOL ret = FALSE; /* set reserve register */ ret = rtc_write_byte(RTC_REG17_DATA, RTC_REG17_ADDR); ret = rtc_write_byte(RTC_REG30_DATA, RTC_REG30_ADDR); ret = rtc_write_byte(RTC_REG31_DATA, RTC_REG31_ADDR); ret = rtc_write_byte(RTC_IRQ_DATA, RTC_IRQ_ADDR); /* write 0x04 to reg_0x1d */ ret = rtc_write_byte(0x04, 0x1d); /* write 0x00 to reg_0x1e */ ret = rtc_write_byte(0x00, 0x1e); /* stop clock */ ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR); /* set the present time */ // ret = rtc_write_bytes(calendar, RTC_CLOCK_ADDR, sizeof(calendar)); /* start clock */ ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR); return ret; } /** * @brief RTC芯片初始化 * @return {BOOL} TRUE = 成功,FALSE = 失败 * @note */ BOOL rtc_init(TIM_TypeDef *timer_us) { if (timer_us != NULL) { _timer_us = timer_us; ENABLE_TIM_COUNT(_timer_us); } i2c_gpio_group_t gpios; int ret = 1; gpios.scl = gpio_create(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN); gpios.sda = gpio_create(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN); rtc = i2c_create(gpios, 10); rtc_dummy_read(); /* wait for VLF bit clear */ ret = rtc_wait_vlf_clear(); if (ret == 0) { /* software reset */ ret = rtc_soft_reset(); if (ret == FALSE) { return FALSE; } } else if (ret == 1) { return FALSE; } /* register initialize */ return rtc_clock_reginit(); } /** * @brief RTC芯片反初始化 * @return {*} * @note */ BOOL rtc_dinit(void) { GPIO_SET_ANALOG(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN); GPIO_SET_ANALOG(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN); return TRUE; } /** * @brief 从RTC芯片读取时间 * @param {uint8_t} *read_buf - 接收缓存指针,用于接收读取到的数据 * @return {BOOL} TRUE = 成功,FALSE = 失败 * @note */ BOOL rtc_get_clock_time(uint8_t *read_buf) { return rtc_read_bytes(read_buf, RTC_CLOCK_ADDR, 7); } /** * @brief 向RTC芯片写入时间 * @param {rtc_date} *data - 发送缓存指针,用于存储要发送的数据 * @return {BOOL} TRUE = 成功,FALSE = 失败 * @note */ BOOL rtc_set_clock_time(rtc_date *data) { BOOL ret = FALSE; uint8_t tmp[7]; tmp[0] = data->second; tmp[1] = data->minute; tmp[2] = data->hour; tmp[3] = data->weekday; tmp[4] = data->day; tmp[5] = data->month; tmp[6] = data->year; tmp[3] = (rtc_week_e)tmp[3]; // 改成星期几 /* stop clock */ ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR); /* set the present time */ ret = rtc_write_bytes(tmp, RTC_CLOCK_ADDR, sizeof(tmp)); /* start clock */ ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR); return ret; } /** * 获取实时时钟(RTC)的时间戳 * * 该函数从RTC设备中获取当前的日期和时间,并将其转换为自1970年1月1日以来的秒数(时间戳)。 * 如果无法从RTC设备中获取时间,则返回0。 * * @return 返回从1970年1月1日以来的秒数(时间戳),如果无法获取时间则返回0。 */ uint32_t rtc_timestamp(void) { BOOL ret = FALSE; uint8_t tmp[7]; rtc_date_t date; rtc_time_t time; ret = rtc_get_clock_time(tmp); if (ret == FALSE) { return 0; } date.year = hex_format_dec(tmp[6]); date.month = hex_format_dec(tmp[5]); date.day = hex_format_dec(tmp[4]); time.hour = hex_format_dec(tmp[2]); time.minute = hex_format_dec(tmp[1]); time.second = hex_format_dec(tmp[0]); return time2stamp(&date, &time); } /** * @brief 将星期转为星期码 * @param {uint8_t} *weekday 星期几 * @return {*} * @note */ void rtc_weekday_convert(uint8_t *weekday) { switch (*weekday) { case 1: *weekday = MON; break; case 2: *weekday = TUE; break; case 3: *weekday = WED; break; case 4: *weekday = THUR; break; case 5: *weekday = FRI; break; case 6: *weekday = SAT; break; case 7: *weekday = SUN; break; default: *weekday = 0; break; } } /** * @brief 将星期码转为星期 * @param {uint8_t} *weekday 星期码 * @return {*} * @note */ void rtc_weekday_rconvert(uint8_t *weekday) { switch (*weekday) { case MON: *weekday = 1; break; case TUE: *weekday = 2; break; case WED: *weekday = 3; break; case THUR: *weekday = 4; break; case FRI: *weekday = 5; break; case SAT: *weekday = 6; break; case SUN: *weekday = 7; break; default: *weekday = 0; break; } }