【简介】
在上一篇我们已经基于S32K146 完成了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已经正常工作几秒后年月日 信息均发生更新,跟预期的保持一致。