计时芯片
就是一个需要连接32.768k晶振的RTC芯片
规格书阅读
首先我们先读懂这个芯片是怎么用的。
引脚表
封装是这样的,一共10个引脚。
基本上一看这个引脚表就知道大概。
T1和T2是工厂测试的,不用管。
SCL和SDA是IIC通讯用的。
FOUT和FOE就是链接晶振的。
INT是中断输出,闹钟、唤醒啥的都靠它。
其他的都是电源和地了。
模块框图
这个也简单,就是个S-35390A的简化版,有需要的朋友可以看我另一篇文章。S-35390A计时芯片介绍及开发方案_s35390-CSDN博客
软件设计
寄存器定义
里面有好几组寄存器,可以按照命令来读取。
基础信息寄存器
// RX-8900 Basic Time and Calendar Register definitions
#define RA8900_BTC_SEC 0x00
#define RA8900_BTC_MIN 0x01
#define RA8900_BTC_HOUR 0x02
#define RA8900_BTC_WEEK 0x03
#define RA8900_BTC_DAY 0x04
#define RA8900_BTC_MONTH 0x05
#define RA8900_BTC_YEAR 0x06
#define RA8900_BTC_RAM 0x07
#define RA8900_BTC_ALARM_MIN 0x08
#define RA8900_BTC_ALARM_HOUR 0x09
#define RA8900_BTC_ALARM_WEEK_OR_DAY 0x0A
#define RA8900_BTC_TIMER_CNT_0 0x0B
#define RA8900_BTC_TIMER_CNT_1 0x0C
#define RA8900_BTC_EXT 0x0D
#define RA8900_BTC_FLAG 0x0E
#define RA8900_BTC_CTRL 0x0F
扩展寄存器1
// RX-8900 Extension Register 1 definitions
#define RA8900_EXT_SEC 0x10
#define RA8900_EXT_MIN 0x11
#define RA8900_EXT_HOUR 0x12
#define RA8900_EXT_WEEK 0x13
#define RA8900_EXT_DAY 0x14
#define RA8900_EXT_MONTH 0x15
#define RA8900_EXT_YEAR 0x16
#define RA8900_EXT_TEMP 0x17
#define RA8900_EXT_BACKUP 0x18
#define RA8900_EXT_TIMER_CNT_0 0x1B
#define RA8900_EXT_TIMER_CNT_1 0x1C
#define RA8900_EXT_EXT 0x1D
#define RA8900_EXT_FLAG 0x1E
#define RA8900_EXT_CTRL 0x1F
#define RA8900_EXT_0X30 0x30
#define RA8900_EXT_0X32 0x32
#define RA8900_EXT_0X40 0x40
#define RA8900_EXT_0X5A 0x5a
#define RA8900_EXT_0X5C 0x5c
扩展寄存器
// Flag RA8900_BTC_EXT Register bit positions
#define RA8900_BTC_EXT_TSEL0 (1 << 0)
#define RA8900_BTC_EXT_TSEL1 (1 << 1)
#define RA8900_BTC_EXT_FSEL0 (1 << 2)
#define RA8900_BTC_EXT_FSEL1 (1 << 3)
#define RA8900_BTC_EXT_TE (1 << 4)
#define RA8900_BTC_EXT_USEL (1 << 5)
#define RA8900_BTC_EXT_WADA (1 << 6)
#define RA8900_BTC_EXT_TEST (1 << 7)
标志位寄存器
#define RA8900_BTC_FLAG_VDET (1 << 0)
#define RA8900_BTC_FLAG_VLF (1 << 1)
#define RA8900_BTC_FLAG_AF (1 << 3)
#define RA8900_BTC_FLAG_TF (1 << 4)
#define RA8900_BTC_FLAG_UF (1 << 5)
控制寄存器
#define RA8900_BTC_CTRL_RESET (1 << 0)
#define RA8900_BTC_CTRL_AIE (1 << 3)
#define RA8900_BTC_CTRL_TIE (1 << 4)
#define RA8900_BTC_CTRL_UIE (1 << 5)
#define RA8900_BTC_CTRL_CSEL0 (1 << 6)
#define RA8900_BTC_CTRL_CSEL1 (1 << 7)
初始化
就是调用两个接口,用配置结构体初始化,接个状态回来。由于我这里用的是软件IIC,用到硬件IIC的朋友请自行初始化,调用下LPI2C_DRV_MasterInit函数。
RA8900_Init(&rtc_Config,&g_RA8900State);
结构体长这样,按照内容配置就行,不会的可以照抄。
rtc_user_config rtc_Config=
{
ONCE_PER_SECONDS,//按照秒或者分钟计时
FREQ_32dot768KHZ,//晶振选择
CLOCK_EVERY_SECOND,//时钟频率
EXECUTE_PER_2_SEC,//时钟补偿
false,//是否使能修正周期
false,//是否使能时钟更新
false,//是否使能闹钟
WEEK_ALARM,//闹钟按照天还是周
/*year,month,day,hour,min,second,week*///默认时钟
{ 18, 1, 1 ,0 , 0, 0, 1 },
/*min ,hour ,week, day ,AF ,enabled,wada,pending*///默认闹钟
{ 0, 0, 1 ,1 ,0, 0, 0, 0 }
};
代码如下
rtc_state g_RA8900State;
rtc_state *g_RA8900StatePtr=&g_RA8900State;
void RA8900_Init(rtc_user_config *userConfigPtr, rtc_state *rtc)
{
uint8_t ctrl[3]={0x02,0x00,0x61};/*the default data*/
g_RA8900StatePtr = rtc;
ctrl[0]=(userConfigPtr->WADA<<6)|(ctrl[0]&(~RA8900_BTC_EXT_WADA));
ctrl[0]=(userConfigPtr->updateTime <<5)|(ctrl[0]&(~RA8900_BTC_EXT_USEL));
ctrl[0]=(userConfigPtr->outputFreq<<2)|(ctrl[0]&(~(RA8900_BTC_EXT_FSEL0|RA8900_BTC_EXT_FSEL1)));
ctrl[0]=(userConfigPtr->timerClock)|(ctrl[0]&(~(RA8900_BTC_EXT_TSEL0|RA8900_BTC_EXT_TSEL1)));
/*config the ext reg must set TE \TEST to 1,RESET to 1 (when set clock need to stop clock) */
/*set TE \TEST to 1,RESET to 1,anothers are the config */
RA8900_WriteRegs(RA8900_BTC_EXT,3,ctrl);
RA8900_ReadRegs(RA8900_BTC_EXT,3,ctrl);
RA8900_SetTime(&(userConfigPtr->defaulRtcTime));
/*check the time fixedCycle function*/
if(userConfigPtr->isFixedCycleEnable)
{
/*if enable ,set the TE bit in EXT Reg*/
ctrl[0]=(userConfigPtr->isFixedCycleEnable<<4)|(ctrl[0]&(~RA8900_BTC_EXT_TE));
}/*if not enable,dont't need to change, it is 0 alreadly*/
/*set the function interrupt*/
ctrl[2]=(userConfigPtr->isAlarmEnable<<3)|(ctrl[2]&(~RA8900_BTC_CTRL_AIE));
ctrl[2]=(userConfigPtr->isFixedCycleEnable<<4)|(ctrl[2]&(~RA8900_BTC_CTRL_TIE));
ctrl[2]=(1<<5)|(ctrl[2]&(~RA8900_BTC_CTRL_UIE));
/*set the Temperature compensation */
ctrl[2]=(userConfigPtr->temperCompensation<<6)|(ctrl[2]&(~(RA8900_BTC_CTRL_CSEL0|RA8900_BTC_CTRL_CSEL1)));
/*reset the device*/
ctrl[2]=0;
RA8900_WriteRegs(RA8900_BTC_EXT,3,ctrl);
g_RA8900StatePtr->extReg=ctrl[0];
g_RA8900StatePtr->ctrlreg=ctrl[2];
g_RA8900StatePtr->flagReg=ctrl[1];
}
读取接口
互斥锁的作用就是防止同时读和写。
static bool RA8900_ReadRegs(uint8_t regIdx, uint8_t length,uint8_t *values)
{
bool ret = false;
uint8_t Try = 3;
if( xSemaphore_i2c != NULL )//互斥锁
{
if( xSemaphoreTake( xSemaphore_i2c, ( TickType_t ) 50 ) == pdTRUE )
{
while((Try--)&&(ret != true)) // allow setting 3 times
{
ret=I2C_read_bytes(RA8900_ADDRESS,regIdx,RTC_ADDR_BYTES,length,values);
}
xSemaphoreGive( xSemaphore_i2c);
}
}
return ret;
}
写入接口
因为要把命令先写进去,所以这里开了个buffer把命令和数据拼起来再发出去。地址默认为0x32
bool RA8900_WriteRegs(uint8_t regIdx,uint8_t length, uint8_t *values)
{
bool ret = false;
uint8_t Try = 3;
uint8_t masterbuffer[32]={0};
masterbuffer[0]=regIdx;
memcpy(&masterbuffer[1],values,length);
if( xSemaphore_i2c != NULL )
{
if( xSemaphoreTake( xSemaphore_i2c, ( TickType_t ) 50 ) == pdTRUE )
{
while((Try--)&&(ret != true))// allow setting 3 times
{
ret = I2C_write_bytes(RA8900_ADDRESS,length+ 1,masterbuffer);
}
xSemaphoreGive( xSemaphore_i2c);
}
}
return ret;
}
获取时间
time就是个结构体,里面有的东西下面都能看到了。将时间信息获取到regdate里面之后按照字节来转换BCD码。
uint8_t RA8900_GetTime(rtc_time *time)
{
uint8_t regdate[16]={0,0,0,0,0,0,0};
uint8_t err = 0;
err = RA8900_ReadRegs(RA8900_BTC_SEC,7,regdate);
time->second = RA8900_Bcd2Dec(regdate[0] & 0x7f);
time->minute = RA8900_Bcd2Dec(regdate[1] & 0x7f);
time->hour = RA8900_Bcd2Dec(regdate[2] & 0x3f);
time->week = RA8900_GetWeekDay(regdate[3] & 0x7f);
time->day = RA8900_Bcd2Dec(regdate[4] & 0x3f);
time->month = RA8900_Bcd2Dec(regdate[5] & 0x1f)-1;
time->year = RA8900_Bcd2Dec(regdate[6])+2000;
if (err==false)
{
return 0;
}
return 1;
}
设置当前时间
按照位置去写入就行。
uint8_t RA8900_SetTime(rtc_time *time)
{
uint8_t date[7];
int ret = 0;
date[0] = RA8900_Dec2Bcd(time->second);
date[1] = RA8900_Dec2Bcd(time->minute);
date[2] = RA8900_Dec2Bcd(time->hour);
date[3] = 1 << (time->week);
date[4] = RA8900_Dec2Bcd(time->day);
date[5] = RA8900_Dec2Bcd(time->month+1);
date[6] = RA8900_Dec2Bcd(time->year % 100);
ret = RA8900_WriteRegs(RA8900_BTC_SEC, 7, date);
return ret;
}
十进制转换BCD码
这种基本都是标准的
uint8_t RA8900_Dec2Bcd(uint8_t binData)
{
uint8_t ret;
ret = binData/10;//十位
binData %= 10; //个位
ret <<= 4;
ret += binData;
return ret;
}
BCD码转换为十进制
uint8_t RA8900_Bcd2Dec(uint8_t bcdData)
{
uint8_t ret;
ret = bcdData&0x0f;//个位。
bcdData >>= 4;
bcdData &= 0x0f;
bcdData *= 10;//十位
ret += bcdData;//相加
return ret;
}
获取闹钟时间
uint8_t RA8900_GetAlarm(rtc_alarm *alarmTime)
{
uint8_t alarmvals[3];//分,时,天
uint8_t ctrl[3];//标志位
int err;
//获取闹钟时间,分
err = RA8900_ReadRegs(RA8900_BTC_ALARM_MIN, 3, alarmvals);
if (!err){return err;}
//获取标志位:extension, flag, control values
err = RA8900_ReadRegs(RA8900_BTC_EXT, 3, ctrl);
if (!err){return err;}
//将获取到的闹钟信息转换为十进制
alarmTime->minute = RA8900_Bcd2Dec(alarmvals[0] & 0x7f);
alarmTime->hour = RA8900_Bcd2Dec(alarmvals[1] & 0x3f);
if (ctrl[0] & RA8900_BTC_EXT_WADA )//闹钟为每天
{
alarmTime->week = 0xff;
alarmTime->day = RA8900_Bcd2Dec(alarmvals[2] & 0x3f);
}
else//闹钟为每周
{
alarmTime->week = RA8900_GetWeekDay( alarmvals[2] & 0x7f );
alarmTime->day = 0xff;
alarmTime->WADA=0;
}
//检查闹钟中断是否使能
alarmTime->enabled = !!(ctrl[2] & RA8900_BTC_CTRL_AIE);
//检查闹钟是否已经触发,就是是否响了
alarmTime->pending = (ctrl[1] & RA8900_BTC_FLAG_AF) && alarmTime->enabled;
return err;
}
设置闹钟时间
可以是日闹钟,可以是周闹钟。
int RA8900_SetAlarmClock(rtc_alarm *alarmClockTime)
{
uint8_t alarmvals[3];//分, 时, 天
uint8_t ctrl[3];//控制信息
int err;
alarmvals[0] = RA8900_Dec2Bcd(alarmClockTime->minute);
alarmvals[1] = RA8900_Dec2Bcd(alarmClockTime->hour);
//获取控制信息
RA8900_ReadRegs(RA8900_BTC_EXT, 3, ctrl);
if(alarmClockTime->WADA)
{
alarmvals[2] = RA8900_Dec2Bcd(alarmClockTime->day);
ctrl[0] |= RA8900_BTC_EXT_WADA;
}
else
{
/* bit: 7 6 5 4 3 2 1 0 */
/* week: 0 Sat Fri Thur Wed Tue Mon Sun */
alarmvals[2] = alarmClockTime->week;
ctrl[0] &= ~(RA8900_BTC_EXT_WADA);
}
RA8900_WriteRegs( RA8900_BTC_EXT, 1, &ctrl[0]);
ctrl[2]= RA8900_BTC_CTRL_AIE|ctrl[2];
ctrl[2] &= ~(RA8900_BTC_CTRL_UIE | RA8900_BTC_CTRL_TIE);
RA8900_WriteRegs(RA8900_BTC_CTRL, 1, &ctrl[2]);
RA8900_WriteRegs(RA8900_BTC_ALARM_MIN, 3, alarmvals);
ctrl[1] &= ~RA8900_BTC_FLAG_AF;
err = RA8900_WriteRegs(RA8900_BTC_FLAG, 1 ,&ctrl[1]);
return err;
}
停止闹钟
int RA8900_StopAlarm(void)
{
uint8_t ctrl[1];//控制位 ext, flag registers
int err;
RA8900_ReadRegs(RA8900_BTC_CTRL, 1, ctrl);
ctrl[0] &= ~RA8900_BTC_CTRL_AIE;
err = RA8900_WriteRegs(RA8900_BTC_CTRL, 1, ctrl);
return err;
}
清除所有标志位
void RA8900_ClearFlag(uint8_t flag)
{
uint8_t currentFlag;
RA8900_ReadRegs(RA8900_BTC_FLAG,1,¤tFlag);
currentFlag=currentFlag & (~flag);
RA8900_WriteRegs(RA8900_BTC_FLAG,1,¤tFlag);
}
转换为周几
长度7位,从低到高哪位为1就是周几。
static uint_8 RA8900_GetWeekDay(uint8_t regWeekDay )
{
uint_8 index, timeWeekDay;
for ( index=0; index < 7; index++ )
{
if ( regWeekDay & 1 )
{
timeWeekDay = index;
break;
}
regWeekDay >>= 1;
}
return timeWeekDay;
}
获取温度
只是个功能,没啥用,将获取到的数值按公式算就行。
#define RA8900_EXT_TEMP 0x17
uint8_t RA8900_GetTemp(void)
{
uint8_t tempvalue = 0;
float RtcTemp = 0.0;
RA8900_ReadRegs(RA8900_EXT_TEMP,1,&tempvalue);
RtcTemp = (tempvalue*2 - 187.19)/3.218;
tempvalue = (uint8_t)RtcTemp;
return tempvalue;
}
保持增强功能
他有个增强功能,要在初始化的时候做这几件事,50ms为周期调用下面这个初始化接口。其实就是写入几个命令。
#define RA8900_EXT_0X30 0x30
#define RA8900_EXT_0X32 0x32
#define RA8900_EXT_0X40 0x40
#define RA8900_EXT_0X5A 0x5a
#define RA8900_EXT_0X5C 0x5c
uint8_t RA8900CE_Inhance(void)
{
bool err = false;
uint8_t ctrl = 0;
static uint8_t s_Step = FSM_STEP_01;//默认为第一步
switch(s_Step)
{
case FSM_STEP_01://第一步
{
ctrl = 0xd1;
err = RA8900_WriteRegs(RA8900_EXT_0X30,1,&ctrl);
if (!err){return err;}
ctrl = 0x00;
err = RA8900_WriteRegs(RA8900_EXT_0X40,1,&ctrl);
if (!err){return err;}
ctrl = 0x81;
err = RA8900_WriteRegs(RA8900_EXT_0X32,1,&ctrl);
if (!err){return err;}
s_Step = FSM_STEP_02;//跳到第二步
}break;
case FSM_STEP_02://第二步
{
ctrl = 0x80;
err = RA8900_WriteRegs(RA8900_EXT_0X32,1,&ctrl);
if (!err){return err;}
s_Step = FSM_STEP_03;//跳到第三步
break;
}
case FSM_STEP_03://第三步
{
ctrl = 0x04;
err = RA8900_WriteRegs( RA8900_EXT_0X32, 1, &ctrl);
if (!err){return err;}
s_Step = FSM_STEP_04;//跳到第四步
break;
}
case FSM_STEP_04://第四步
{
ctrl = 0x00;
err = RA8900_ReadRegs(RA8900_EXT_0X5C,1,&ctrl);
if (!err){return err;}
ctrl &= ~0x0f;
ctrl |= 0x0c;
err = RA8900_WriteRegs(RA8900_EXT_0X5C,1,&ctrl);
if (!err){return err;}
err = RA8900_ReadRegs(RA8900_EXT_0X5A,1,&ctrl);
if (!err){return err;}
ctrl &= ~0xe0;
ctrl |= 0xe0;
err = RA8900_WriteRegs( RA8900_EXT_0X5A, 1, &ctrl);
if (!err){return err;}
ctrl = 0x00;
err = RA8900_WriteRegs(RA8900_EXT_0X30,1, &ctrl);
if (!err){return err;}
s_Step = FSM_STEP_END;//结束
break;
}
default:
s_Step = FSM_STEP_END;
break;
}
return err;
}