计时芯片
S-35390A芯片是计时芯片,一般用来计算时间。低功耗,宽电压,受温度影响小,适用于很多电路。它有一个问题,不阻止用户设置不存在的时间,设置进去之后计时或者闹钟定时会出错。
规格书阅读
首先我们先读懂这个芯片是怎么用的。
引脚表
基本上一看这个引脚表就知道大概,1号和5号引脚是中断输出引脚,2号和3号引脚之间应该接个晶振,6号和7号引脚通过IIC跟主控芯片通讯,其余的都是电源和地。
模块框图
大概就能知道里面的框架是啥样的,里面最主要就是实时时间的寄存器,通过跟中断1和中断2寄存器对比决定两路中断的输出。具体时间通过中间的总线借助IIC串口通讯模块发出去。另外就是有几个检测电源电压的寄存器、状态寄存器、时钟校正寄存器和一个给用户可以当做E方用的自由寄存器。
通讯数据配置
跟普通的IIC一样,主控芯片先发个设备号0110,跟着3位的命令,1位的读写方向,等到计时芯片ACK之后,继续发送剩下的数据。
命令表格
这里有所有的命令,读写状态寄存器1和2、读写实时数据1和2、读写中断寄存器1和2、存取时钟校正寄存器和自由寄存器。
标1的是只写。标2的是用户自由寄存器,对应上面框图的用户自由寄存器。标3和4的是只读。标5的是测试位,使用的时候要保证为0。标6的怎么写都不影响。
状态寄存器1
POC检测电源电流,初始化的时候是0,有电流的时候是1,如果上电之后为0,就要重启整个芯片。
BLD检测电源电压,正常为0,读到是1的时候要重启整个芯片了。
INT1和INT2是中断事件发生标志位。
SC0和SC1都是用户自由定义的。
12/24如果是0就是12小时制,如果是1就是24小时制。
RESET平时为0,写个1进去就重启芯片。
状态寄存器2
TEST测试位设置为0就行,当设置为1的时候,INT1会输出1Hz的方波。
INT2AE、 INT2ME、 INT2FE共同决定了中断输出2引脚的输出模式:无中断、输出用户设定的频率、每分钟边沿中断、50%占空比的每分钟周期中断、闹钟模式。
INT1AE、 INT1ME、 INT1FE共同决定了中断输出1引脚的输出模式:无中断、输出用户设定的频率、每分钟边沿中断、50%占空比的每分钟周期中断、闹钟模式、每分钟周期中断、输出32.768kHz。
实时数据1和2
以写入实时数据寄存器1为例,流程如下。
里面的年是按照西历来计算的,只能写0-99,使用BCD码,低位在前。
譬如2053年,就写入53,二进制Y1-Y80就是1100 1010.
AM/PM如果是24小时制就无所谓写什么,如果是12小时制0是am,1就是pm.
后面的实时数据寄存器2也是一样。
中断寄存器1和2
根据状态寄存器2的设置,选择中断输出引脚是闹钟模式或者输出用户设定的频率。
闹钟模式
时间到了就触发中断,里面的AM/PM要跟实时时钟里面一致。
B0的三个标志位表示设置的周、时、分是否有效,譬如下面这个设置闹钟为每天晚上7点,周的标志位就需要为0.
输出用户设定的频率
频率是每个位累加的,SC是用户自由寄存器。
校准寄存器
可以提前和延迟一定的时间。
用户自由寄存器
写一些用户想存储的信息,当做E方使用
使用注意事项
上电后POC为0,或者POC为1但是INT没有输出1 Hz方波,芯片内部数据可能处于不确定状态,需要重新上电。
上电之后由于检测电路正在工作,在上电后至少0.5秒后才进行数据传输。
接口编写
基本定义
命令
#define S35390_TIME_ADDRESS 0x32
#define S35390_STATUS1_ADDRESS 0x30
#define S35390_STATUS2_ADDRESS 0x31
#define S35390_INT1_REG_ADDRESS 0x34
#define S35390_INT2_REG_ADDRESS 0x35
当前时间结构体和中断闹钟结构体
typedef struct
{
unsigned int year; //year
unsigned char month; //month
unsigned char day; //day
unsigned char hour; //hour
unsigned char minute; //minute
unsigned char second; //second
unsigned char week; //week
}rtc_time;
typedef struct
{
unsigned char minute; //minute
unsigned char hour; //hour
unsigned char week; //week
unsigned char min_en; //minute enable
unsigned char hour_en; //hour enable
unsigned char week_en; //min enable
}rtc_alarm;
接口
RTC_S35390_Init
初始化函数里面只是要配置下主控芯片的IIC外设,设置下上电默认时间。
S35390_ReadRegs
读取多字节数据
bool S35390_ReadRegs(uint8_t addr, uint8_t length,uint8_t *values)
{
status_t ret=STATUS_BUSY;
uint8_t Try = 3;
if(xSemaphore_i2c != NULL )
{
if( xSemaphoreTake( xSemaphore_i2c, ( TickType_t ) 50 ) == pdTRUE )//获取信号量
{
while((Try--)&&(ret != STATUS_SUCCESS))//最多尝试3次
{
lpi2cMasterState.slaveAddress = addr;//读取地址
/* Request data from the bus slave */
ret = I2C_DRV_MasterReceiveDataBlock(INST_LPI2C1,values,length,true,10);
}
xSemaphoreGive( xSemaphore_i2c);//释放信号量
}
}
if(ret != STATUS_SUCCESS)//读出失败
{
I2C_Reset();//重新初始化IIC模块
}
return ret;
}
S35390_WriteRegs
读取多字节数据
static bool S35390_WriteRegs(uint8_t addr, uint8_t length, uint8_t *values)
{
status_t ret=STATUS_BUSY;
uint8_t Try = 3;
uint8_t masterbuffer[32]={0};
memcpy(&masterbuffer[0],values,length);
if( xSemaphore_i2c != NULL )
{
if( xSemaphoreTake( xSemaphore_i2c, ( TickType_t ) 50 ) == pdTRUE )//获取信号量
{
while((Try--)&&(ret != STATUS_SUCCESS))//最多尝试3次
{
lpi2cMasterState.slaveAddress = addr;//写入地址
ret=I2C_DRV_MasterSendDataBlock(INST_LPI2C1, masterbuffer, length, true,100UL);
}
xSemaphoreGive( xSemaphore_i2c);//释放信号量
}
}
if(ret != STATUS_SUCCESS)//写入失败
{
I2C_Reset();//重新初始化IIC模块
}
return ret;
}
S35390_GetYear
别被这个名字骗了,其实就是将获取的年数据BCD码转成十进制数据。
static uint8_t S35390_GetYear(uint8_t regYearData )
{
uint8_t timeYearData = 0x00;
if ( regYearData & 1 )
{
timeYearData += 80;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 40;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 20;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 10;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 8;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 4;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 2;
}
regYearData >>= 1;
if ( regYearData & 1 )
{
timeYearData += 1;
}
if ( timeYearData > 99 )//最大限制
{
timeYearData = 99;
}
else
{
; /* do nothing*/
}
return timeYearData;
}
S35390_GetMonth
获取月数据BCD码转成十进制数据。
static uint8_t S35390_GetMonth(uint8_t regMonthData )
{
uint8_t timeMonthData = 0x00;
regMonthData >>= 3;
if ( regMonthData & 1 )
{
timeMonthData += 10;
}
regMonthData >>= 1;
if ( regMonthData & 1 )
{
timeMonthData += 8;
}
regMonthData >>= 1;
if ( regMonthData & 1 )
{
timeMonthData += 4;
}
regMonthData >>= 1;
if ( regMonthData & 1 )
{
timeMonthData += 2;
}
regMonthData >>= 1;
if ( regMonthData & 1 )
{
timeMonthData += 1;
}
if ( timeMonthData == 0x00 )//最小限制
{
timeMonthData = 0x01;
}
else if ( timeMonthData > 12 )//最大限制
{
timeMonthData = 12;
}
else
{
; /* do nothing*/
}
return timeMonthData;
}
S35390_GetDay
获取日数据BCD码转成十进制数据。
static uint8_t S35390_GetDay(uint8_t regDayData )
{
uint8_t timeDayData = 0x00;
regDayData >>= 2;
if ( regDayData & 1 )
{
timeDayData += 20;
}
regDayData >>= 1;
if ( regDayData & 1 )
{
timeDayData += 10;
}
regDayData >>= 1;
if ( regDayData & 1 )
{
timeDayData += 8;
}
regDayData >>= 1;
if ( regDayData & 1 )
{
timeDayData += 4;
}
regDayData >>= 1;
if ( regDayData & 1 )
{
timeDayData += 2;
}
regDayData >>= 1;
if ( regDayData & 1 )
{
timeDayData += 1;
}
if ( timeDayData == 0 )//最小限制
{
timeDayData = 1;
}
else if ( timeDayData > 31 )//最大限制
{
timeDayData = 31;
}
else
{
; /* do nothing*/
}
return timeDayData;
}
S35390_GetWeek
获取周数据BCD码转成十进制数据。
static uint8_t S35390_GetWeek(uint8_t regWeekData )
{
uint8_t timeWeekData = 0x00;
regWeekData >>= 5;
if ( regWeekData & 1 )
{
timeWeekData += 4;
}
regWeekData >>= 1;
if ( regWeekData & 1 )
{
timeWeekData += 2;
}
regWeekData >>= 1;
if ( regWeekData & 1 )
{
timeWeekData += 1;
}
return timeWeekData;
}
S35390_GetHour
获取时数据BCD码转成十进制数据。
static uint8_t S35390_GetHour(uint8_t regHourData )
{
uint8_t timeHourData = 0x00;
regHourData >>= 2;
if ( regHourData & 1 )
{
timeHourData += 20;
}
regHourData >>= 1;
if ( regHourData & 1 )
{
timeHourData += 10;
}
regHourData >>= 1;
if ( regHourData & 1 )
{
timeHourData += 8;
}
regHourData >>= 1;
if ( regHourData & 1 )
{
timeHourData += 4;
}
regHourData >>= 1;
if ( regHourData & 1 )
{
timeHourData += 2;
}
regHourData >>= 1;
if ( regHourData & 1 )
{
timeHourData += 1;
}
if ( timeHourData > 23 )//最大限制
{
timeHourData = 23;
}
else
{
; /* do nothing*/
}
return timeHourData;
}
S35390_GetMinute
获取分数据BCD码转成十进制数据。
static uint8_t S35390_GetMinute(uint8_t regMinuteData )
{
uint8_t timeMinuteData = 0x00;
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 40;
}
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 20;
}
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 10;
}
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 8;
}
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 4;
}
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 2;
}
regMinuteData >>= 1;
if ( regMinuteData & 1 )
{
timeMinuteData += 1;
}
if ( timeMinuteData > 59 )//最大限制
{
timeMinuteData = 59;
}
else
{
; /* do nothing*/
}
return timeMinuteData;
}
S35390_SetYear
别被它名字骗了,这个接口只是将年数据转换成BCD码,然后返回回去。
static uint8_t S35390_SetYear(uint16_t timeYearData )
{
uint8_t regYearData = 0x00;
if (timeYearData >= 2000)//允许入参格式为2000+的
{
timeYearData -= 2000;
}
else
{
; /* do nothing*/
}
if (timeYearData > 99)//限制入参大小
{
timeYearData = 0;
}
else
{
; /* do nothing*/
}
if ((timeYearData/80) == 1)
{
regYearData |= 0x01;
timeYearData -= 80;
}
if ((timeYearData/40) == 1)
{
regYearData |= 0x02;
timeYearData -= 40;
}
if ((timeYearData/20) == 1)
{
regYearData |= 0x04;
timeYearData -= 20;
}
if ((timeYearData/10) == 1)
{
regYearData |= 0x08;
timeYearData -= 10;
}
if ((timeYearData/8) == 1)
{
regYearData |= 0x10;
timeYearData -= 8;
}
if ((timeYearData/4) == 1)
{
regYearData |= 0x20;
timeYearData -= 4;
}
if ((timeYearData/2) == 1)
{
regYearData |= 0x40;
timeYearData -= 2;
}
if (timeYearData == 1)
{
regYearData |= 0x80;
}
return regYearData;
}
S35390_SetMonth
将月数据转换成BCD码,然后返回回去。
static uint8_t S35390_SetMonth(uint8_t timeMonthData )
{
uint8_t regMonthData = 0x00;
if (timeMonthData == 0x00)//限制最小
{
timeMonthData = 0x01;
}
else if (timeMonthData > 12)//限制最大
{
timeMonthData = 1;
}
else
{
; /* do nothing*/
}
if ((timeMonthData/10) == 1)
{
regMonthData |= 0x08;
timeMonthData -= 10;
}
if ((timeMonthData/8) == 1)
{
regMonthData |= 0x10;
timeMonthData -= 8;
}
if ((timeMonthData/4) == 1)
{
regMonthData |= 0x20;
timeMonthData -= 4;
}
if ((timeMonthData/2) == 1)
{
regMonthData |= 0x40;
timeMonthData -= 2;
}
if (timeMonthData == 1)
{
regMonthData |= 0x80;
}
return regMonthData;
}
S35390_SetDay
将日数据转换成BCD码,然后返回回去。
static uint8_t S35390_SetDay(uint8_t timeDayData )
{
uint8_t regDayData = 0x00;
if (timeDayData == 0x00)//限制最小
{
timeDayData = 0x01;
}
else if (timeDayData > 31)//限制最大
{
timeDayData = 1;
}
else
{
; /* do nothing*/
}
if ((timeDayData/20) == 1)
{
regDayData |= 0x04;
timeDayData -= 20;
}
if ((timeDayData/10) == 1)
{
regDayData |= 0x08;
timeDayData -= 10;
}
if ((timeDayData/8) == 1)
{
regDayData |= 0x10;
timeDayData -= 8;
}
if ((timeDayData/4) == 1)
{
regDayData |= 0x20;
timeDayData -= 4;
}
if ((timeDayData/2) == 1)
{
regDayData |= 0x40;
timeDayData -= 2;
}
if (timeDayData == 1)
{
regDayData |= 0x80;
}
return regDayData;
}
S35390_SetWeek
将周数据转换成BCD码,然后返回回去。
static uint8_t S35390_SetWeek(uint8_t timeWeekData )
{
uint8_t regWeekData = 0x00;
if (timeWeekData > 6)//限制最大
{
timeWeekData = 0;
}
else
{
; /* do nothing*/
}
if ((timeWeekData/4) == 1)
{
regWeekData |= 0x20;
timeWeekData -= 4;
}
if ((timeWeekData/2) == 1)
{
regWeekData |= 0x40;
timeWeekData -= 2;
}
if (timeWeekData == 1)
{
regWeekData |= 0x80;
}
return regWeekData;
}
S35390_SetHour
将时数据转换成BCD码,然后返回回去。
static uint8_t S35390_SetHour(uint8_t timeHourData)
{
uint8_t regHourData = 0x00;
if (timeHourData > 23)//限制最大
{
timeHourData = 0;
}
else
{
; /* do nothing*/
}
if ((timeHourData/20) == 1)
{
regHourData |= 0x04;
timeHourData -= 20;
}
if ((timeHourData/10) == 1)
{
regHourData |= 0x08;
timeHourData -= 10;
}
if ((timeHourData/8) == 1)
{
regHourData |= 0x10;
timeHourData -= 8;
}
if ((timeHourData/4) == 1)
{
regHourData |= 0x20;
timeHourData -= 4;
}
if ((timeHourData/2) == 1)
{
regHourData |= 0x40;
timeHourData -= 2;
}
if (timeHourData == 1)
{
regHourData |= 0x80;
}
return regHourData;
}
S35390_SetMinute
将分数据转换成BCD码,然后返回回去。
static uint8_t S35390_SetMinute(uint8_t timeMinuteData )
{
uint8_t regMinuteData = 0x00;
if (timeMinuteData > 59)//限制最大
{
timeMinuteData = 0;
}
else
{
; /* do nothing*/
}
if ((timeMinuteData/40) == 1)
{
regMinuteData |= 0x02;
timeMinuteData -= 40;
}
if ((timeMinuteData/20) == 1)
{
regMinuteData |= 0x04;
timeMinuteData -= 20;
}
if ((timeMinuteData/10) == 1)
{
regMinuteData |= 0x08;
timeMinuteData -= 10;
}
if ((timeMinuteData/8) == 1)
{
regMinuteData |= 0x10;
timeMinuteData -= 8;
}
if ((timeMinuteData/4) == 1)
{
regMinuteData |= 0x20;
timeMinuteData -= 4;
}
if ((timeMinuteData/2) == 1)
{
regMinuteData |= 0x40;
timeMinuteData -= 2;
}
if (timeMinuteData == 1)
{
regMinuteData |= 0x80;
}
return regMinuteData;
}
S35390_GetStatusReg1
获取状态寄存器1、状态寄存器2也是如此。
uint8_t S35390_GetStatusReg1(uint8_t *statusReg)
{
return S35390_ReadRegs(S35390_STATUS1_ADDRESS,1,statusReg);
}
S35390_SetStatusReg1
设置状态寄存器1、状态寄存器2也是如此。
uint8_t S35390_SetStatusReg1(uint8_t *statusReg)
{
return S35390_WriteRegs(S35390_STATUS1_ADDRESS,1, statusReg);
}
S35390_SetAlarm1
设置中断闹钟1、中断闹钟2也是如此,注意需要先在状态寄存器里面设置为闹钟模式。
int S35390_SetAlarm1(rtc_alarm *alarmTime)
{
status_t ret=STATUS_BUSY;
uint8_t reg_data[3] = {0};
uint8_t flg_am_pm = 0; //0:am,1:pm
reg_data[0] = S35390_SetWeek(alarmTime->week) | alarmTime->week_en;
if(alarmTime->hour >= 12)
{
flg_am_pm = 1;
}
reg_data[1] = S35390_SetHour(alarmTime->hour) | alarmTime->hour_en | (flg_am_pm << 1);
reg_data[2] = S35390_SetMinute(alarmTime->minute) | alarmTime->min_en;
ret = S35390_WriteRegs(S35390_INT1_REG_ADDRESS,3,reg_data);
return ret;
}
S35390_GetAlarm1
获取闹钟1设置
int S35390_GetAlarm1(rtc_alarm *alarmTime)
{
status_t ret=STATUS_BUSY;
uint8_t reg_data[3] = {0};
ret = S35390_ReadRegs(S35390_INT1_REG_ADDRESS,3, reg_data);
if(ret != STATUS_SUCCESS)
{
LOG_ERR( "Get alarm set time error!\r\n");
}
else
{
alarmTime->week = S35390_GetWeek(reg_data[0]);
alarmTime->week_en = reg_data[0] & 0x01;
alarmTime->hour = S35390_GetHour(reg_data[1]);
alarmTime->hour_en = reg_data[1] & 0x01;
alarmTime->minute = S35390_GetMinute(reg_data[2]);
alarmTime->min_en = reg_data[2] & 0x01;
}
return ret;
}
S35390_EnableAlarm1
使能闹钟模式,其实就是将状态寄存器1改成闹钟模式。
int S35390_EnableAlarm1(void)
{
status_t ret=STATUS_BUSY;
uint8_t reg_data = 0;
ret = S35390_ReadRegs(S35390_STATUS2_ADDRESS,1, ®_data);
if(STATUS_SUCCESS == ret)
{
//INT1AE = 1;INT1ME = 0;INT1FE = 0;
reg_data &= ~(0x01 << 4);
reg_data |= (0x01 << 5);
reg_data &= ~(0x03 << 6);
ret = S35390_WriteRegs(S35390_STATUS2_ADDRESS,1, ®_data);
}
return ret;
}
S35390_DisableAlarm
失能闹钟模式,其实就是改变状态寄存器1。
int S35390_DisableAlarm(void)
{
status_t ret=STATUS_BUSY;
uint8_t reg_data = 0;
ret = S35390_ReadRegs(S35390_STATUS1_ADDRESS,1, ®_data);
if(STATUS_SUCCESS == ret)
{
//INT1AE = 0;INT1ME = 0;INT1FE = 0;
reg_data &= ~(0x01 << 4);
reg_data &= ~(0x01 << 5);
reg_data &= ~(0x03 << 6);
ret = S35390_WriteRegs(S35390_STATUS1_ADDRESS,1, ®_data);
}
return ret;
}
S35390_GetTime
获取当前时间
#define S35390_BTC_SEC 0x06
#define S35390_BTC_MIN 0x05
#define S35390_BTC_HOUR 0x04
#define S35390_BTC_WEEK 0x03
#define S35390_BTC_DAY 0x02
#define S35390_BTC_MONTH 0x01
#define S35390_BTC_YEAR 0x00
uint8_t S35390_GetTime(rtc_time *time)
{
uint8_t reg_date[7]={0};
uint8_t err = 0;
err = S35390_ReadRegs(S35390_TIME_ADDRESS,7,reg_date);//获取当前时间到reg_date
time->second = S35390_GetMinute(reg_date[S35390_BTC_SEC]); //转换成十进制数据
time->minute = S35390_GetMinute(reg_date[S35390_BTC_MIN]);
time->hour = S35390_GetHour(reg_date[S35390_BTC_HOUR]);
time->week = S35390_GetWeek(reg_date[S35390_BTC_WEEK]);
time->day = S35390_GetDay(reg_date[S35390_BTC_DAY]);
time->month = S35390_GetMonth(reg_date[S35390_BTC_MONTH]);
time->year = S35390_GetYear(reg_date[S35390_BTC_YEAR])+2000;
LOG_INFO("%-30s%s%d,%d,%d,%d,%d,%d,%d\r\n","GetRTC","=",time->year,time->month,time->day,time->hour,time->minute,time->second,time->week);
return err;
}
S35390_EnableAlarm
其实就是将状态寄存器设置为闹钟模式
int S35390_EnableAlarm(void)
{
status_t ret=STATUS_BUSY;
uint8_t reg_data = 0;
ret = S35390_ReadRegs(S35390_STATUS2_ADDRESS,1, ®_data);
if(STATUS_SUCCESS == ret)
{
//INT1AE = 1;INT1ME = 0;INT1FE = 0;
reg_data &= ~(0x01 << 4);
reg_data |= (0x01 << 5);
reg_data &= ~(0x03 << 6);
ret = S35390_WriteRegs(S35390_STATUS2_ADDRESS,1, ®_data);
}
return ret;
}
S35390_SetTime
设置当前时间
uint8_t S35390_SetTime(rtc_time *time)
{
uint8_t reg_date[7] = {0};
uint8_t ret = 0xff;
uint8_t week_tmp = 0;
reg_date[S35390_BTC_SEC] = S35390_SetMinute(time->second); //转换成BCD码
reg_date[S35390_BTC_MIN] = S35390_SetMinute(time->minute);
reg_date[S35390_BTC_HOUR] = S35390_SetHour(time->hour);
week_tmp = CalculateWeekForKimLarsenCalculationFormula(time->year,time->month,time->day);//计算出周数据
if(time->week != week_tmp)
{
LOG_ERR("week set error!\r\n");
}
reg_date[S35390_BTC_WEEK] = S35390_SetWeek(week_tmp);
reg_date[S35390_BTC_DAY] = S35390_SetDay(time->day);
reg_date[S35390_BTC_MONTH] = S35390_SetMonth(time->month);
reg_date[S35390_BTC_YEAR] = S35390_SetYear(time->year);
ret = S35390_WriteRegs(S35390_TIME_ADDRESS,7, reg_date);
return ret;
}
static uint8_t CalculateWeekForKimLarsenCalculationFormula(uint16_t year, uint8_t month, uint8_t date)
{
uint8_t week;
if ((month == 1) || (month == 2)) {
month += 12;
year--;
}
week = (date + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400 + 1) % 7;
return week;
}