stm32f103c8t6学习笔记(学习B站up江科大自化协)-UNIX时间戳

UNIX时间戳 

        ·UNIX时间戳最早是在UNIX系统使用的,所以叫做UNIX时间戳,之后很多由UNIX演变而来的系统也继承了UNIX时间戳的规定,目前linux,windows,安卓这些操作系统的底层计时系统都是用UNIX时间戳

        ·时间戳这个计时系统和我们常用的年月日时分秒的计时系统具有较大的差别。

        年月日时分秒计时系统是每60秒进位1次,记为一分钟,每60分钟进位1次记为1小时,之后便是日月年        

        时间戳计时系统定义1970年1月1日0点整为0秒,之后只用最基本的秒计时,永不进位,无论数有多大都不进位。        

        这样有很多好处:

        第一,简化了硬件电路,在设计RTC硬件电路的时候直接用一个很大的寄存器即可,无需考虑其他的寄存器

        第二,在进行一些时间间隔的计算时非常方便,秒数相见换成时间很方便

        第三,存储方便,存储秒数一个很大的变量即可,存储年月日时分秒则需要很多变量

        坏处:比较占用软件资源,每次进行转换的时候软件需要进行复杂的计算

        ·时间戳存储在一个秒计数器中,计算机为了存储这一个永不进位的秒数,定义的变量类型通常会较大。变量类型在不同的系统中是不一样的。在早起的UNIX系统中,这个秒数大多是用32位有符号的整形变量进行存储,32位有符号数所能表示的最大数是2^{32} / 2 - 1,这个数是21亿多,有溢出的风险,根据计算,32位的有符号时间戳会在2038年的1月19号溢出,到时候采用32位有符号数存储时间戳的设备,计时系统会因数据溢出而出错,会导致很多不健全的计算机程序崩溃。

        随着操作系统和设备的更新换代,目前基本使用64位的数据存储时间戳。

        stm32核心的计时部分是一个32位的可编程计数器,说明这款stm32的时间戳是32位的数据类型,但是是无符号的32位,最大是2^{32} - 1,大概到2106年才会溢出

        ·地球上不一样的经度时间是不一样的,穿过英国伦敦的经线是本初子午线,这个位置的时间是一个时间标准,时间戳的1970年1月1日0时0分0秒也是指伦敦时间的0时0分0秒,其他地方分为24个时区,每偏差一个时区时间需要加或减一个小时

        对于不同地区是共用同一个时间戳的秒计数器,在伦敦是0,北京也是0,根据不同时区添加小时的偏移即可,伦敦是0点的时候,北京处于东八区就是8点

        图中的箭头代表的是一个时间轴,在时间轴上定义一个起点,时间戳从起点开始计时,起点是人为规定的,对于起点之前的时间,时间戳是无法表示的

UTC/GMT

        ·格林尼治是一个地名,位于英国伦敦,也就是伦敦的标准时间。在格林尼治有个天文台,可以观察天上太阳和星星以确定太阳的自转和公转,将地球自转一周的时间间隔分为24小时。

        GMT是以前的计时时间,因为存在一个棘手的问题,因为地球自转一周的时间是不固定的,由于潮汐力等因素导致地球处于越转越慢的状态,根据这个时间基准定义新的时间,那么这个时间基准是不断变化的,地球越转越慢,那么定义的1秒的时间也就越来越长

        ·为了时间的定义更标准,提出了新的计时系统,叫做UTC协调世界时。在原子钟计时系统的基础上加入了润秒的机制,来消除计时一天和自转一周的误差。所谓润秒就是计时标准是恒定不变的,但是地球越转越慢,当误差超过0.9秒的时候,计时系统就多走一秒等一下地球的自转。

        润秒的机制可能会造成一些程序的bug,可能会出现一分钟61秒的情况。平时不追求严谨的话使用GMT或UTC都可以

时间戳转换

·time_t time(time_t*):

        作用是获取系统时钟,返回值是time_t,表示当前系统时钟的秒计数期值,参数time_t*,是一个输出参数,输出的内容和返回值是一样的。这个函数可以通过返回值获取系统时钟,也可以通过输出参数获取。

        这个函数在电脑中可以直接读取电脑的时间,在STM32中无法使用,因为STM32是一个离线的裸机系统,无法获取目前的时间状态,

代码(使用DEV C++进行编写)
#include <stdio.h>
#include <time.h>

time_t time_cnt;//秒计数器	time_t是一个typedef重命名的类型  如果不是特别声明要用32位的秒计数器类型 
//那么默认状况下就是__time64_t  然后__time_t实际上是int64 是一个有符号的整型数据 无需担心溢出问题 可以用于存储时间戳中一直自增的秒数 
struct tm time_date;//tm是结构体类型名,time_date是结构体变量名 
char *time_str; 

int main()
{
	time_cnt = time(NULL);//参数不需要的话给null即可 
	printf("%d\n",time_cnt);
	return 0;
}

编译烧录后在中断可以看到这样的输出

         将终端的数值复制到网络上的时间戳在线转换工具中,进行转换,验证了数据是准确的

     将代码

time_cnt = time(NULL);

进行注释,替换成

time(&time_cnt);

也可以进行正常的显示当前的时间值

·struct tm* gmtime(const time_t):

        将秒计数器的值转换为格林尼治,也就是伦敦时间。

        参数是const time_t*,秒计数器指针类型,是输入参数,返回值struct tm*是日期时间结构体指针类型

        gmtime()的参数是time_t*,需要将地址传进去,返回值是struct tm*

        如果直接time_date = gmtime(&time_cnt)是指针跨级赋值,等号左边是一个变量,右边是一个地址,解决方法有两个:
        第一:time_date = *gmtime(&time_cnt),在函数的右边返回值加上*取内容,等号左右两边都是变量,结构体变量互相赋值是ok的 
        第二:定义变量的时候struct tm *time_date定义为指针类型,那么time_date = gmtime(&time_cnt)的等号左右都是指针,结构体指针之间相互赋值

代码(使用DEV C++进行编写)
#include <stdio.h>
#include <time.h>

time_t time_cnt;//秒计数器	time_t是一个typedef重命名的类型  如果不是特别声明要用32位的秒计数器类型 
//那么默认状况下就是__time64_t  然后__time_t实际上是int64 是一个有符号的整型数据 无需担心溢出问题 可以用于存储时间戳中一直自增的秒数 
struct tm time_date;//tm是结构体类型名,time_date是结构体变量名 
char *time_str; 

int main()
{
	//time_cnt = time(NULL);//参数不需要的话给null即可 
	//time(&time_cnt);
	time_cnt = 1713960225;
	printf("%d\n",time_cnt);
	
	time_date = *gmtime(&time_cnt);
	printf("%d\n",time_date.tm_year + 1900);//年份偏移	
	printf("%d\n",time_date.tm_mon+1);//月份偏移
	printf("%d\n",time_date.tm_mday);
	printf("%d\n",time_date.tm_hour+8);//北京时间东八区
	printf("%d\n",time_date.tm_min);
	printf("%d\n",time_date.tm_sec);
	printf("%d\n",time_date.tm_wday);

	return 0;
}

·struct tm* localtime(const time_t*):       

        转换当地时间,函数的使用方法和gmtime是一样的,作用也是一样的,不过localtime会根据时区自动添加小时的偏移,程序只需将gmtime的名字替换成localtime即可,这个函数会自动根据当前电脑的设置判断所处的时区,将时间添加时区偏移之后输出

·time_t mktime(struct tm*):

        将日期时间转换为秒计数器,mktime传入的日期时间需要是当地的,maketime就是前边两个转换的逆过程。

代码(使用DEV C++进行编写)

        编译烧录后,第一行是给定的时间秒数,中间是转换成当地的时间,最后一行是根据日期时间转换回的秒数,最终的秒数和最初的秒数是一样的。

        将localtime改成gmtime,最终的秒数就和最初的秒数不一样,说明mktime不是根据伦敦时间进行的  

        mktime的参数前边是没有const,这个参数既是输入参数也是输出参数。内部的工作过程是这样的:日期时间结构体,里面有年月日时分秒星期等数据,但是仅通过年月日时分秒就足以计算出秒计数器,里边的星期参数实际上是不作为输入参数的;但是相反的是,函数在算出秒数的同时还会顺便计算当前年月日是星期几,回填到结构体中的星期之中。使用这个函数可以很方便的计算对应的是星期几

#include <stdio.h>
#include <time.h>

time_t time_cnt;//秒计数器	time_t是一个typedef重命名的类型  如果不是特别声明要用32位的秒计数器类型 
//那么默认状况下就是__time64_t  然后__time_t实际上是int64 是一个有符号的整型数据 无需担心溢出问题 可以用于存储时间戳中一直自增的秒数 
struct tm time_date;//tm是结构体类型名,time_date是结构体变量名 
char *time_str; 

int main()
{
	//time_cnt = time(NULL);//参数不需要的话给null即可 
	//time(&time_cnt);
	time_cnt = 1713960225;
	printf("%d\n",time_cnt);
	
	time_date = *localtime(&time_cnt);
	printf("%d\n",time_date.tm_year + 1900);	
	printf("%d\n",time_date.tm_mon+1);
	printf("%d\n",time_date.tm_mday);
	printf("%d\n",time_date.tm_hour);
	printf("%d\n",time_date.tm_min);
	printf("%d\n",time_date.tm_sec);
	printf("%d\n",time_date.tm_wday);

	time_cnt = mktime(&time_date); //参数是日期时间结构体指针 可以传入日期时间结构体的地址 返回秒计数器的值 
	printf("%d\n",time_cnt);
	return 0;
}

char* ctime(const time_t*):

        将秒计时器转换成字符串表示,使用默认的格式,相当于转换成char* 格式的字符串

char* asctime(const struct tm*):

        将日期转换为字符串,使用默认格式。相当于转换成char* 格式的字符串

代码:两个函数卸载一块进行展示
#include <stdio.h>
#include <time.h>

time_t time_cnt;//秒计数器	time_t是一个typedef重命名的类型  如果不是特别声明要用32位的秒计数器类型 
//那么默认状况下就是__time64_t  然后__time_t实际上是int64 是一个有符号的整型数据 无需担心溢出问题 可以用于存储时间戳中一直自增的秒数 
struct tm time_date;//tm是结构体类型名,time_date是结构体变量名 
char *time_str; 

int main()
{
	//time_cnt = time(NULL);//参数不需要的话给null即可 
	//time(&time_cnt);
	time_cnt = 1713960225;
	printf("%d\n",time_cnt);
	
	time_date = *localtime(&time_cnt);
	printf("%d\n",time_date.tm_year + 1900);	
	printf("%d\n",time_date.tm_mon+1);
	printf("%d\n",time_date.tm_mday);
	printf("%d\n",time_date.tm_hour+8);
	printf("%d\n",time_date.tm_min);
	printf("%d\n",time_date.tm_sec);
	printf("%d\n",time_date.tm_wday);

	time_cnt = mktime(&time_date); //参数是日期时间结构体指针 可以传入日期时间结构体的地址 返回秒计数器的值 
	printf("%d\n",time_cnt);
		
	time_str = ctime (&time_cnt);
	printf(time_str);
	
	time_str = asctime (&time_date);
	printf(time_str);
	
	return 0;
}

 编译运行后显示的为下图所示(最后两行)

size_t strftime(char*,size_t,const char*,const struct tm*):

        这个函数作用同上边两个函数一样,但是区别是可以自定义格式。输入下列代码接在前边的代码后边,

char t[50]; 
strftime(t,50,"%H-%M-%S",&time_date);//前两个参数需要传入一个字符数组和数组长度 第三个参数需要给定指定的格式字符串 第四个参数把time_date传进去即可 
printf(t);

         其中关于第三个参数的格式,可以参考菜鸟教程中的文件,

       编译烧录后得到结果在最后一行

菜鸟教程

        clock_t clock(void)函数可以用于计算程序执行的时长,

        double difftime(time_t time1,time_t2 time2)可以计算两个时间之间的差值

        最重要且最复杂的是其中的localtime 和mktime函数,在STM32的RTC程序会使用到,需要重点掌握

各个函数的关系和作用

秒计数器数据类型time_t:

        秒计数器,time_t是一个typedef重命名的类型  如果不是特别声明要用32位的秒计数器类型,那么默认状况下就是__time64_t ,__time_t实际上是int64,是一个有符号的整型数据,无需担心溢出问题,可以用于存储时间戳中一直自增的秒数 

日期时间数据类型struct tm:

        struct tm time_date;//tm是结构体类型名,time_date是结构体变量名.

        关于struct tm里边的变量定义如下

  struct tm {
    int tm_sec;	//表示秒 取值范围0-59 
    int tm_min;	//表示分钟 取值范围0-59 
    int tm_hour; //表示午夜开始的小时 取值范围0-23 
    int tm_mday; //表示一个月的几号 取值范围0-31 
    int tm_mon;  //表示1月开始的第几个月 取值范围0-11 1月是0 12月是11 参数值需要加1才是所需月份 
    int tm_year; //表示从1900年的第几年 所以参数需要加上1900才是年份 时间戳起点是1970 参数最小值是70 
    int tm_wday; //表示周末开始的星期几  0表示周日  1表示周一  直到6表示周六 
    int tm_yday; //表示从1月1号开始的第几天 取值范围0-365 
    int tm_isdst; //是否使用夏令时 +1表示使用 0表示不用 -1表示不知道 
  };
字符串数据类型char*

        char型数据的指针,用于表示一个指向时间的字符串

        需要在ST_Link上引出一个3.3V电源接到stm32的VBAT引脚上,用于模拟电池的电源,一般情况下VBAT是电池供电口,需要接备用电池

        BKP寄存器和Flash存储器类似,都是用于存储数据,不过Flash的数据是掉电不丢失,而BKP的数据是需要通过VBAT引脚上的备用电池来维持的。只要VBAT有电池供电,即使STM32主电源断电,BKP的值仍然可以维持原状,并且在按下系统复位键之后,BKP的数据并不会产生复位

        如果把VBAT的电池断电,再次拔掉主电源上电,BKP的数据被清零,因为BKP的本质并不完全掉电不丢失,数据需要VBAT引脚提供备用电源维持。如果stm32接了备用电池,那么BKP可以完成主电源掉电时保存少量数据的任务。



实时时钟

        第一行显示日期,给的是一个测试时间;第二行是时间;第三行是时间戳的秒计数器;第四行是RTC预分频器的计数值

        作为实时时钟,按下复位键的时候不能复位,时间会继续运行。实时时钟在系统主电源断电之后需要继续运行,就像手机关机后时钟需继续运行,否则造成时间错误。在VBAT接上了电源再断开系统主电源,可以看到时间数据不会丢失,并且在断电期间RTC会继续走时不会暂停,是借助BKP实现的,RTC与BKP关联度较高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/571698.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

5 个有用的 Linux Shell 转义序列

目录 ⛳️推荐 前言          1、退格符 2、换行符 3、换页符 4、制表符 5、Unicode ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 前言          如何在你的…

计算机网络-IS-IS路由计算

前面已经学习了建立IS-IS邻接关系和同步LSDB&#xff0c;然后基于此路由器会进行路由计算。 一、路由计算 因为IS-IS路由器有不同的级别&#xff0c;只维护自身级别的LSDB&#xff0c;因此就是Level-1只有区域内的路由信息&#xff0c;Level-2有Level-2的路由信息&#xff0c;L…

初始Maven

1.背景 问题引入&#xff1a;为什么要使用 Maven&#xff1f; 1.1 添加第三方 jar 包 在 JavaEE 开发领域&#xff0c;有大量的第三方框架和工具可以供我们使用。使用这些 jar 包最简单的方法就是复制粘贴到 WEB-INF 目录中的 lib 目录下。但是会导致每次创建一个新工程就需要将…

【Linux】信号的产生

目录 一. 信号的概念signal() 函数 二. 信号的产生1. 键盘发送2. 系统调用kill()raise()abort() 3. 软件条件alarm() 4. 硬件异常除零错误:野指针: 三. 核心转储 一. 信号的概念 信号是消息的载体, 标志着不同的行为; 是进程间发送异步信息的一种方式, 属于软中断. 信号随时都…

医药流通企业如何安全访问医药ERP?无需公网IP和改变现有IT架构

随着目前医药流通行业竞争的加剧&#xff0c;市场供应日趋饱和&#xff0c;传统的粗放式管理缺陷逐渐暴露&#xff0c;导致从事医药行业企业的利润不同程度的下滑&#xff0c;想要满足医药行业客户的个性化需求&#xff0c;为适应企业未来发展&#xff0c;医药流通行业越来越多…

Docker 安装 Mongo

创建宿主机目录 在你的宿主机上创建必要的目录来存储 MongoDB 的数据和配置文件。这样做可以保证即使容器被删除&#xff0c;数据也能得到保留。 mkdir -p /develop/mongo/data mkdir -p /develop/mongo/config创建 MongoDB 配置文件 创建一个名为 mongod.conf 的 MongoDB 配…

C语言中整型与浮点型在内存中的存储

今天让我们来看看整型的数据和浮点型的数据在内存中是怎么存储的呢 整型数据在内存中的存储 整型数据在内存中存储的是二进制的补码 正数的话也没什么可说的&#xff0c;原码反码补码都相同 我们来看看负数&#xff1a; 以-5为例 原码&#xff1a;10000000 00000000 00000000 0…

【数据库】Redis

文章目录 [toc]Redis终端操作进入Redis终端Redis服务测试切换仓库 String命令存储字符串普通存储设置存储过期时间批量存储 查询字符串查询单条批量查询 Key命令查询key查询所有根据key首字母查询判断key是否存在查询指定的key对应的value的类型 删除键值对 Hash命令存储hash查…

【HTML】页面引用Vue3和Element-Plus

在现代前端开发中&#xff0c;Vue 3 和 Element Plus 是非常受欢迎的技术。Vue 3 是一个用于构建用户界面的渐进式 JavaScript 框架&#xff0c;而 Element Plus 是一个基于 Vue 3 的组件库&#xff0c;提供了丰富的 UI 组件&#xff0c;帮助开发者快速构建高质量的前端应用。 …

frp 实现 http / tcp 内网穿透(穿透 wordpress )

frp 实现 http / tcp 内网穿透&#xff08;穿透 wordpress &#xff09; 1. 背景简介与软件安装2. 服务端配置2.1 配置文件2.2 wordpress 配置文件2.3 frps 自启动 3.客户端配置3.1 配置文件3.2 frpc 自启动 同步发布在个人笔记frp 实现 http / tcp 内网穿透&#xff08;穿透 w…

多目标粒子群算法及其MATLAB实现

多目标粒子群优化&#xff08;Multi-Objective Particle Swarm Optimization, MOPSO&#xff09;算法是一种基于种群的优化算法&#xff0c;它结合了粒子群优化&#xff08;Particle Swarm Optimization, PSO&#xff09;和多目标优化的思想。多目标粒子群&#xff08;MOPSO&am…

DevOps(八)Jenkins的Maven和Git插件

一、Maven简介 Maven是一个构建生命周期管理和理解工具&#xff0c;用于Java项目。它提供了标准化的构建流程&#xff0c;并简化了从项目编译到文档生成等各种构建方面的管理。 Maven是由Apache软件基金会开发和维护的一个流行的项目管理工具。它的设计目的是简化Java项目的构…

PE结构(二)PE头字段说明

PE头字段 DOS头 PE标记 标准PE头 可选PE头 我们今天分析一下PE头字段中所有重要成员的含义 DOS头 DOS头中我们需要去分析的是如下两个成员&#xff1a; 1.WORD e_magic&#xff1a;MZ标记&#xff0c;用于判断是否为可执行文件&#xff0c;即如果显示4D 5A&#xff0c;…

[2021年最新]国产时序性数据TDenige入门

一、TDenige简介 TDengine&#xff1a;是涛思数据面对高速增长的物联网大数据市场和技术挑战推出的创新性的大数据处理产品&#xff0c;它不依赖任何第三方软件&#xff0c;也不是优化或包装了一个开源的数据库或流式计算产品&#xff0c;而是在吸取众多传统关系型数据库、NoS…

图搜索的经典启发式算法A星(A*、A Star)算法详解

文章目录 1. 引言2. 广度优先搜索3. Dijkstra 算法4. 启发式优先搜索&#xff08;Heuristic&#xff09;4.1 贪心最佳优先搜索4.2 A*搜索 1. 引言 在许多场景中&#xff0c;我们常会遇到一类问题&#xff0c;即“找到一个位置到另一个位置的距离最短&#xff08;用时最少&…

ELK 日志分析系统(二)

一、ELK Kibana 部署 1.1 安装Kibana软件包 #上传软件包 kibana-5.5.1-x86_64.rpm 到/opt目录 cd /opt rpm -ivh kibana-5.5.1-x86_64.rpm 1.2 设置 Kibana 的主配置文件 vim /etc/kibana/kibana.yml --2--取消注释&#xff0c;Kiabana 服务的默认监听端口为5601 server.po…

ubuntu 24.04 beta server NAT模式上网设置

在Ubuntu 24.04 Beta上设置网络通常涉及使用命令行工具。以下是设置静态IP地址和动态IP地址的步骤&#xff1a; 动态IP设置&#xff1a; 查找你的网络接口名称&#xff1a; ip a ens37是我NAT模型的一张网卡&#xff0c;此时是没有ip的。 下面介绍如何NAT模式下添加DHCP动态…

Maven多模块快速升级超好用Idea插件-MPVP

功能&#xff1a;多模块maven项目快速升级指定版本插件&#xff0c;并提供预览和相关升级模块日志能力。 可快速进行版本升级&#xff0c;进行部署到Maven仓库。 安装&#xff1a; 可在idea插件中心进行安装 / 下载资源拖动安装 MPVP(Maven) - IntelliJ IDEs Plugin | Marke…

node.js 解析post请求 方法一

前提&#xff1a;依旧以前面发的node.js服务器动态资源处理代码 具体见 http://t.csdnimg.cn/TSNW9为模板&#xff0c;在这基础上进行修改。与动态资源处理代码不同的是&#xff0c;这次的用户信息我们借用表单来实现。post请求解析来获取和展示用户表单填写信息 1》代码难点&…

快速新建springboot项目

一、初始化 1.打开IDEA&#xff0c;在Spring initializer这里按照下图项目进行配置。注意&#xff1a;如果jdk是1.8建议将Server URL这里替换为图中的阿里云服务器&#xff0c;否则容易找不到对应的java8&#xff0c;然后点击next 2.在这里提前配置一些需要使用的依赖&#xf…