文章目录
- BKP备份寄存器
- Ⅰ、BKP简介
- 1. BKP的基本功能
- 2. BKP的存储容量
- 3. BKP的访问和操作
- 4. BKP的应用场景
- 5. BKP的控制寄存器
- Ⅱ、BKP基本结构
- Ⅲ、BKP函数
- Ⅳ、BKP使用示例
- 时间戳
- 一、Unix时间戳
- 二、时间戳的转换(time.h函数介绍)
- Ⅰ、time()
- Ⅱ、mktime()
- Ⅲ、localtime()
- Ⅳ、gmtime()
- Ⅴ、asctime()
- Ⅵ、strftime()
- Ⅶ、ctime()
- Ⅷ、clock()
- Ⅸ、difftime()
BKP备份寄存器
Ⅰ、BKP简介
主要用于在系统断电或复位后保存和恢复关键数据
BKP(
Backup Registers
)备份寄存器BKP可用于存储用户应用程序数据。当
VDD
(2.0~3.6V)电源被切断,他们仍然由VBAT
(1.8~3.6V)维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位
TAMPER
引脚产生的侵入事件将所有备份寄存器内容清除
RTC
引脚输出RTC
校准时钟、RTC闹钟脉冲或者秒脉冲存储RTC时钟校准寄存器
用户数据存储容量:
- 20字节(中容量和小容量)
- 84字节(大容量和互联型)
1. BKP的基本功能
- 数据备份:BKP可以存储用户应用程序数据。当
VDD(2.0~3.6V)
电源被切断时,BKP仍然由VBAT(1.8~3.6V)
维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,BKP中的数据也不会被复位 - 侵入检测:TAMPER引脚可以产生侵入事件,将所有备份寄存器内容清除。这在需要防止数据被恶意获取时非常有用
- RTC校准:BKP还包含RTC时钟校准寄存器,用于存储RTC校准值。此外,RTC引脚可以输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲
2. BKP的存储容量
- 中容量和小容量:20字节(10个16位寄存器)
- 大容量和互联型:84字节(42个16位寄存器)
3. BKP的访问和操作
-
使能时钟:在访问BKP寄存器之前,需要使能PWR和BKP的时钟,并解锁写保护机制
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE);
-
读写操作:可以使用标准库函数进行读写操作
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); // 写备份寄存器 uint16_t data = BKP_ReadBackupRegister(BKP_DR1); // 读备份寄存器
-
复位操作:可以使用
BKP_DeInit()
函数复位BKP寄存器,清除备份寄存器数据BKP_DeInit(); // 备份域复位,复位BKP寄存器,清除备份寄存器数据
4. BKP的应用场景
- 系统配置保存:保存系统的配置参数,如通信设置、用户偏好等,以便在系统重启后快速恢复
- 状态信息保存:保存关键状态信息,如设备的工作模式、传感器状态等,确保系统在重启后能够继续正常运行
- 故障恢复:在系统发生故障时,保存关键数据,以便在系统恢复后进行故障诊断和恢复
- 侵入检测:通过TAMPER引脚检测外部侵入事件,保护系统数据的安全
5. BKP的控制寄存器
- BKP_CR:备份控制寄存器,用于管理侵入检测和RTC校准功能
- BKP_DRx:备份数据寄存器,用于存储用户数据,每个寄存器为16位
Ⅱ、BKP基本结构
Ⅲ、BKP函数
// 备份寄存器(BKP)去初始化函数,用于将备份寄存器寄存器重置为默认值
void BKP_DeInit(void);
// 配置备份寄存器(BKP)防篡改引脚电平(侵入检测)
void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel);
// 使能或失能备份寄存器(BKP)防篡改引脚
void BKP_TamperPinCmd(FunctionalState NewState);
// 使能或失能备份寄存器(BKP)中断
void BKP_ITConfig(FunctionalState NewState);
// 配置备份寄存器(BKP)RTC输出源
void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource);
// 设置备份寄存器(BKP)RTC校准值
void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue);
// 向备份寄存器(BKP)备份寄存器写入数据
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
// 从备份寄存器(BKP)备份寄存器读取数据
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);
// 获取备份寄存器(BKP)标志位状态
FlagStatus BKP_GetFlagStatus(void);
// 清除备份寄存器(BKP)标志位
void BKP_ClearFlag(void);
// 获取备份寄存器(BKP)中断状态
ITStatus BKP_GetITStatus(void);
// 清除备份寄存器(BKP)中断待处理位
void BKP_ClearITPendingBit(void);
Ⅳ、BKP使用示例
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Key.h"
#include "OLED.h"
uint16_t ArrayWrite[] = {0x1122, 0xAABB};//写入BKP的数据
uint16_t ArrayRead[2] = { 0 }; //从BKP中读出的数据
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"W:");
OLED_ShowString(2,1,"R:");
char keynum;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能PWR
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);//使能BKP
PWR_BackupAccessCmd(ENABLE);//备份寄存器访问使能
while(1)
{
keynum = Get_KeyNum();
if(keynum == 2)
{
BKP_WriteBackupRegister(BKP_DR1, ArrayWrite[0]);
BKP_WriteBackupRegister(BKP_DR2, ArrayWrite[1]);
OLED_ShowHexNum(1,3,ArrayWrite[0],4);
OLED_ShowHexNum(1,8,ArrayWrite[1],4);
ArrayWrite[0]++;
ArrayWrite[1]++;
}
ArrayRead[0] = BKP_ReadBackupRegister(BKP_DR1);
ArrayRead[1] = BKP_ReadBackupRegister(BKP_DR2);
OLED_ShowHexNum(2,3,ArrayRead[0] ,4);
OLED_ShowHexNum(2,8,ArrayRead[1] ,4);
}
}
时间戳
一、Unix时间戳
时间戳是指自1970年1月1日(UTC/GMT的午夜) 以来经过的秒数(不考虑闰秒)
它是一个表示时间的数值,常用于计算机系统、数据库、网络通信等领域来记录事件发生的时间
- 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量
- 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间
二、时间戳的转换(time.h函数介绍)
类型 | 说明 |
---|---|
time_t | 用于表示时间的类型,通常是表示自1970年1月1日以来的秒数(时间戳) |
struct tm | 用于表示时间的结构体,包含年、月、日、时、分、秒等时间信息 |
宏 | 说明 |
---|---|
CLOCKS_PER_SEC | 每秒的时钟周期数,用于clock() 函数计算时间间隔 |
TIME_UTC | 表示 UTC 时间(C11) |
函数 | 说明 |
---|---|
time(time_t *tloc) | 获取时间戳 |
mktime(struct tm *timeptr) | struct tm 结构体—>时间戳 |
localtime(const time_t *timer) | 时间戳—>本地时间的struct tm 结构体 |
gmtime(const time_t *timer) | 将时间戳—>UTC时间的struct tm 结构体 |
asctime(const struct tm *timeptr) | 将时间结构体—>字符串,格式为“Wed Jan 01 00:00:00 1990\n” |
strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) | 时间结构体—>字符串 |
ctime(const time_t *timer) | 时间戳—>字符串 |
clock() | 获取程序中某部分代码的执行时间,单位为时钟周期,常用于性能测试 |
difftime(time_t time1, time_t time0) | 计算两个时间戳之间的时间差,单位为秒 |
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月份,范围从 0 到 11 (+1)*/
int tm_year; /* 自1900年起的年数 (+1900)*/
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时标识符,1 表示夏令时,0 表示非夏令时,-1 表示自动检测 */
};
Ⅰ、time()
获取时间戳
time(time_t *tloc)
函数用于获取当前时间和日期,并将其存储为自1970年1月1日以来的秒数(时间戳)
函数原型:
time_t time(time_t *tloc);
参数:
tloc
:指向time_t
类型的指针
- 如果为
NULL
,则函数仅返回时间戳,不进行存储- 如果非空,则将时间戳存储在
tloc
指向的位置返回值:
- 成功时,返回当前时间的时间戳(自1970年1月1日以来的秒数)
- 失败时,返回
(time_t)-1
示例代码
示例1:仅获取时间戳
#include <stdio.h> #include <time.h> int main() { time_t current_time = time(NULL); // 获取当前时间的时间戳 printf("当前时间的时间戳: %ld\n", current_time); return 0; }
示例中,
time(NULL)
获取当前时间的时间戳,并将其存储在current_time
变量中,然后打印出来示例2:获取时间戳并存储
#include <stdio.h> #include <time.h> int main() { time_t current_time; time(¤t_time); // 获取当前时间的时间戳并存储在current_time中 // 将时间戳转换为本地时间并格式化输出 struct tm *local_time = localtime(¤t_time); char buffer[80]; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local_time); printf("本地时间:%s\n", buffer); return 0; }
在示例中,
time(¤t_time)
获取当前时间的时间戳并存储在current_time
变量中。然后使用localtime
函数将时间戳转换为本地时间的struct tm
结构体,再使用strftime
函数将本地时间格式化为字符串并打印出来
Ⅱ、mktime()
将
struct tm
结构体表示的时间转换为时间戳
mktime
函数用于将struct tm
结构体表示的本地时间转换为自1970年1月1日以来的秒数(时间戳)
该函数会自动处理时区和夏令时的转换
函数原型:
time_t mktime(struct tm *timeptr);
参数:
timeptr
:指向struct tm
结构的指针,该结构体包含年、月、日、时、分、秒等时间信息返回值:
- 成功时,返回自1970年1月1日以来的秒数
- 失败时,返回
(time_t)-1
struct tm
结构体struct tm { int tm_sec; /* 秒,范围从 0 到 59 */ int tm_min; /* 分,范围从 0 到 59 */ int tm_hour; /* 小时,范围从 0 到 23 */ int tm_mday; /* 一月中的第几天,范围从 1 到 31 */ int tm_mon; /* 月份,范围从 0 到 11 */ int tm_year; /* 自1900年起的年数 */ int tm_wday; /* 一周中的第几天,范围从 0 到 6 */ int tm_yday; /* 一年中的第几天,范围从 0 到 365 */ int tm_isdst; /* 夏令时标识符,1 表示夏令时,0 表示非夏令时,-1 表示自动检测 */ };
示例代码 示例:将特定日期和时间转换为时间戳
#include <stdio.h> #include <time.h> int main() { struct tm time_info; time_t time_as_seconds; // 设置tm结构体为2023年8月17日08:34:56 time_info.tm_year = 2023 - 1900; // 年份从1900年开始 time_info.tm_mon = 8 - 1; // 月份从0开始 time_info.tm_mday = 17; // 日 time_info.tm_hour = 8; // 小时 time_info.tm_min = 34; // 分钟 time_info.tm_sec = 56; // 秒 time_info.tm_isdst = -1; // 让mktime()自动检测夏令时 // 转换为time_t类型 time_as_seconds = mktime(&time_info); if (time_as_seconds != (time_t)(-1)) { printf("时间转换为秒数成功: %ld\n", (long)time_as_seconds); } else { printf("时间转换失败\n"); } return 0; }
输出结果:
时间转换为秒数成功: 1692232496
注意事项
mktime
函数会自动处理时区和夏令时的转换tm_isdst
字段可以设置为 -1,让mktime
自动检测夏令时tm_wday
和tm_yday
字段在调用mktime
时会被自动计算和更新
Ⅲ、localtime()
将时间戳转换为本地时间的
struct tm
结构体
localtime
函数用于将时间戳(time_t
类型)转换为本地时间的struct tm
结构体
该函数会自动处理时区和夏令时的转换
函数原型:
struct tm *localtime(const time_t *timer);
参数:
timer
:指向time_t
类型的指针,该类型表示自1970年1月1日00:00:00 UTC以来的秒数(时间戳)返回值:
- 成功时,返回指向
struct tm
结构体的指针,该结构体包含本地时间的信息- 失败时,返回
NULL
示例代码 示例:将时间戳转换为本地时间并格式化输出
#include <stdio.h> #include <time.h> int main() { time_t current_time; struct tm *local_time; // 获取当前时间的时间戳 current_time = time(NULL); // 将时间戳转换为本地时间 local_time = localtime(¤t_time); // 格式化输出本地时间 char buffer[80]; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local_time); printf("本地时间:%s\n", buffer); return 0; }
输出结果:
本地时间:2025-01-13 12:34:56
Ⅳ、gmtime()
将时间戳转换为UTC时间
gmtime
函数用于将时间戳(time_t
类型)转换为UTC(协调世界时)时间的struct tm
结构体
函数原型:
struct tm *gmtime(const time_t *timer);
参数:
timer
:指向time_t
类型的指针,该类型表示自1970年1月1日00:00:00 UTC以来的秒数(时间戳)返回值:
- 成功时,返回指向
struct tm
结构体的指针,该结构体包含UTC时间的信息- 失败时,返回
NULL
示例代码 示例:将时间戳转换为UTC时间并格式化输出
#include <stdio.h> #include <time.h> int main() { time_t current_time; struct tm *utc_time; // 获取当前时间的时间戳 current_time = time(NULL); // 将时间戳转换为UTC时间 utc_time = gmtime(¤t_time); // 格式化输出UTC时间 char buffer[80]; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", utc_time); printf("UTC时间:%s\n", buffer); return 0; }
输出结果:
UTC时间:2025-01-13 04:34:56
Ⅴ、asctime()
将时间结构体转换为字符串,格式为
Wed Jan 01 00:00:00 1990\n
asctime
函数用于将struct tm
结构体表示的时间转换为一个标准的字符串格式
该函数不会考虑时区和夏令时,直接将时间戳转换为UTC时间
函数原型:
char *asctime(const struct tm *timeptr);
参数:
timeptr
:指向struct tm
结构的指针,该结构体包含年、月、日、时、分、秒等时间信息返回值:
- 成功时,返回指向格式化时间字符串的指针,字符串格式为
"Wed Jan 01 00:00:00 1990\n"
- 失败时,返回
NULL
示例代码 示例:将当前时间转换为字符串
#include <stdio.h> #include <time.h> int main() { time_t current_time; struct tm *local_time; // 获取当前时间的时间戳 current_time = time(NULL); // 将时间戳转换为本地时间 local_time = localtime(¤t_time); // 将本地时间转换为字符串 char *time_string = asctime(local_time); printf("当前时间:%s", time_string); return 0; }
输出结果:
当前时间:Mon Jan 13 12:34:56 2025
Ⅵ、strftime()
按照指定格式将时间结构体格式化为字符串
strftime
函数用于将struct tm
结构体表示的时间格式化为指定格式的字符串
该函数非常灵活,可以生成各种格式的时间字符串,常用于日志记录、时间显示等场景
函数原型:
size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr);
参数:
s
:指向字符数组的指针,用于存储格式化后的字符串maxsize
:指定字符数组的最大长度,以确保不会发生缓冲区溢出format
:格式化字符串,用于指定时间的输出格式timeptr
:指向struct tm
结构的指针,该结构体包含年、月、日、时、分、秒等时间信息返回值:
- 成功时,返回格式化字符串的长度(不包括终止空字符)
- 如果输出字符串的长度超过
maxsize
,则返回0,并且s
指向的数组内容未定义常见格式化字符串
%Y
:四位年份(例如 2025)%m
:月份(01 到 12)%d
:一月中的第几天(01 到 31)%H
:小时(00 到 23)%M
:分钟(00 到 59)%S
:秒(00 到 59)%a
:星期几的缩写(例如 Mon)%b
:月份的缩写(例如 Jan)%c
:本地日期和时间的表示(例如 Mon Jan 13 12:34:56 2025)%x
:本地日期的表示(例如 01/13/25)%X
:本地时间的表示(例如 12:34:56)示例代码 示例:将当前时间格式化为字符串
#include <stdio.h> #include <time.h> int main() { time_t current_time; struct tm *local_time; char buffer[80]; // 获取当前时间的时间戳 current_time = time(NULL); // 将时间戳转换为本地时间 local_time = localtime(¤t_time); // 将本地时间格式化为字符串 strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local_time); printf("当前时间:%s\n", buffer); return 0; }
输出结果:
当前时间:2025-01-13 12:34:56
Ⅶ、ctime()
将时间戳转换为字符串
ctime
函数用于将时间戳(time_t
类型)转换为一个标准的字符串格式
该函数会将时间戳转换为本地时间,并格式化为一个固定格式的字符串,通常用于日志记录和时间显示
函数原型:
char *ctime(const time_t *timer);
参数:
timer
:指向time_t
类型的指针,该类型表示自1970年1月1日00:00:00 UTC以来的秒数(时间戳)返回值:
- 成功时,返回指向格式化时间字符串的指针,字符串格式为
"Wed Jan 01 00:00:00 1990\n"
- 失败时,返回
NULL
示例代码 示例:将当前时间的时间戳转换为字符串
#include <stdio.h> #include <time.h> int main() { time_t current_time; // 获取当前时间的时间戳 current_time = time(NULL); // 将时间戳转换为字符串 char *time_string = ctime(¤t_time); printf("当前时间:%s", time_string); return 0; }
输出结果:
当前时间:Mon Jan 13 12:34:56 2025
asctime()
ctime()
时间戳—>字符串 时间结构体—>字符串 Wed Jan 01 00:00:00 1990\n Wed Jan 01 00:00:00 1990\n
Ⅷ、clock()
clock
函数用于获取程序中某部分代码的执行时间,单位为时钟周期(clock ticks)。这通常用于性能测试,以测量代码段的执行时间
函数原型:
clock_t clock(void);
参数:
- 无参数
返回值:
- 返回自程序开始执行以来的时钟周期数
- 如果无法获取时钟周期数,返回
(clock_t)-1
示例代码 示例1:测量代码段的执行时间
#include <stdio.h> #include <time.h> int main() { clock_t start, end; double cpu_time_used; // 获取开始时间 start = clock(); // 要测量的代码段 for (int i = 0; i < 1000000; i++) { // 一些计算 } // 获取结束时间 end = clock(); // 计算执行时间 cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; printf("代码段的执行时间:%.6f 秒\n", cpu_time_used); return 0; }
输出结果:
代码段的执行时间:0.012345 秒
Ⅸ、difftime()
计算两个时间戳之间的时间差
difftime
函数用于计算两个时间戳之间的时间差,单位为秒。这通常用于测量时间间隔,例如计算代码段的执行时间或两个事件之间的时间差
函数原型:
double difftime(time_t time1, time_t time0);
参数:
time1
:结束时间的时间戳time0
:开始时间的时间戳返回值:
- 返回两个时间戳之间的时间差,单位为秒
- 如果
time1
早于time0
,返回值为负数说明:
difftime
函数计算time1
和time0
之间的时间差,单位为秒- 该函数考虑了时间戳的溢出问题,因此可以安全地用于大范围的时间计算
difftime
函数返回的是一个double
类型的值,可以提供更精确的时间差示例代码 示例1:测量代码段的执行时间
#include <stdio.h> #include <time.h> int main() { time_t start, end; double elapsed; // 获取开始时间 start = time(NULL); // 要测量的代码段 for (int i = 0; i < 1000000; i++) { // 一些计算 } // 获取结束时间 end = time(NULL); // 计算时间差 elapsed = difftime(end, start); printf("代码段的执行时间:%.6f 秒\n", elapsed); return 0; }
输出结果:
代码段的执行时间:0.012345 秒