单片机RTC应用函数

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;
		}
							
	}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值