BKPの読み書き
アイデア:
- PWR および BKP クロックを有効にし、
- PWR 関数 void PWR_BackupAccessCmd(FunctionalState NewState); を通じて BKP RTC アクセスを有効にします。
- BKP の読み取りおよび書き込み関数 void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data); uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR); を呼び出して、BKP の読み取りおよび書き込みを行います。
バックアップ電源がないと、主電源が切断された後、BKP 内のデータを維持できません。リードデータ0000
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.H"
#include "Key.h"
#include "OLED.H"
uint16_t ArrayWrite[2]={0x1234,0x5678};
uint16_t ArrayRead[2];
uint8_t keynum;
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"W");
OLED_ShowString(2,1,"R");
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
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の基本構造に基づく)
- BKP の読み取りと書き込みの手順 1 と 2 と同じです。
- LSE クロックをオンにします (デフォルトではオフになっています。使用時にオンにする関数を手動で呼び出す必要があります)。
- RTCCLK データ セレクターを設定し、RTCCLK として LSE を指定します。
- 2 番目のポイントは同期を待機する関数を呼び出し、4 番目のポイントは書き込み操作の終了を待機する関数を呼び出していることに注意してください。
- カウンタクロック周波数が 1hz になるようにプリスケーラを設定します。
- cnt を設定し、RTC 初期値を与えます。設定前に目覚まし時計または割り込みが必要です。
MyRTCモジュール
#include "stm32f10x.h" // Device header
#include <time.h>
#include "MYRTC.h"
uint16_t RTC_Time[6]={2023,1,1,23,59,55};
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)//借助BKP的特性,置一个数当作标志位实习复位断电不重置时间
{
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)//调用函数等RCC有个标志位LSERDY置1时时钟启动完成
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
//注意事项第三点。函数本身调用了进入配置模式函数,所以不用再每次调用。
RTC_SetPrescaler(32768 - 1); //时钟频率1hz
RTC_WaitForLastTask(); //对RTC寄存器的写操作要等待写操作结束
RTC_SetTime(); //每次复位后调用MyRTC_Init()会将时间重置。
BKP_WriteBackupRegister(BKP_DR1,0XA5A5);
}
else
{
RTC_WaitForSynchro();
RTC_WaitForLastTask();
}
}
void RTC_SetTime(void)
{
time_t time_cnt;
struct tm time_date;
time_date.tm_year=RTC_Time[0] - 1900;
time_date.tm_mon=RTC_Time[1] - 1;
time_date.tm_mday=RTC_Time[2];
time_date.tm_hour=RTC_Time[3];
time_date.tm_min=RTC_Time[4];
time_date.tm_sec=RTC_Time[5];
time_cnt = mktime(&time_date) - 8*60*60;
RTC_SetCounter(time_cnt);
RTC_WaitForLastTask();
}
void RTC_ReadTime(void)
{
time_t time_cnt;
struct tm time_date;
time_cnt=RTC_GetCounter() + 8*60*60;
time_date = *localtime(&time_cnt);
RTC_Time[0] = time_date.tm_year + 1900;
RTC_Time[1] = time_date.tm_mon + 1;
RTC_Time[2] = time_date.tm_mday;
RTC_Time[3] = time_date.tm_hour;
RTC_Time[4] = time_date.tm_min;
RTC_Time[5] = time_date.tm_sec;
}
メイン機能
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)
{
RTC_ReadTime();
OLED_ShowNum(1,6,RTC_Time[0],4);
OLED_ShowNum(1,11,RTC_Time[1],2);
OLED_ShowNum(1,14,RTC_Time[2],2);
OLED_ShowNum(2,6,RTC_Time[3],2);
OLED_ShowNum(2,9,RTC_Time[4],2);
OLED_ShowNum(2,12,RTC_Time[5],2);
OLED_ShowNum(3,6,RTC_GetCounter(),10);
//DIV每自减一轮,cnt+1;自减范围32767~0
OLED_ShowNum(4,6,RTC_GetDivider(),10);
}
}
DIV の使用法の 1 つは、線形関係でミリ秒などまでカウントしたい場合です。 DIVが1ラウンド減少するたびにcnt+1、自動減少範囲は32767~0です。
32767------ 0
0 ----------- 999
範囲を 0 ~ 32767 に変更するには、32767-RTC_GetDivider() を使用します。
次に、ズーム (32767-RTC_GetDivider())/32767.0 *999;
このようにして、DIV は元の 32767-0 からミリ秒を表す 0 ~ 999 の値に変換されます。