【简介】
在上一篇我们已经基于S32K146完成了I2C总线设备的驱动(s32k146适配zephyr(三)添加I2C总线设备驱动)。本地的LPI2C上挂载了四个I2C设备,我们使用I2C总线驱动 RA8900CE 芯片,RA8900CE 是一个RTC时钟芯片,对应框图的如下。

查看芯片的寄存器如下,OD/OF对应的控制寄存器,如果我们不需要实现中断输出及alarm 功能,我们不需要额外的配置,只需要配置00-06寄存器就会周期的更新计数,然后读取数据即可。

我们暂时只是驱动RA8900CE芯片能够读取配置RTC时间为基本任务,暂可以不关注上述框起来控制寄存器。
对应的时间寄存器描述如下



寄存器的数据格式采用BCD编码的数据格式,读取配置都需要进行格式转换。我们按照RTC芯片的寄存器layout 分别定义RTC BCD格式时间和便准时间的数据结构。
typedef struct {
/* [RA8900CE Register table */
uint8_t second; /**< second Register, offset: 0x0 */
uint8_t minutes; /**< minutes Register, offset: 0x1 */
uint8_t hour; /**< hour Register, offset: 0x2 */
uint8_t week; /**< week Register, offset: 0x3 */
uint8_t day; /**< day Register, offset: 0x4 */
uint8_t month; /**< month Register, offset: 0x5 */
uint8_t year; /**< year Register, offset: 0x6 */
}ExternRTCType;
typedef struct {
uint8_t second; /**< second Register, offset: 0x0 */
uint8_t minutes; /**< minutes Register, offset: 0x1 */
uint8_t hour; /**< hour Register, offset: 0x2 */
uint8_t week; /**< week Register, offset: 0x3 */
uint8_t day; /**< day Register, offset: 0x4 */
uint8_t month; /**< month Register, offset: 0x5 */
uint8_t year; /**< year Register, offset: 0x6 */
}ExternSTDType;按照BCD 和标准的时间格式实现对应格式转换代码
/* external RTC data 2 std data */
static void ExtrRtc_RtcTime2StdTime(ExternRTCType *rtcData,ExternSTDType *stdData)
{
uint8_t l_pos = 0 ,l_i = 0;
ExternSTDType stdDataBuf={0} ;
ExternRTCType rtcDataBuf = *rtcData;
for(l_pos=0;l_pos<EX_RTC_TIME_LEN;l_pos++)
{
*(&stdDataBuf.second+l_pos) = 0 ;
if(l_pos!=3)//sec min hour day month year
{
for(l_i=0;l_i<8;l_i++)
{
if( ( *(&rtcDataBuf.second + l_pos) & (1<<(7-l_i)) ) != 0 )
{
*(&stdDataBuf.second+l_pos) += rtaDataMap[l_pos][l_i] ;
}
}
}
else//week
{
for(l_i=0;l_i<8;l_i++)
{
if( ( *(&rtcDataBuf.second + l_pos) & (1<<(7-l_i)) ) != 0 )
{
*(&stdDataBuf.second+l_pos) += rtaDataMap[l_pos][l_i];
break;
}
}
}
}
*stdData = stdDataBuf;
}
/* external std data 2 RTC data */
static void ExtrRtc_StdTime2RtcTime(ExternSTDType *stdData,ExternRTCType *rtcData)
{
uint8_t l_pos = 0 ,l_i = 0;
ExternSTDType stdDataBuf=*stdData ;
ExternRTCType rtcDataBuf = {0};
for(l_pos=0;l_pos<EX_RTC_TIME_LEN;l_pos++)
{
*(&rtcDataBuf.second+l_pos) = 0 ;
if(l_pos!=3)//sec min hour day month year
{
for(l_i=0;l_i<8;l_i++)
{
if(( rtaDataMap[l_pos][l_i] != 0 ) &&
( *(&stdDataBuf.second + l_pos) >= rtaDataMap[l_pos][l_i] ) )
{
*(&rtcDataBuf.second+l_pos) |= (1<<(7-l_i));
*(&stdDataBuf.second + l_pos) -= rtaDataMap[l_pos][l_i];
}
}
}
else//week
{
if( *(&stdDataBuf.second + l_pos) < 7 )// 0 1 2 3 4 5 6
{
*(&rtcDataBuf.second+l_pos) |= (1<<(*(&stdDataBuf.second + l_pos)));
}
}
}
*rtcData = rtcDataBuf;
}使用zephyr的I2C 总线接口实现RTC芯片读写RTC寄存器代码。
/* external RTC write */
static uint8_t ExtrRtc_Write(uint8_t address,uint8_t *bufferPtr,uint32_t length)
{
uint8_t l_returnValue = RET_EOK;
static uint8_t l_databuf[EXTERN_RTC_DATA_MAX_LEN+1];
static struct i2c_msg msg;
/* RTC write atart address*/
l_databuf[0] = address ;
/* RTC write data copy to l_databuf */
ExtrRtc_MemCpy(bufferPtr,&l_databuf[1],length);
if(dev_lpi2c ==NULL)
{
dev_lpi2c = device_get_binding("i2c@40066000");
if(dev_lpi2c == NULL)
{
printf("i2c binding failed.\r\n");
}
l_returnValue = RET_ERROR;
}
if(dev_lpi2c)
{
msg.len = 1 + length;
msg.buf = l_databuf;
msg.flags = I2C_MSG_WRITE | I2C_MSG_STOP;
if(i2c_transfer(dev_lpi2c,&msg,1,EX_RTC_ADDR))
{
l_returnValue = RET_ERROR;
}
}
return l_returnValue;
}
/* external RTC read */
static uint8_t ExtrRtc_Read(uint8_t address,uint8_t *bufferPtr,uint32_t length)
{
uint8_t l_returnValue = RET_EOK;
static uint8_t l_databuf[EXTERN_RTC_DATA_MAX_LEN+1];
static struct i2c_msg msg;
/* RTC write atart address*/
l_databuf[0] = address ;
if(dev_lpi2c ==NULL)
{
dev_lpi2c = device_get_binding("i2c@40066000");
if(dev_lpi2c == NULL)
{
printf("i2c binding failed.\r\n");
}
l_returnValue = RET_ERROR;
}
if(dev_lpi2c)
{
msg.len = 1;
msg.buf = l_databuf;
msg.flags = I2C_MSG_WRITE | I2C_MSG_STOP;
if(i2c_transfer(dev_lpi2c,&msg,1,EX_RTC_ADDR))
{
l_returnValue = RET_ERROR;
goto out;
}
msg.len = length;
msg.buf = bufferPtr;
msg.flags = I2C_MSG_READ | I2C_MSG_STOP;
if(i2c_transfer(dev_lpi2c,&msg,1,EX_RTC_ADDR))
{
l_returnValue = RET_ERROR;
goto out;
}
}
out:
return l_returnValue;
}以上基本的格式转换和I2C读写访问接口实现完成后我们添加data 测试命令用于设置读取RTC时间来验证RTC功能。
static unsigned int date(char argc,char ** argv)
{
ExternRTCType rtc_data;
ExternSTDType std_data;
const char * week[] = {"Sun","Mon","Tues","Wed","Thur","Fri","Sat"};
if(argc == 1)
{
ExtrRtc_Read(0,(uint8_t *)&rtc_data,7);
ExtrRtc_RtcTime2StdTime(&rtc_data,&std_data);
printf("%d-%d-%d,%s,%d:%d:%d \r\n",std_data.year + 2000,std_data.month,std_data.day,week[std_data.week],
std_data.hour,std_data.minutes,std_data.second);
}
if(argc == 7)
{
std_data.year = atoi(argv[1])-2000;
std_data.month = atoi(argv[2]);
std_data.day = atoi(argv[3]);
std_data.week = inner_weekday(std_data.year,std_data.month,std_data.day);
std_data.hour = atoi(argv[4]);
std_data.minutes = atoi(argv[5]);
std_data.second = atoi(argv[6]);
ExtrRtc_StdTime2RtcTime(&std_data,&rtc_data);
ExtrRtc_Write(0,(uint8_t *)&rtc_data,7);
}
return 0;
}
LTSH_FUNCTION_EXPORT(date,"get/set rtc time");【功能验证】
将时间设置为2025-12-31 23:59:56 后连续读取RTC时间,RTC已经正常工作几秒后年月日 信息均发生更新,跟预期的保持一致。
在驱动的基础上我们继续添加如下代码,在初始化时根据编译当前文件的时间戳,提取时间更新到RTC内部
// 月份缩写到数字的映射
int get_month_num(const char *month_str) {
const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
for (int i = 0; i < 12; i++) {
if (strcmp(month_str, months[i]) == 0) {
return i + 1;
}
}
return -1;
}
int rtcinit(void) {
printf("rtc init.\r\n");
ExternRTCType rtc_data;
ExternSTDType std_data;
// 定义存储日期和时间的变量
int year, month, day, hour, minute, second;
// 解析 __DATE__ 宏获取年、月、日
char month_str[4];
sscanf(__DATE__, "%3s %d %d", month_str, &day, &year);
month = get_month_num(month_str);
year -= 2000;
// 解析 __TIME__ 宏获取时、分、秒
sscanf(__TIME__, "%d:%d:%d", &hour, &minute, &second);
std_data.year = year;
std_data.month = month;
std_data.day = day;
std_data.week = inner_weekday(std_data.year,std_data.month,std_data.day);
std_data.hour = hour;
std_data.minutes = minute;
std_data.second = second;
ExtrRtc_StdTime2RtcTime(&std_data,&rtc_data);
ExtrRtc_Write(0,(uint8_t *)&rtc_data,7);
return 0;
}
SYS_INIT(rtcinit, APPLICATION, 2);启动后输入date 命令读取RTC时间已经是代码的编译时间了。

我要赚赏金
