RTC时钟几乎是现在单片机的标配,特别在做物联网相关的项目的时候,这个功能几乎是必需品,现总结几个常用的函数;
1、RTC与BDC码的转换
/********************************************************************
*@函数名:bsp_BCD_Check
*@功 能:检查BCD码格式
*@形 参:bcd 需要检验的 BCD 码
*@返回值:-1 不合格 0 属于BCD码
*@备 注:NULL
********************************************************************/
int bsp_BCD_Check(uint8_t bcd)
{
uint8_t byte;
byte = bcd&0X0F;
if(byte > 9) return -1;
byte = (bcd>>4)&0X0F;
if(byte > 9) return -1;
return 0;
}
/********************************************************************
*@函数名:bsp_BCDToHex
*@功 能:将BCD转换成HEX码
*@形 参:bcd 需要转换的 BCD 码
*@返回值:转换后的十六进制码
*@备 注:NULL
********************************************************************/
uint8_t bsp_BCDToHex(uint8_t bcd)
{
uint8_t byte;
byte = (bcd>>4)*10+(bcd&0X0F);
return byte;
}
/********************************************************************
*@函数名:bsp_BCDToHex
*@功 能:将HEX换成BCD码
*@形 参:hex 需要转换的 HEX 码
*@返回值:转换后的BCD码
*@备 注:NULL
********************************************************************/
uint8_t bsp_HexToBCD(uint8_t hex)
{
uint8_t byte;
byte = (hex/10)%10;
byte = (byte<<4)+hex%10;
return byte;
}
2、RTC时间戳的转换
/* 定义时间结构体 */
struct rtc_time {
int tm_year;
int tm_mon;
int tm_mday;
int tm_hour;
int tm_min;
int tm_sec;
int tm_wday;
};
/* 定义相关的宏 */
#define FEBRUARY 2
#define STARTOFTIME 1970
#define SECDAY 86400L /* 一天有多少s */
#define SECYR (SECDAY * 365)
#define leapyear(year) ((year) % 4 == 0)
#define days_in_year(a) (leapyear(a) ? 366 : 365)
#define days_in_month(a) (month_days[(a) - 1])
static int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/*
* This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
*/
/*计算公历*/
void GregorianDay(struct rtc_time * tm)
{
int leapsToDate;
int lastYear;
int day;
int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
lastYear = tm->tm_year-1;
/*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/
leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
/*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/
if((tm->tm_year%4==0) &&
((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
(tm->tm_mon>2)) {
/*
* We are past Feb. 29 in a leap year
*/
day=1;
} else {
day=0;
}
day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; /*计算从公元元年元旦到计数日期一共有多少天*/
tm->tm_wday = day%7;
if(tm->tm_wday == 0)
{
tm->tm_wday = 7;
}
}
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines were long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*
*/
uint32_t mktimev(struct rtc_time *tm)
{
if (0 >= (int) (tm->tm_mon -= 2)) { /* 1..12 -> 11,12,1..10 */
tm->tm_mon += 12; /* Puts Feb last since it has leap day */
tm->tm_year -= 1;
}
return (((
(uint32_t) (tm->tm_year/4 - tm->tm_year/100 + tm->tm_year/400 + 367*tm->tm_mon/12 + tm->tm_mday) +
tm->tm_year*365 - 719499
)*24 + tm->tm_hour /* now have hours */
)*60 + tm->tm_min /* now have minutes */
)*60 + tm->tm_sec; /* finally seconds */
}
void to_tm(uint32_t tim, struct rtc_time * tm)
{
register uint32_t i;
register long hms, day;
day = tim / SECDAY; /* 有多少天 */
hms = tim % SECDAY; /* 今天的时间,单位s */
/* Hours, minutes, seconds are easy */
tm->tm_hour = hms / 3600;
tm->tm_min = (hms % 3600) / 60;
tm->tm_sec = (hms % 3600) % 60;
/* Number of years in days */ /*算出当前年份,起始的计数年份为1970年*/
for (i = STARTOFTIME; day >= days_in_year(i); i++) {
day -= days_in_year(i);
}
tm->tm_year = i;
/* Number of months in days left */ /*计算当前的月份*/
if (leapyear(tm->tm_year)) {
days_in_month(FEBRUARY) = 29;
}
for (i = 1; day >= days_in_month(i); i++) {
day -= days_in_month(i);
}
days_in_month(FEBRUARY) = 28;
tm->tm_mon = i;
/* Days are what is left over (+1) from all that. *//*计算当前日期*/
tm->tm_mday = day + 1;
/*
* Determine the day of week
*/
GregorianDay(tm);
}
3、将格林威治时间转北京时间
/**********************************************************
* 函数名: bsp_gen_UTCToBeijing
* 功 能: 将UTC时间转化成北京时间
* 输 入: 时间缓冲数组,十六进制数
* 返 回: 无
**********************************************************/
void bsp_gen_UTCToBeijing(uint8_t *utc_time)
{
int year=0;
uint8_t month=0,day=0,hour=0;
uint8_t lastday = 0; // 月的最后一天日期
year = utc_time[0]+2000;
month = utc_time[1];
day = utc_time[2];
hour = utc_time[3]+8;//UTC+8转换为北京时间
if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12)
{
lastday = 31;
}
else if(month == 4 || month == 6 || month == 9 || month == 11)
{
lastday = 30;
}
else
{
if( (year%400 == 0)||(year%4 == 0 && year%100 != 0) )//闰年的2月为29天,平年为28天
lastday = 29;
else
lastday = 28;
}
if(hour >= 24)//当算出的时大于或等于24:00时,应减去24:00,日期加一天
{
hour -= 24;
day += 1;
if(day > lastday)//当算出的日期大于该月最后一天时,应减去该月最后一天的日期,月份加上一个月
{
day -= lastday;
month += 1;
if(month > 12)//当算出的月份大于12,应减去12,年份加上1年
{
month -= 12;
year += 1;
}
}
}
utc_time[0] = (uint8_t)(year-2000);
utc_time[1] = month;
utc_time[2] = day;
utc_time[3] = hour;
}
4、通过NB或者4G校时的函数
如果从NB端接收的时间返回的字符串为:
+CCLK: "2023/05/14,10:31:22+32"
使用以下函数进行处理
int ret, time[6];
ret = sscanf((char *)rxdata, "%*s \"%d/%d/%d,%d:%d:%d", &time[0], &time[1], &time[2],
&time[3], &time[4], &time[5]);
if(ret == 6)
{
if( (time[0]>2020)&&(time[1]<13)&&(time[2]<31)\
&&(time[3]<24)&&(time[4]<60)&&((time[5]<60)))
{
buf[0] = time[0]-2000;
buf[1] = time[1];
buf[2] = time[2];
buf[3] = time[3];
buf[4] = time[4];
buf[5] = time[5];
return 1;
}
}