561 lines
13 KiB
C
561 lines
13 KiB
C
/**
|
||
* @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;
|
||
}
|
||
}
|