文章目录
1 介绍
rx8900 软件包是针对RT-Thread驱动框架实现的实时时钟驱动,遵循RT-Thread RTC框架,可以从芯片内置RTC无缝切换使用外置高精度rx8900 RTC。rx8900是爱普生公司一款i2c接口
的高精度RTC芯片,集成32768Hz晶振和温度传感器,并能根据环境温度调整频率,具有供电范围灵活、待机电流低、走时精度高等优点。
1.1 支持功能
- 实时时钟
- 闹钟
- 片内温度读取
1.2 目录结构
名称 | 说明 |
---|---|
docs | 文档目录 |
inc | 头文件目录 |
src | 源代码目录 |
LICENSE | 许可证文件 |
SConscript | RT-Thread默认构建脚本 |
1.3 许可证
rx8900 软件包遵循 Apache license v2.0 许可,详见 LICENSE
文件。
1.4 依赖
- RT-Thread 3.0+
- RT-Thread I2C设备驱动框架
- RT-Thread RTC设备驱动框架
2 实现功能
实现rx8900驱动层的函数接口。
2.1 rx8900驱动描述
RT-Thread rtc驱动设备继承其标准设备驱动,使用struct rt_device
描述rx8900设备。rx8900访问接口是i2c,依赖于RT-Thread i2c总线框架,我们使用struct rt_device
的私有数据user_data
保存rx8900的i2c设备总线信息。后面过程则是实现struct rt_device
函数指针实体。
/**
* Device structure
*/
struct rt_device
{
struct rt_object parent; /**< inherit from rt_object */
enum rt_device_class_type type; /**< device type */
rt_uint16_t flag; /**< device flag */
rt_uint16_t open_flag; /**< device open flag */
rt_uint8_t ref_count; /**< reference count */
rt_uint8_t device_id; /**< 0 - 255 */
/* device call back */
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
#ifdef RT_USING_DEVICE_OPS
const struct rt_device_ops *ops;
#else
/* common device interface */
rt_err_t (*init) (rt_device_t dev);
rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close) (rt_device_t dev);
rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
#endif
#if defined(RT_USING_POSIX)
const struct dfs_file_ops *fops;
struct rt_wqueue wait_queue;
#endif
void *user_data; /**< device private data */
};
static struct rt_device rx8900_dev; /* rx8900 device */
2.2 rx8900读寄存器接口
static rt_err_t rx8900_write_reg(rt_device_t dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t data_size)
- dev,rx8900设备描述地址
- reg,寄存器地址
- data,返回数据地址(缓存)
- data_size,读取数据长度
- 返回值,成功返回RT_EOK
2.3 rx8900写寄存器接口
static rt_err_t rx8900_write_reg(rt_device_t dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t data_size)
- dev,rx8900设备描述地址
- reg,寄存器地址
- data,待写数据地址
- data_size,待写数据长度
- 返回值,成功返回RT_EOK
2.4 RT-Thread设备虚拟文件接口
RT-Thread设备虚拟文件接口,常用有open/read/write/control/close
,RT-Thread rtc设备框架中,关于时间和闹钟的设置,使用的是control
接口,实现该接口具体功能内容。
- 获取实时时间
/* get time */
case RT_DEVICE_CTRL_RTC_GET_TIME:
time = (time_t *)args;
ret = rx8900_read_reg(dev, REG_SEC, buff, 7);
if(ret == RT_EOK)
{
time_temp.tm_year = bcd_to_hex(buff[6]) + 2000 - 1900;
time_temp.tm_mon = bcd_to_hex(buff[5]&0x1f) - 1;
time_temp.tm_mday = bcd_to_hex(buff[4]&0x3f);
time_temp.tm_hour = bcd_to_hex(buff[2]&0x3f);
time_temp.tm_min = bcd_to_hex(buff[1]&0x7f);
time_temp.tm_sec = bcd_to_hex(buff[0]&0x7f);
*time = mktime(&time_temp);
}
break;
- 设置实时时间
/* set time */
case RT_DEVICE_CTRL_RTC_SET_TIME:
{
struct tm *time_new;
time = (time_t *)args;
time_new = localtime(time);
buff[6] = hex_to_bcd(time_new->tm_year + 1900 - 2000);
buff[5] = hex_to_bcd(time_new->tm_mon + 1);
buff[4] = hex_to_bcd(time_new->tm_mday);
buff[3] = hex_to_bcd(time_new->tm_wday+1);
buff[2] = hex_to_bcd(time_new->tm_hour);
buff[1] = hex_to_bcd(time_new->tm_min);
buff[0] = hex_to_bcd(time_new->tm_sec);
ret = rx8900_write_reg(dev, REG_SEC, buff, 7);
}
break;
- 获取闹钟时间
rx8900闹钟模式支持的最小精度是1分钟,因此闹钟秒数值(tm_sec
)应设置为0。rx8900支持天数和星期闹钟,RT-Thread目前闹钟框架暂时支持时分秒匹配,天数和星期由软件实现。
/* get alarm time */
case RT_DEVICE_CTRL_RTC_GET_ALARM:
{
struct rt_rtc_wkalarm *alm_time;
rt_uint8_t state[3] = {0x00};
ret = rx8900_read_reg(dev, REG_ALM_MIN, buff, 2);
if(ret != RT_EOK)
{
return -RT_ERROR;
}
alm_time = (struct rt_rtc_wkalarm *)args;
alm_time->tm_hour = bcd_to_hex(buff[1]&0x7f);
alm_time->tm_min = bcd_to_hex(buff[0]&0x7f);
alm_time->tm_sec = 0; /* hardware alarm precision is 1 minute */
ret = rx8900_read_reg(dev, RGE_EXT, state, 3);
if(ret != RT_EOK)
{
return -RT_ERROR;
}
if (state[0] & VALUE_EXT_WADA)
{/* day alarm */
/* todo, RTT not supported temporarily */
}
else
{/* week day alarm */
/* todo, RTT not supported temporarily */
}
}
- 设置闹钟时间
针对RT-Thread闹钟框架,rx8900闹钟禁止天数(tm_day
)和星期(tm_wday
)匹配功能,只设置小时(tm_hour
)和分钟(tm_minute
),同时将秒数(tm_sec
)设为0。
/* set alarm time */
case RT_DEVICE_CTRL_RTC_SET_ALARM:
{
struct rt_rtc_wkalarm *alm_time;
rt_uint8_t ctrl[2] = {0x00};
alm_time = (struct rt_rtc_wkalarm *)args;
buff[2] = ALM_DISABLE(0x00); /* week and day alarm disable */
buff[1] = hex_to_bcd(ALM_ENABLE(alm_time->tm_hour));/* enable, alarm when hours and minutes match */
buff[0] = hex_to_bcd(ALM_ENABLE(alm_time->tm_min));
ret = rx8900_read_reg(dev, REG_FLAG, ctrl, 2);
if (ret != RT_EOK)
{
return -RT_ERROR;
}
if (ctrl[0]&VALUE_EXT_WADA)
{/* day alarm */
/* todo, RTT not supported temporarily */
}
else
{/* week day alarm */
/* todo, RTT not supported temporarily */
}
/* write to hardware */
ret = rx8900_write_reg(dev, REG_ALM_MIN, buff, 3);
if (ret != RT_EOK)
{
return -RT_ERROR;
}
/* clear alarm flag */
ctrl[1] &= ~VALUE_FLAG_AF;
ret = rx8900_write_reg(dev, REG_FLAG, &ctrl[1], 1);
}
break;
2.5 片内温度获取接口
rx8900内部集成一个温度传感器和温度校准系数,读取温度寄存器值通过一定的换算公式计算出实际温度。rx8900芯片手册提供摄氏度(℃)与寄存器值转换公式如下。
获取温度接口
/*
* @brief Read temperature from rx8900
* @return Celsius temperature,-55~+125C,0.1C
*/
float rx8900_get_temperature(void)
{
rt_uint8_t value = 0;
float temp = 0.0f;
rx8900_read_reg(&rx8900_dev, REG_TEMP, (rt_uint8_t*)&value, 1);
temp = (value*2-187.19)/3.218;
return temp;
}
我们把温度读取接口注册到RT-Thread的调试终端(msh/finsh),方便测试。
#ifdef RT_USING_FINSH
#include <finsh.h>
void list_rx89_temp(void)
{
float temp = 0.0f;
temp = rx8900_get_temperature();
rt_kprintf("rx8900 temperature: [%d.%dC] \n", (int)temp, (int)(temp * 10) % 10);
}
FINSH_FUNCTION_EXPORT(list_rx89_temp, list rx8900 temperature.)
#endif /* RT_USING_FINSH */
#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
MSH_CMD_EXPORT(list_rx89_temp, list rx8900 temperature.);
#endif /* RT_USING_FINSH & FINSH_USING_MSH */
2.6 rtc设备注册
rx8900设备注册
- rx8900使用的是系统
“i2c1”
,i2c1驱动请自行初始化注册;否则rx8900设备注册失败 - rx8900设备中
user_data
保存i2c1总线信息,用于后续访问rx8900 - rx8900设备名称保持与rtc设备框架的rtc名称一致,统一为
“rtc”
int rt_hw_rx8900_init(void)
{
struct rt_i2c_bus_device *i2c_bus =RT_NULL;
rt_uint8_t flags = 0;
rt_bool_t need_clear = RT_FALSE;
rt_bool_t need_reset = RT_FALSE;
i2c_bus = rt_i2c_bus_device_find(RX8900_I2C_BUS);
if (i2c_bus == RT_NULL)
{
LOG_E("i2c bus device %s not found!\r\n", RX8900_I2C_BUS);
return -RT_ERROR;
}
/* register rtc device */
rx8900_dev.type = RT_Device_Class_RTC;
rx8900_dev.init = RT_NULL;
rx8900_dev.open = rt_rx8900_open;
rx8900_dev.close = RT_NULL;
rx8900_dev.read = rt_rx8900_read;
rx8900_dev.write = RT_NULL;
rx8900_dev.control = rt_rx8900_control;
rx8900_dev.user_data= (void*)i2c_bus; /* save i2cbus */
rt_device_register(&rx8900_dev, RX8900_DEVICE_NAME, RT_DEVICE_FLAG_RDWR);
......
}
rx8900私有信息初始化
rx8900私有信息初始化主要包括:
- 获取rx8900状态寄存器值
- 根据状态寄存器值每一bit的含义执行复位、初始化操作或者清除状态寄存器操作
if (flags & VALUE_FLAG_VDET)
{
LOG_D("Temperature compensation stop detected.\n");
need_clear = RT_TRUE;
}
/* alarm */
if (flags & VALUE_FLAG_AF)
{
LOG_D("Alarm was detected.\n");
need_clear = RT_TRUE;
}
/* timer */
if (flags & VALUE_FLAG_TF)
{
LOG_D("Timer was detected.\n");
need_clear = RT_TRUE;
}
......
3 获取 rx8900 软件包
使用 rx8900 package, 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
RT-Thread online packages --->
peripheral libraries and drivers --->
[*] extren rtc drivers --->
[*] rx8900:Extern RTC drivers fo rx8900
Version (latest) --->
然后让 RT-Thread 的包管理器自动更新,或者执行 pkgs --update
命令更新包到 BSP 中。
4 使用 rx8900 软件包
4.1 初始化
首先需初始化rx8900 驱动,可以手动在初始化线程合适的地方调用rt_hw_rx8900 _init()
初始化,也可以直接使用INIT_DEVICE_EXPORT
通过RT-Thread的自动初始化机制来初始化注册。初始化成功后,会注册名称为“rtc”
的驱动设备。
INIT_DEVICE_EXPORT(rt_hw_rx8900_init); /* 通过RTT的自动初始化机制初始化rx8900 */
注:
如果开启了芯片内部RTC,需先禁止内部RTC
4.2 使用方式
查看设备是否注册成功。
对于时间和闹钟的访问,使用RT-Thread标准RTC和闹钟接口访问。片内温度读取,则由软件包提供一个函数接口。
- 获取当前时间
time_t time(time_t *t)
time_t now;
now = time(RT_NULL);
- 设置时间
#define RT_DEVICE_CTRL_RTC_SET_TIME 0x11 /**< set time */
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg);
- 获取闹钟时间
#define RT_DEVICE_CTRL_RTC_GET_ALARM 0x12 /**< get alarm */
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg);
- 设置闹钟时间
#define RT_DEVICE_CTRL_RTC_SET_ALARM 0x13 /**< set alarm */
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg);
注:
详细用法可以参考“/components/drivers/rtc.c”和“/components/drivers/alarm.c”源码。
- 提供一个获取片内温度接口
float rx8900_get_temperature(void);
4.3 msh/finsh测试
- msh获取时间
msh >date
Sat Jan 1 00:00:04 2000
- msh设置时间
msh date 2020 04 10 17 41 10
msh >date
Fri Apr 10 17:41:15 2020
- finsh获取时间
finsh >list_date()
Fri Apr 10 17:41:49 2020
- finsh设置时间
finsh >set_date(2020,5,1)
0, 0x00000000
finsh >set_time(12,0,0)
0, 0x00000000
finsh >list_date()
Fri May 1 12:00:04 2020
- msh打印温度
msh >list_rx89_temp
rx8900 temperature: [27.5C]
- finsh打印温度
finsh >list_rx89_temp()
rx8900 temperature: [27.5C]
5 注意事项
使用RT-Thread的RTC框架,RTC设备注册名称为“rtc”
,注意需先屏蔽芯片内置RTC驱动。