手機(jī)賺錢網(wǎng)站新河seo怎么做整站排名
文章目錄
- Unix時(shí)間戳
- UTC/GMT
- 時(shí)間戳轉(zhuǎn)換
- BKP簡(jiǎn)介
- BKP基本結(jié)構(gòu)
- 讀寫B(tài)KP備份寄存器
- 電路設(shè)計(jì)
- 關(guān)鍵代碼
- RTC簡(jiǎn)介
- RTC框圖
- RTC基本結(jié)構(gòu)
- 硬件電路
- RTC操作注意事項(xiàng)
- 讀寫實(shí)時(shí)時(shí)鐘
- 電路設(shè)計(jì)
- 關(guān)鍵代碼
Unix時(shí)間戳
- Unix 時(shí)間戳(Unix Timestamp)定義為從UTC/GMT的1970年1月1日0時(shí)0分0秒開始所經(jīng)過的秒數(shù),不考慮閏秒
- 時(shí)間戳存儲(chǔ)在一個(gè)秒計(jì)數(shù)器中,秒計(jì)數(shù)器為32位/64位的整型變量
世界上所有時(shí)區(qū)的秒計(jì)數(shù)器相同,不同時(shí)區(qū)通過添加偏移來得到當(dāng)?shù)貢r(shí)間
- 底層使用秒計(jì)數(shù)器可以節(jié)省硬件設(shè)計(jì)電路,計(jì)算時(shí)間間隔,存儲(chǔ)方便
UTC/GMT
-
GMT(Greenwich Mean Time)格林尼治標(biāo)準(zhǔn)時(shí)間是一種以地球自轉(zhuǎn)為基礎(chǔ)的時(shí)間計(jì)量系統(tǒng)。它將地球自轉(zhuǎn)一周的時(shí)間間隔等分為24小時(shí),以此確定計(jì)時(shí)標(biāo)準(zhǔn)
-
UTC(Universal Time Coordinated)協(xié)調(diào)世界時(shí)是一種以原子鐘為基礎(chǔ)的時(shí)間計(jì)量系統(tǒng)。它規(guī)定銫133原子基態(tài)的兩個(gè)超精細(xì)能級(jí)間在零磁場(chǎng)下躍遷輻射9,192,631,770周所持續(xù)的時(shí)間為1秒。當(dāng)原子鐘計(jì)時(shí)一天的時(shí)間與地球自轉(zhuǎn)一周的時(shí)間相差超過0.9秒時(shí),
UTC會(huì)執(zhí)行閏秒來保證其計(jì)時(shí)與地球自轉(zhuǎn)的協(xié)調(diào)一致
時(shí)間戳轉(zhuǎn)換
C語言的time.h模塊提供了時(shí)間獲取和時(shí)間戳轉(zhuǎn)換的相關(guān)函數(shù),可以方便地進(jìn)行秒計(jì)數(shù)器、日期時(shí)間和字符串之間的轉(zhuǎn)換
- time_t 是int64數(shù)據(jù)類型
- struct tm 這是一個(gè)用來保存時(shí)間和日期的結(jié)構(gòu)。
struct tm {int tm_sec; /* 秒,范圍從 0 到 59 */int tm_min; /* 分,范圍從 0 到 59 */int tm_hour; /* 小時(shí),范圍從 0 到 23 */int tm_mday; /* 一月中的第幾天,范圍從 1 到 31 */int tm_mon; /* 月,范圍從 0 到 11 */int tm_year; /* 自 1900 年起的年數(shù) */int tm_wday; /* 一周中的第幾天,范圍從 0 到 6 */int tm_yday; /* 一年中的第幾天,范圍從 0 到 365 */int tm_isdst; /* 夏令時(shí) */
};
在線工具:在線時(shí)間戳轉(zhuǎn)換工具
菜鳥教程:C 標(biāo)準(zhǔn)庫(kù) - <time.h>
localtime和mktime的實(shí)例:
注意:mktime的參數(shù)不加const,因?yàn)樵搮?shù)既是輸入?yún)?shù)也是輸出參數(shù),因?yàn)橛?jì)算出星期后會(huì)填寫回去
strftime函數(shù):按照格式輸出
BKP簡(jiǎn)介
- BKP(Backup Registers)備份寄存器【需要VBAT引腳供電才能維持,掉電會(huì)清零,即使主電源掉電、系統(tǒng)復(fù)位也不會(huì)清零】【本質(zhì)是RAM存儲(chǔ)器,掉電丟失】
- BKP可用于存儲(chǔ)用戶應(yīng)用程序數(shù)據(jù)。當(dāng)VDD(
2.0~3.6V
)電源被切斷,他們?nèi)匀挥蒝BAT(1.8~3.6V
)維持供電。當(dāng)系統(tǒng)在待機(jī)模式下被喚醒,或系統(tǒng)復(fù)位或電源復(fù)位時(shí),他們也不會(huì)被復(fù)位【VBAT和VDD共地即可】 - TAMPER引腳產(chǎn)生的侵入事件【電平檢測(cè)】將所有備份寄存器BKP內(nèi)容清除,會(huì)申請(qǐng)中斷【VDD斷電也會(huì)工作】
- RTC引腳
輸出
RTC校準(zhǔn)時(shí)鐘、RTC鬧鐘脈沖或者秒脈沖【引腳2同一個(gè)時(shí)間內(nèi)只能使用一個(gè)功能】 - 存儲(chǔ)RTC時(shí)鐘校準(zhǔn)寄存器
- 用戶數(shù)據(jù)存儲(chǔ)容量:
- 20字節(jié)(中容量和小容量)/ 84字節(jié)(大容量和互聯(lián)型)
BKP基本結(jié)構(gòu)
當(dāng)VDD有電時(shí)就使用VDD供電,沒有時(shí)則使用功能VBAT供電
讀寫B(tài)KP備份寄存器
電路設(shè)計(jì)
關(guān)鍵代碼
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"void Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0;if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);Delay_ms(20);KeyNum = 1;}if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);Delay_ms(20);KeyNum = 2;}return KeyNum;
}
Key.h
#ifndef __KEY_H
#define __KEY_Hvoid Key_Init(void);
uint8_t Key_GetNum(void);#endif
main.c
按下按鍵,寫入備份寄存器,然后再讀出來
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"uint8_t KeyNum;uint16_t ArrayWrite[] = {0x1234, 0x5678};
uint16_t ArrayRead[2];int main(void)
{OLED_Init();Key_Init();OLED_ShowString(1, 1, "W:");OLED_ShowString(2, 1, "R:");//開啟PWR和BKP時(shí)鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);//在pwrd的庫(kù)函數(shù)中,備份寄存器訪問使能,設(shè)置PWR_CR的DBP,使能對(duì)BKP和RTC的訪問PWR_BackupAccessCmd(ENABLE);while (1){KeyNum = Key_GetNum();if (KeyNum == 1){ArrayWrite[0] ++;ArrayWrite[1] ++;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);}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);}
}
RTC簡(jiǎn)介
- RTC(Real Time Clock)實(shí)時(shí)時(shí)鐘
- RTC是一個(gè)獨(dú)立的定時(shí)器,可為系統(tǒng)提供時(shí)鐘和日歷的功能
RTC和時(shí)鐘配置系統(tǒng)處于后備區(qū)域,系統(tǒng)復(fù)位時(shí)數(shù)據(jù)不清零,VDD(2.0~3.6V)斷電后可借助VBAT(1.8~3.6V)供電繼續(xù)走時(shí)
【和BKP一樣,屬于后備區(qū)域】- 32位的可編程計(jì)數(shù)器,可對(duì)應(yīng)Unix時(shí)間戳的秒計(jì)數(shù)器【簡(jiǎn)化電路設(shè)計(jì)】
- 20位的可編程預(yù)分頻器,可適配不同頻率的輸入時(shí)鐘【變成1Hz頻率】
- 可選擇三種RTC時(shí)鐘源(PTCCLK):
- HSE時(shí)鐘除以128(通常為8MHz/128)
- LSE振蕩器時(shí)鐘(通常為32.768KHz)【經(jīng)過15位分頻器自然溢出得到1hz頻率】
- LSI振蕩器時(shí)鐘(40KHz)
RTC 復(fù)位和主電源掉電后,數(shù)據(jù)不丟失是BKP來實(shí)現(xiàn)的
注意:整個(gè)stm32有四個(gè)時(shí)鐘源
- HSE =高速外部時(shí)鐘信號(hào)
- HSI = 高速內(nèi)部時(shí)鐘信號(hào)
- LSl=低速內(nèi)部時(shí)鐘信號(hào)【低速時(shí)鐘供RTC和看門狗】
- LSE =低速外部時(shí)鐘信號(hào)【低速時(shí)鐘供RTC和看門狗】
RTC框圖
- 灰色區(qū)域?qū)儆诤髠鋮^(qū)域,待機(jī)時(shí)會(huì)供電
- RTC_ALR是鬧鐘,當(dāng)值與RTC_CNT相同時(shí)會(huì)產(chǎn)生信號(hào),讓stm退出待機(jī)
- 中斷信號(hào)有三種:秒中斷、計(jì)數(shù)器溢出中斷(2106年中斷)、鬧鐘中斷
- 鬧鐘信號(hào)和wkup引腳都可以喚醒設(shè)備(10引腳)
- stm32芯片框圖,常用32.768KHz,其他兩路都是備用方案,主要工作是給系統(tǒng)主時(shí)鐘和看門狗使用。且中間分頻器是可以通過VBAT供電,而另外兩路在掉電后時(shí)鐘會(huì)暫停
RTC基本結(jié)構(gòu)
- 余數(shù)寄存器是一個(gè)自減計(jì)數(shù)器,存儲(chǔ)當(dāng)前計(jì)數(shù)值
- 重裝寄存器是計(jì)數(shù)目標(biāo),決定分頻值
硬件電路
stm內(nèi)部供電方案中設(shè)計(jì)了供電開關(guān),有VDD用VDD,沒有則用VBAT
stm32自帶RTC晶振電路,如圖所示是32.768KHz和8MHz的晶振
RTC操作注意事項(xiàng)
- 執(zhí)行以下操作將使能對(duì)BKP和RTC的訪問:【初始化要完成如下操作】
- 設(shè)置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP時(shí)鐘
- 設(shè)置PWR_CR的DBP,使能對(duì)BKP和RTC的訪問
- 若在讀取RTC寄存器時(shí),RTC的APB1接口曾經(jīng)處于禁止?fàn)顟B(tài),則軟件首先必須等待RTC_CRL寄存器中的RSF位(寄存器同步標(biāo)志)被硬件置1
【等待同步】
- PCLK1和RTCCLK兩個(gè)時(shí)鐘頻率不一致,PCLK1在掉電后會(huì)停止,如果使用APB1總線開啟就去讀RTC的值會(huì)讀到0,需要等待RTC_CNT內(nèi)有值
- 必須設(shè)置RTC_CRL寄存器中的CNF位,使RTC進(jìn)入配置模式后,才能寫入RTC_PRL、RTC_CNT、RTC_ALR寄存器
- 對(duì)RTC任何寄存器的寫操作,都必須在前一次寫操作結(jié)束后進(jìn)行??梢酝ㄟ^查詢RTC_CR寄存器中的RTOFF狀態(tài)位,判斷RTC寄存器是否處于更新中。僅當(dāng)RTOFF狀態(tài)位是1時(shí),才可以寫入RTC寄存器
【等待上一步完成】
上述注意事項(xiàng)涉及到如下的框圖:
PCLK1和RTCCLK兩個(gè)時(shí)鐘頻率不一致,這會(huì)導(dǎo)致讀取和寫入操作不能立刻在寄存器中,需要通過RTC_CRL寄存器的RSF和CNF位去判斷在RTCCLK頻率下內(nèi)部電路是否完成了數(shù)據(jù)的變動(dòng)。
讀寫實(shí)時(shí)時(shí)鐘
電路設(shè)計(jì)
關(guān)鍵代碼
MyRTC.c
庫(kù)函數(shù)在rcc和rtc里面
#include "stm32f10x.h" // Device header
#include <time.h>//編譯器內(nèi)置的庫(kù)函數(shù)uint16_t MyRTC_Time[] = {2023, 1, 1, 23, 59, 55};void MyRTC_SetTime(void);void MyRTC_Init(void)
{//開啟PWR和BKP時(shí)鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);//在pwrd的庫(kù)函數(shù)中,備份寄存器訪問使能,設(shè)置PWR_CR的DBP,使能對(duì)BKP和RTC的訪問PWR_BackupAccessCmd(ENABLE);//復(fù)位的時(shí)候RTC計(jì)數(shù)器會(huì)清零,通過BKP的寄存器可以判斷是否使用備用電源,如果使用則RTC始終不用重新初始化if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){RCC_LSEConfig(RCC_LSE_ON);//開啟LSE//LSE開啟之后不是立馬就工作,需要判斷一下標(biāo)志位while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//選擇RTCCLK時(shí)鐘為L(zhǎng)SERCC_RTCCLKCmd(ENABLE);//使能//可加可不加下面2行RTC_WaitForSynchro();//等待同步RTC_WaitForLastTask();//等待上一次操作完成RTC_SetPrescaler(32768 - 1);//設(shè)置預(yù)分頻的值、該函數(shù)內(nèi)部會(huì)調(diào)用RTC_EnterConfigMode()和退出配置的代碼,設(shè)置RTC_CRL寄存器中的CNF位,此時(shí)RTC寄存器都可以被使用RTC_WaitForLastTask();//等待上一次操作完成MyRTC_SetTime();BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);}else{RTC_WaitForSynchro();//等待同步RTC_WaitForLastTask();//等待上一次操作完成}
}//如果LSE無法起振導(dǎo)致程序卡死在初始化函數(shù)中
//可將初始化函數(shù)替換為下述代碼,使用LSI當(dāng)作RTCCLK
//LSI無法由備用電源供電,故主電源掉電時(shí),RTC走時(shí)會(huì)暫停
/*
void MyRTC_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);PWR_BackupAccessCmd(ENABLE);if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){RCC_LSICmd(ENABLE);while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);//LSI是40khz,預(yù)分頻系數(shù)為40000-1RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro();RTC_WaitForLastTask();RTC_SetPrescaler(40000 - 1);RTC_WaitForLastTask();MyRTC_SetTime();BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);}else{RCC_LSICmd(ENABLE);while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro();RTC_WaitForLastTask();}
}*/void MyRTC_SetTime(void)
{time_t time_cnt;struct tm time_date;time_date.tm_year = MyRTC_Time[0] - 1900;time_date.tm_mon = MyRTC_Time[1] - 1;time_date.tm_mday = MyRTC_Time[2];time_date.tm_hour = MyRTC_Time[3];time_date.tm_min = MyRTC_Time[4];time_date.tm_sec = MyRTC_Time[5];//mktime始終是0時(shí)區(qū)time_cnt = mktime(&time_date) - 8 * 60 * 60;RTC_SetCounter(time_cnt);//寫入CNT計(jì)數(shù)器RTC_WaitForLastTask();//等待上一次操作完成
}void MyRTC_ReadTime(void)
{time_t time_cnt;struct tm time_date;time_cnt = RTC_GetCounter() + 8 * 60 * 60;//RTC_GetCounter讀取秒計(jì)數(shù)器//因?yàn)槭菛|八區(qū),多了8*60*60秒time_date = *localtime(&time_cnt);//stm32內(nèi)置的庫(kù)函數(shù)棄用gmtime函數(shù),只用localtime,同時(shí)該函數(shù)不能確定時(shí)區(qū),始終是0時(shí)區(qū)MyRTC_Time[0] = time_date.tm_year + 1900;MyRTC_Time[1] = time_date.tm_mon + 1;MyRTC_Time[2] = time_date.tm_mday;MyRTC_Time[3] = time_date.tm_hour;MyRTC_Time[4] = time_date.tm_min;MyRTC_Time[5] = time_date.tm_sec;
}
MyRTC.h
#ifndef __MYRTC_H
#define __MYRTC_Hextern uint16_t MyRTC_Time[];void MyRTC_Init(void);
void MyRTC_SetTime(void);
void MyRTC_ReadTime(void);#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"int main(void)
{OLED_Init();MyRTC_Init();OLED_ShowString(1, 1, "Date:XXXX-XX-XX");OLED_ShowString(2, 1, "Time:XX:XX:XX");OLED_ShowString(3, 1, "CNT :");OLED_ShowString(4, 1, "DIV :");while (1){MyRTC_ReadTime();//顯示日期OLED_ShowNum(1, 6, MyRTC_Time[0], 4);OLED_ShowNum(1, 11, MyRTC_Time[1], 2);OLED_ShowNum(1, 14, MyRTC_Time[2], 2);//顯示時(shí)間OLED_ShowNum(2, 6, MyRTC_Time[3], 2);OLED_ShowNum(2, 9, MyRTC_Time[4], 2);OLED_ShowNum(2, 12, MyRTC_Time[5], 2);OLED_ShowNum(3, 6, RTC_GetCounter(), 10);OLED_ShowNum(4, 6, RTC_GetDivider(), 10);//RTC_GetDivider可以獲取更加精細(xì)的時(shí)間}
}
參考視頻:江科大自化協(xié)