IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 利用单片机定时器做Unix时间戳保持及时间转换 -> 正文阅读

[嵌入式]利用单片机定时器做Unix时间戳保持及时间转换

引言

单片机平台上进行网络应用开发时,总会遇到Unix时间戳与本地时间转换的问题。对于使用低端MCU+WIFI模块的硬件方案,策略是使用MCU的一个定时器,配置定时器1S产生一次中断,中断处理函数中对uint32_t的变量做累加,用于表示本机时间。当MCU通过WIFI模块获取到网络时间后,计算网络时间与本机时间的差值,并保存此差值。 此后MCU端需要使用Unix时间戳是,就无需通过WIFI模块获取网络事件,而是直接通过本地时间和保存的时间差值做累加,即可得出相对准确的Unix时间戳。此种方式维护的时间戳肯定存在误差,仅用于对时间精度要求不高且硬件方案中无RTC模块的方案

以下具体实现过程。如有错误,欢迎提出。

流程图

Created with Rapha?l 2.3.0 开始 启动定时器 并开启中断 获取网络时间 获取成功? 计算本机时间偏差 根据本机时间与时间偏差输出unix时间戳 结束 yes no

1、定时器相关代码片段

//本机时间,定时器自动累加,不能修改,初始值为0
uint32_t globalSystemTime=0;  
uint32_t timeAbsDifference = 0;  
/* 定时器初始化  1s产生一次中断*/
void TimerInit(void)
{
	// 1.配置定时器时钟源
	// 2.配置定时器分频因子
	// 3.配置定时器比较寄存器数值
	// 4.使能定时器中断
	// 5.开启定时器
}

/**
 * @Name       : void TMR1_IRQHandler(void)
 * @Description: 定时器中断处理函数,确保每s系统时钟自增1 
 * @In         : NULL
 * @Out        : NULL
 * @Author     : Denis
 */
void Timer_IRQHandler(void)
{
    if(/*判断定时器中断标志为定时器中断*/)
    {
        // 1.清除中断标志位
        globalSystemTime++;  // 2.累加本机时间
    }
}


/**
 * @Name       : void UpdateUnixTime(uint32_t unix) 
 * @Description: 根据unix时间戳和本机时间计算出时间差值
 * @In         : 接收到的unix时间戳
 * @Out        : NULL
 * @Author     : Denis
 */
void UpdateUnixTime(uint32_t unix)
{
    timeAbsDifference = unix - globalSystemTime; //计算真实时间与系统时间的时间差值
}

/**
 * @Name       : uint32_t GetUnixTime(void)
 * @Description: 获取本地生成的Unix时间戳 
 * @In         : NULL
 * @Out        : 本地生成的Unix时间戳
 * @Author     : Denis
 */
uint32_t GetUnixTime(void)
{
	uint32_t u32SysTime = 0;				             //unix时间戳
	u32SysTime = globalSystemTime + timeAbsDifference;	 //计算本地记录的unix时间戳
	return u32SysTime;						             //返回unix时间戳
}

至此,时间戳本地更新及获取逻辑已经展示完成,下面将展示时间戳转换相关的代码。

2、时间戳转换


uint32_t SEC_PER_YR[2] = {31536000, 31622400}; //闰年和非闰年的秒数
uint32_t SEC_PER_MT[2][12] = {
	{2678400, 2419200, 2678400, 2592000, 2678400, 2592000,
	 2678400, 2678400, 2592000, 2678400, 2592000, 2678400},
	{2678400, 2505600, 2678400, 2592000, 2678400, 2592000,
	 2678400, 2678400, 2592000, 2678400, 2592000, 2678400},
};


/**
 * @Name       : static int is_leap(int yr)
 * @Description: 判断是否为闰年  
 * 				"非整百年份:能被4整除的是闰年。"
 * 				"整百年份:能被400整除的是闰年。"
 * @In         : 待机算的年份
 * @Out        : 1:是闰年   0:非闰年
 * @Author     : Denis
 */
static int is_leap(int yr)
{
	if (NULL == (yr % 100))
		return (yr % 400 == 0) ? 1 : 0;
	else
		return (yr % 4 == 0) ? 1 : 0;
}


/**
 * @Name       : static unsigned char day_of_week_get(unsigned char month, unsigned char day,
									 unsigned short year) 
 * @Description: 根据输入的年月日计算当天为星期几
 * @In         : 年、月、日
 * @Out        : 星期几
 * @Author     : Denis
 */
static unsigned char day_of_week_get(unsigned char month, unsigned char day,
									 unsigned short year)
{
	/* Month should be a number 0 to 11, Day should be a number 1 to 31 */

	static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
	year -= (uint8_t)(month < 3);
	return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7;
}

/**
 * @Name       : void transformTime(struct devtm *result)
 * @Description: 将unix时间戳转换为
 * @In         : 存储时间信息的结构体指针
 * @Out        : NULL
 * @Author     : Denis
 */
void transformTime(struct devtm *result)
{
	int leapyr = 0;
	/*默认将Unix时间戳转换为东八区时间信息,8×60×60=28800*/
	uint32_t ltime = GetUnixTime() + 28800;

	memset(result, 0, sizeof(struct devtm));
	result->tm_year = EPOCH_YR;

	while (1)
	{
		if (ltime < SEC_PER_YR[is_leap(result->tm_year)])
		{
			break;
		}
		ltime -= SEC_PER_YR[is_leap(result->tm_year)];
		++(result->tm_year);
	}

	leapyr = is_leap(result->tm_year);

	while (1)
	{
		if (ltime < SEC_PER_MT[leapyr][result->tm_mon])
			break;
		ltime -= SEC_PER_MT[leapyr][result->tm_mon];
		++(result->tm_mon);
	}

	result->tm_mday = ltime / SEC_PER_DY;
	++(result->tm_mday);
	ltime = ltime % SEC_PER_DY;

	result->tm_hour = ltime / SEC_PER_HR;
	ltime = ltime % SEC_PER_HR;

	result->tm_min = ltime / 60;
	result->tm_sec = ltime % 60;

	result->tm_wday =
		day_of_week_get(result->tm_mon + 1, result->tm_mday,
						result->tm_year);

	/*
	 * The number of years since YEAR0"
	 */
	result->tm_year -= YEAR0;
}


/**
 * @Name       : int getLocalTimeStr(char *pbuf)
 * @Description: 获取本地时间的字符串
 * @In         : 存储时间字符串的数组指针,数组最小为32个Byte
 * @Out        : NULL
 * @Author     : Denis
 */
int getLocalTimeStr(char *pbuf)
{
	struct devtm timeInfo;
	if (pbuf == 0)
	{
		return 0;
	}
	transformTime(&timeInfo);
	snprintf(pbuf, 32, "<%4d-%02d-%02d %02d:%02d:%02d>", timeInfo.tm_year + YEAR0,
			 timeInfo.tm_mon + 1,
			 timeInfo.tm_mday,
			 timeInfo.tm_hour,
			 timeInfo.tm_min,
			 timeInfo.tm_sec);
	return 1;
}
//对应的.h
#define YEAR0 (1900)	   /* The first year */
#define EPOCH_YR (1970)	   /* EPOCH = Jan 1 1970 00:00:00 */
#define SEC_PER_DY (86400) //一天的秒数
#define SEC_PER_HR (3600)  //一小时的秒数

typedef struct devtm
{
    uint16_t tm_year;
    uint8_t tm_mon;
    uint8_t tm_mday;
    uint8_t tm_hour;
    uint8_t tm_min;
    uint8_t tm_sec;
    uint8_t tm_wday;
}tm_t;

void transformTime(struct devtm *result);
int getLocalTimeStr(char *pbuf);

至此,时间戳转换相关的代码片段展示完毕。

3、字符串转换函数

考虑到从WIFI模块获取到的时间戳信息可能是以字符串形式发送至MCU,现将字符串转换函数放在下方。

/*
 * @Name       : uint32_t UT_GetInt(char **p, int DefaultValue)
 * @Description: 将一个数值字符串转换为数字[0x32],[0x35],[0x37],[0x39],[0x32]----->25792
 * @In         : 待转换字符串的地址指针的指针 转换失败后的默认值
 * @Out        : 转换后的结果
 * @Author     : Denis
**/
uint32_t UT_GetInt(char **p, int DefaultValue)
{
    uint32_t Value = 0;
    bool UseDefault;

    UseDefault = true;
    UT_SkipBlanks(p);

    while (((**p) <= '9' && (**p) >= '0'))
    {
        Value = Value * 10 + (**p) - '0';
        UseDefault = false;
        ++(*p);
    }
    for (; **p == ' '; ++(*p))
        ; // skip spaces

    if (UseDefault)
    {
        return DefaultValue;
    }
    else
    {
        return Value;
    }
}

void StrTimeToValue(char *timeStr)
{
	uint32_t unixTimeValue = 0;
	char *str = NULL;
    char **p = &str;
    str = timeStr;
    /*将Unix字符串信息转换为Unix数值*/
    unixTimeValue = UT_GetInt(p,0);
    /*使用最新的时间戳信息更新时间差值*/
    UpdateUnixTime(unixTime);
}

好了,终于将想展示的都写完了,好累~~~~

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-24 15:43:22  更:2021-08-24 15:44:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 20:58:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码