stm32week7

server/2025/3/18 23:24:16/

stm32_0">stm32学习

三.通信

10.BKP和RTC

BKP(backup registers)备份寄存器
BKP可以存储数据,掉电丢失,平时用外部电源供电,外部电源切断后用VBAT维持供电,当系统在待机状态下被唤醒,或系统复位或电源复位时,它们也不会被复位
TAMPER引脚可以产生的侵入事件,将所有备份寄存器的内容清除
RTC引脚可以输出RTC校准时钟(对内部RTC微小的误差进行校准)、RTC闹钟脉冲、秒脉冲(可以输出出来,为别的设备提供信号)
PC13、TAMPER、RTC共用一个引脚,同时只能使用一个功能
存储RTC时钟校准寄存器,联合RTC校准时钟,对RTC进行校准
用户数据存储容量:20字节(中容量和小容量)/84字节(大容量和互联型)

图片消失了

RTC实时时钟,是一个独立的定时器,可为系统提供时钟和日历的功能
RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD断电后可借助VBAT供电
32位的可编程计数器,可对应Unix时间戳的秒计数器
20位的可编程预分频器,可适配不同频率的输入时钟
可选择的三种RTC时钟源:

  1. HSE时钟(除以128,因为本身8MHz,预分频不够用)
  2. LSE振荡器时钟(32.768kHz)
  3. LSI振荡器时钟(40kHz)

RTC框图:

图片消失了

RTC基本结构:

图片消失了

硬件电路:

图片消失了

RTC操作注意事项:

  1. 执行以下操作将使能对BKP和RTC的访问
    1. 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟
    2. 设置PWR_CR的DBP,使能对BKP和RTC的访问
  2. 若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1
  3. 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器
  4. 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器

读写备份寄存器的初始化只需要

 /*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);  //开启PWR的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);  //开启BKP的时钟/*备份寄存器访问使能*/PWR_BackupAccessCmd(ENABLE);       //使用PWR开启对备份寄存器的访问

然后就可以用BKP_ReadBackupRegister和BKP_WriteBackupRegister来读写寄存器

RTC初始化和RTC读写时间的函数:

#include "stm32f10x.h"                  // Device header
#include <time.h>uint16_t MyRTC_Time[] = {2023, 1, 1, 23, 59, 55}; //定义全局的时间数组,数组内容分别为年、月、日、时、分、秒void MyRTC_SetTime(void);    //函数声明/*** 函    数:RTC初始化* 参    数:无* 返 回 值:无*/
void MyRTC_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);  //开启PWR的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);  //开启BKP的时钟/*备份寄存器访问使能*/PWR_BackupAccessCmd(ENABLE);       //使用PWR开启对备份寄存器的访问if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)   //通过写入备份寄存器的标志位,判断RTC是否是第一次配置//if成立则执行第一次的RTC配置{RCC_LSEConfig(RCC_LSE_ON);       //开启LSE时钟while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET); //等待LSE准备就绪RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);    //选择RTCCLK来源为LSERCC_RTCCLKCmd(ENABLE);        //RTCCLK使能RTC_WaitForSynchro();        //等待同步RTC_WaitForLastTask();        //等待上一次操作完成RTC_SetPrescaler(32768 - 1);      //设置RTC预分频器,预分频后的计数频率为1HzRTC_WaitForLastTask();        //等待上一次操作完成MyRTC_SetTime();         //设置时间,调用此函数,全局数组里时间值刷新到RTC硬件电路BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);   //在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置}else             //RTC不是第一次配置{RTC_WaitForSynchro();        //等待同步RTC_WaitForLastTask();        //等待上一次操作完成}
}//如果LSE无法起振导致程序卡死在初始化函数中
//可将初始化函数替换为下述代码,使用LSI当作RTCCLK
//LSI无法由备用电源供电,故主电源掉电时,RTC走时会暂停
/* 
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);RCC_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);    //即使不是第一次配置,也需要再次开启LSI时钟while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro();RTC_WaitForLastTask();}
}*//*** 函    数:RTC设置时间* 参    数:无* 返 回 值:无* 说    明:调用此函数后,全局数组里时间值将刷新到RTC硬件电路*/
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];time_cnt = mktime(&time_date) - 8 * 60 * 60; //调用mktime函数,将日期时间转换为秒计数器格式//- 8 * 60 * 60为东八区的时区调整RTC_SetCounter(time_cnt);      //将秒计数器写入到RTC的CNT中RTC_WaitForLastTask();       //等待上一次操作完成
}/*** 函    数:RTC读取时间* 参    数:无* 返 回 值:无* 说    明:调用此函数后,RTC硬件电路里时间值将刷新到全局数组*/
void MyRTC_ReadTime(void)
{time_t time_cnt;  //定义秒计数器数据类型struct tm time_date; //定义日期时间数据类型time_cnt = RTC_GetCounter() + 8 * 60 * 60;  //读取RTC的CNT,获取当前的秒计数器//+ 8 * 60 * 60为东八区的时区调整time_date = *localtime(&time_cnt);    //使用localtime函数,将秒计数器转换为日期时间格式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;
}

然后在main.c的while里面调用MyRTC_ReadTime就能读取实时时钟了

11.PWR

PWR电源控制:PWR负责管理stm32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能
可编程电压监测器(PVD)可以监控VDD电源电压,当VDD下降到PVD阈值以下或者之上时,PVD会触发中断,用于执行紧急关闭任务
低功耗功能包括睡眠模式(sleep)、停机模式(stop)、和待机模式(standby),可在系统空闲时,降低stm32的功耗,延长设备的使用时间,睡眠模式一般省电,停机模式比较省电,待机模式极为省电

电源框图:

图片消失了

上电复位和掉电复位的波形图:

图片消失了

PVD的门限:

图片消失了

低功耗模式一览:

图片消失了

模式细分:

图片消失了

睡眠模式:

  1. 执行完WFI/WFE指令后,stm32进入睡眠模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
  2. SLEEPONEXIT位决定stm32执行完WFI或WFE后,是立刻进入睡眠,还是等stm32从最低优先级的中断处理程序中退出时进入睡眠
  3. 在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态
  4. WFI指令进入睡眠模式,可被任何一个NVIC响应的中断唤醒
  5. WFE指令进入睡眠模式,可被唤醒事件唤醒

停止模式:

  1. 执行完WFI/WFE指令后,stm32进入停止模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
  2. 1.8v供电区域的所有时钟都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器内容被保留下来
  3. 在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态
  4. 当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟
  5. 当电压调节器处于低功耗模式下,系统从停止模式退出,会有一段额外的启动延时
  6. WFI指令进入停止模式,可被任何一个EXTI中断唤醒
  7. WFE指令进入停止模式,可被任何一个EXTI事件唤醒

待机模式:

  1. 执行完WFI/WFE指令后,stm32进入待机模式,唤醒后程序从头开始运行
  2. 整个1.8v供电区域被断电,PLL、HSI和HSE也被断电,SRAM和寄存器内容丢失,只有备份的寄存器和待机电路维持供电
  3. 在待机模式下,所有的I/O引脚变为高阻态(浮空输入)
  4. WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、IWDG复位退出待机模式

代码:
修改主频:
(在system_stm32f10x.c中将其它的注释掉就行)

/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
#define SYSCLK_FREQ_36MHz  36000000
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
/* #define SYSCLK_FREQ_72MHz  72000000 */

库函数中已经宏定义了SystemCoreClock:时钟频率
启动睡眠模式:
(串口发送接收会打断睡眠模式)

__WFI(); //执行WFI指令,CPU睡眠,并等待中断唤醒,WFI的内核是汇编代码
//__WFE();  //中断事件,配置比WFI难,所以一般用上面的

启动停止模式:
(烧录前需要按住复位键,烧录完之后松开,外部中断能中断停止模式)

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);  
//开启PWR的时钟
//停止模式和待机模式一定要记得开启
PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); 
//STM32进入停止模式,并等待中断唤醒
SystemInit();          //唤醒后,要重新配置时钟

待机模式:
(同样要按住复位键)

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);  
//开启PWR的时钟
//停止模式和待机模式一定要记得开启
PWR_EnterSTANDBYMode();      
//STM32进入停止模式,并等待指定的唤醒事件(WKUP上升沿或RTC闹钟)

四.看门狗

1.独立看门狗

WDG(Watchdog)看门狗:看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或者跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
看门狗本质上是一个定时器,当指定时间范围内,程序没有执行喂狗(重置计数器)操作时,看门狗硬件电路就自动产生复位信号
stm32内置两个看门狗:
独立看门狗(IWDG):独立工作,对时间精度要求较低
窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用
IWDG框图:

图片消失了

IWDG键寄存器:键寄存器本质上是控制寄存器,用于控制硬件电路的工作
在可能存在干扰的情况下,一般通过在整个键寄存器写入特定值来代替控制寄存器写入一位的功能,以降低硬件电路收到干扰的概率

图片消失了

超时时间: T I W D G = T L S I × P R 预分频系数 × ( R L + 1 ) T_{IWDG} = T_{LSI} \times PR预分频系数 \times (RL + 1) TIWDG=TLSI×PR预分频系数×(RL+1)
其中: T L S I = 1 F L S I T_{LSI} = \frac{1}{F_{LSI}} TLSI=FLSI1

图片消失了

2.窗口看门狗

图片消失了

WWDG工作特性:

  1. 递减计数器T[6:0]的值小于0x40时,WWDG产生复位
  2. 递减计数器T[6:0]在窗口W[6:0]外被重新装载时,WWDG产生复位(不能过早喂狗)
  3. 递减计数器T[6:0]等于0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位(可以保存重要数据等)
  4. 定期写入WWDG_CR寄存器(喂狗)以避免WWDG复位
图片消失了

WWDG超时时间:
超时时间: T W W D G = T P C L K 1 × 4096 × W D G T B 预分频系数 × ( T [ 5 : 0 ] + 1 ) T_{WWDG} = T_{PCLK1} \times 4096 \times WDGTB预分频系数 \times (T[5:0] + 1) TWWDG=TPCLK1×4096×WDGTB预分频系数×(T[5:0]+1)
窗口时间: T W I N = T P C L K 1 × 4096 × W D G T B 预分频系数 × ( T [ 5 : 0 ] − W [ 5 : 0 ] ) T_{WIN} = T_{PCLK1} \times 4096 \times WDGTB预分频系数 \times (T[5:0] - W[5:0]) TWIN=TPCLK1×4096×WDGTB预分频系数×(T[5:0]W[5:0])
其中: T P C L K 1 = 1 F P C L K 1 T_{PCLK1} = \frac{1}{F_{PCLK1}} TPCLK1=FPCLK11

图片消失了

IWDG和WWDG对比:

图片消失了

3.独立看门狗的代码

看门狗的代码较为简单,所以就直接放在main.c里面

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"int main(void)
{/*模块初始化*/OLED_Init();      //OLED初始化Key_Init();       //按键初始化/*显示静态字符串*/OLED_ShowString(1, 1, "IWDG TEST");/*判断复位信号来源*/if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET) //如果是独立看门狗复位{OLED_ShowString(2, 1, "IWDGRST");   //OLED闪烁IWDGRST字符串Delay_ms(500);OLED_ShowString(2, 1, "       ");Delay_ms(100);RCC_ClearFlag();       //清除标志位}else           //否则,即为其他复位{OLED_ShowString(3, 1, "RST");    //OLED闪烁RST字符串Delay_ms(500);OLED_ShowString(3, 1, "   ");Delay_ms(100);}/*IWDG初始化*/IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //独立看门狗写使能IWDG_SetPrescaler(IWDG_Prescaler_16);   //设置预分频为16IWDG_SetReload(2499);       //设置重装值为2499,独立看门狗的超时时间为1000msIWDG_ReloadCounter();       //重装计数器,喂狗IWDG_Enable();         //独立看门狗使能while (1){Key_GetNum();        //调用阻塞式的按键扫描函数,模拟主循环卡死IWDG_ReloadCounter();      //重装计数器,喂狗OLED_ShowString(4, 1, "FEED");    //OLED闪烁FEED字符串Delay_ms(200);        //喂狗间隔为200+600=800msOLED_ShowString(4, 1, "    ");Delay_ms(600);}
}

4.窗口看门狗

窗口看门狗的代码与独立看门狗的代码相似,最大区别是窗口看门狗需要使能APB1的WWDG时钟

// 省略的代码与独立看门狗相同RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //开启WWDG的时钟/*WWDG初始化*/WWDG_SetPrescaler(WWDG_Prescaler_8);   //设置预分频为8WWDG_SetWindowValue(0x40 | 21);     //设置窗口值,窗口时间为30msWWDG_Enable(0x40 | 54);       //使能并第一次喂狗,超时时间为50mswhile (1){Key_GetNum();        //调用阻塞式的按键扫描函数,模拟主循环卡死OLED_ShowString(4, 1, "FEED");    //OLED闪烁FEED字符串Delay_ms(20);        //喂狗间隔为20+20=40msOLED_ShowString(4, 1, "    ");Delay_ms(20);WWDG_SetCounter(0x40 | 54);     //重装计数器,喂狗}

五.闪存

1.内部flash

stm32f1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
读写FLASH的用途:

  1. 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
  2. 通过在程序中编程(IAP),实现程序的自我更新

在线编程(ICP):用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
在程序中编程(IAP)可以使用微控制器支持的任一种通信接口下载程序

闪存模块组织:

图片消失了

FLASH基本结构:

图片消失了

FLASH解锁:
FPEC共有三个键值:

  1. RDPRT = 0x00000045
  2. KEY1 = 0x45670123
  3. KEY2 = 0xCDEF89AB

解锁:

  1. 复位后,FPEC被保护,不能写入FLASH_CR
  2. 在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
  3. 错误的操作序列会在下次复位前锁死FPEC和FLASH_CR

加锁:设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

使用指针访问存储器:

#define __IO volatile
//volatile的作用是防止编译器优化
uint16_t Data = *((__IO uint16_t *)(0x08000000));

使用指针写指定地址下的存储器:

*((__IO uint16_t *)(0x08000000)) = 0x1234;

程序存储器编程:

图片消失了

程序存储器页擦除:

图片消失了

程序存储器全擦除:

图片消失了 图片消失了

选项字节编程:

  1. 检查FLASH_SR的BSY位,以确认没有其它正在进行的编程操作
  2. 解锁FLASH_CR的OPTWRE位
  3. 设置FLASH_CR的OPTPG位为1
  4. 写入要编程的半字到指定的地址
  5. 等待BSY位变为0
  6. 读出写入的地址并验证数据

选项字节擦除:

  1. 检查FLASH_SR的BSY位,以确认没有其它正在进行的闪存操作
  2. 解锁FLASH_CR的OPTWRE位
  3. 设置FLASH_CR的OPTER位为1
  4. 设置FLASH_CR的STAT位为1
  5. 等待BSY位变为0
  6. 读出被擦除的选择字节并做验证

器件电子签名:电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名
闪存容量存储器:基地址:0x1FFF F7E0,大小:16位
产品唯一身份标识寄存器:基地址:0x1FFF F7E8,大小:96位


http://www.ppmy.cn/server/176077.html

相关文章

C++ STL list

文章目录 1. std::list 的特性2. 包含头文件3. 声明和初始化 std::list4. 常用成员函数4.1 元素访问4.2 容量4.3 修改容器4.4 迭代器4.5 其他常用函数 5. 使用示例6. 注意事项7. 与其他容器的比较总结 在 C 标准模板库&#xff08;STL&#xff09;中&#xff0c;std::list 是一…

qt介绍图表 charts 一

qt chartsj基于Q的Graphics View框架&#xff0c;其核心组件是QChartView和QChart.QChartView是一个显示图表的独立部件&#xff0c;基类为QGraphicsView.QChar类管理图表的序列&#xff0c;图例和轴示意图。 绘制一个cos和sin曲线图&#xff0c;效果如下 实现代码 #include…

单臂路由+VLANIF

涉及的具体实验步骤在&#xff1a;三层交换机实现路由功能_三层交换机打开路由功能-CSDN博客 1.技术背景&#xff1a;VLAN间通信 传统交换二层组网中&#xff0c;默认所有网络都处于同一个广播域&#xff0c;这带了诸多问题。VLAN&#xff08;Virtual Local Area Network&…

浅谈StarRocks数据库简介及应用

StarRocks是一款高性能的实时分析型数据库&#xff0c;专为复杂的SQL查询提供极高的性能&#xff0c;尤其适用于数据分析场景。它是一款开源的新一代极速全场景MPP&#xff08;Massively Parallel Processing&#xff0c;大规模并行处理&#xff09;数据库&#xff0c;致力于构…

3ds Max 导入到 After Effects 还原摄像机要注意事项--deepseek

我&#xff1a;dp我这有两个脚本分别是syn软件相机导出到max的和syn软件相机导出到ae的&#xff0c;你能看出差别来吗&#xff1f;如果我想把max里的相机导入到ae里&#xff0c;保持原来的位置方向&#xff0c;该怎么做 dp&#xff1a;从这两个脚本可以看出&#xff0c;3ds Ma…

set详讲(C++)

目录 1. 前言 2. 预备知识 2.1 关联式容器 2.2 键值对 3. set详解 3.1 set是什么 3.2 set模板参数列表 3.3 set构造 3.4 set的使用 3.4.1 insert 3.4.2 find 3.4.3 erase 3.4.4 swap 3.4.5 empty 3.4.6 size 3.4.7 count 3.4.8 lower_bound 3.8.9 upper_bound…

区块链加密技术公司DApp开发指南:从零开始到上线

随着区块链技术的普及&#xff0c;去中心化应用&#xff08;DApp&#xff09;成为加密技术公司探索的核心领域。本文结合行业实践与最新技术趋势&#xff0c;系统梳理DApp从需求分析到上线的完整开发流程&#xff0c;并融入关键工具、安全策略与案例解析&#xff0c;助力企业高…

C语言及内核开发中的回调机制与设计模式分析

在C语言以及操作系统内核开发中,回调机制是一种至关重要的编程模式。它通过注册框架和定义回调函数,实现了模块间的解耦和灵活交互,为系统的扩展性和可维护性提供了有力支持。本文将深入探讨这种机制的工作原理、应用场景以及与设计模式的关联。 一、回调机制的核心概念 (…