单片机的那些事
参考文章
STM32系列
对于STM32来说,复位通常分为三种类型:系统复位、电源复位和备份域复位
- 系统复位
除了RCC的复位标志和备份域中的寄存器外,系统复位会将其它全部寄存器都复位为复位值。
产生系统复位事件:
※
NRST 引脚低电平※
窗口看门狗计数结束※
独立看门狗计数结束※
软件复位※
低功耗管理复位
- 电源复位
除备份域内的寄存器以外,电源复位会将其它全部寄存器设置为复位值。
产生电源复位条件:
※
上电/掉电复位或欠压复位※
在退出待机模式时
- 引起异常复位的原因
原因一:NRST引脚电平被拉低引起复位
有些特殊环境,特别是大型工厂,外界或内部会使电源产生干扰信号,使STM32的NRST引脚电平被拉低,从而导致系统复位。
分析原因:NRST引脚电平拉低20us就会引起系统复位,电源上一个纹波,或者外部静电都会引起电源被拉低20us。
解决办法:电源滤波、使用隔离电源、添加屏蔽措施等。
原因二:欠压引起复位
有些产品在设计之初没有综合计算负载(与STM32同电源),因负载过大,使其欠压,从而导致复位。
分析原因:STM32除了上电和掉电复位之外,绝大部分STM32还有一个欠压复位,当电源电压 (VDD) 降至所选 VBOR 阈值以下时,芯片将复位。
解决办法:选择负载更大的电源、通过软件配置合理的欠压值VBOR。
原因三:数字、模拟电源地压差引起复位
有工程师将VSS 和 VSSA之间使用一个几欧,甚至几十欧的电阻连接,有时候(有大电流经过地线)就会因为电源地的压差导致芯片(电源)复位。
分析原因:我们比较关注 VDD 和 VDDA 的关系,但忽略了 VSSA 和 VSS 压差需要小于 50mV这一点(具体可以看数据手册)。如果有大电流的情况,则会引起电源地存在压差。
解决办法:尽量使用完全连接地的方式处理,比如0欧电阻,或者隔离电源。
原因四:看门狗超时喂狗引起复位
有不少工程师设计低功耗产品时,使用了看门狗,但是他们往往忘记了芯片睡眠模式不能停止喂狗,从而导致看门狗复位。
分析原因:STM32进入睡眠之后,看门狗依然继续在工作,如果不及时喂狗,芯片会产生看门狗复位。
解决办法:进入睡眠之前设置更长的喂狗时间,同时不定期唤醒芯片进行喂狗。
- 关于推挽输出
灌电流(Sink Current):电流方向从外部流入到MCU内部
拉电流(Source Current):电流方向从MCU内部流向外部
推挽输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度
当Vin电压为V+时,上面的N型三极管控制端有电流输入,Q3导通,Q4截止,于是电流从上往下通过,提供电流给负载。经过上面的N型三极管提供电流给负载(Rload),这就叫「推」。
当Vin电压为V-时,下面的三极管有电流流出,Q4导通,Q3截止,于是电流从上往下流过。经过下面的P型三极管提供电流给负载(Rload),这就叫「挽」。
谈谈中断优先级问题
两个中断优先级相同情况下,都是假定中断请求在时间上错开了的情况。如果二者同时到达,那CPU先响应哪一个呢?就看二者在中断矢量表的序号,谁的序号小就先响应谁
所以有时候串口丢包啥的有可能就是中断优先级设置问题
当在系统里开启多个中断事件时,要合理安排各中断源的优先级,有些时候可能还需精心安排
CubeMX配置里在配置引脚时,设置了某功能后按住
CTRL+鼠标左键,然后鼠标往外拖,就可以看到这个功能的其他备用引脚在闪烁了
时钟:
STM32工作电压
CAP引脚
- 未使用的GPIO端口的处理
从目前一些工程师反应的意见来看,有如下几个解决方案:
(1) 悬空,不做任何处理(不推荐);
(2) 将GPIO设置为输出方式(据说,可以抵挡外来的干扰,因干扰是输入类型的);
(3) 将使用的GPIO通过上拉或下拉电阻接电源或地(上拉或下拉电阻一般为10K欧姆)。
在Keil里定义变量没效果啥的,大概率程序没问题就是代码被优化了,在魔法棒那选择不优化那个选项即可
GPIO的八种模式
注意
GPIO的引脚速度与和应用匹配(
1M表示1024Kb,9600波特率大概1.2Kb)
- 对于串口,如果最大波特率只需
115200,那么2M的GPIO引脚速度就够了,既省电又降噪- 对于I2C,假设使用
400KHz波特率,若想把余量留大些,那么用2M的或许不够,此时可选择10M- 对于SPI,假设使用
18M或者9M波特率,用10M明显不够,此时可选择50M的GPIO引脚速度
所有端口均有外部中断能力,为了使用外部中断线,端口必须配置成
输入模式一般上下拉电阻的阻值都在
30-50K之间。这样可以增强MCU的抗干扰能力芯片
内部上/下拉电阻不影响GPIO输出模式,这个只是对输入模式有用必须以
字(32位)的方式操作GPIO外设寄存器
【STM32F407VE数据手册】
![]()
【STM32F407VE数据手册】
例如STM32F407的IO端口驱动电流最大是
25mA
名词解释
- 输入数据寄存器(IDR)
- 输出数据寄存器(ODR)
- 位设置/清除寄存器(BSRR)
- VDD: 能够容忍3.3V电压,最大3.6V(
一般VDD<VCC) - VCC: 一定是5V
- VSS:公共连接,通常指电路公共接地端电压
框图
总体框图
两个二极管起保护作用:
当引脚电压高于VDD 时,上方的二极管导通(防止过高电压进入芯片内部烧坏芯片),当引脚电压低于VSS 时,下方的二极管导通(防止电压过低,从芯片内部汲取电流),虽有这样的保护,但不能驱动大功率器件,如直接驱动电机,电机堵转的反向电流会烧毁芯片
输出驱动器:
两个MOS管,一个PMOS,一个NMOS,同一时间只能一个管子导通
PMOS管导通,NMOS管截止,那么输出的就是高电平
PMOS管截止,NMOS管导通,那么I/O引脚就被拉低了,就是低电平
TTL肖特基触发器:
主要作用是存储和放大数字信号,具有滤波的作用,就是让通过的电平输出稳定的高低电平
信号经过触发器后,模拟信号转化为 0和1 的数字信号。但是,当 GPIO 引脚作为 ADC 采集电压的输入通道时,用其 “模拟输入” 功能,此时信号不再经过触发器进行 TTL 电平转换。 ADC 外设要采集到的原始的模拟信号
浮空输入
浮空输入模式下, I/O 端口的电平信号直接进入输入数据寄存器。也就是说, I/O 的电平状态是不确定的,完全由外部输入决定
上拉输入
上拉输入模式下, I/O 端口的电平信号直接进入输入数据寄存器。但是在 I/O 端口悬空(在无信号输入)的情况下,输入端的电平可以保持在高电平;并且在 I/O 端口输入为低电平的时候,输入端的电平也还是低电平
下拉输入
下拉输入模式下, I/O 端口的电平信号直接进入输入数据寄存器。但是在 I/O 端口悬空(在无信号输入)的情况下,输入端的电平可以保持在低电平;并且在 I/O 端口输入为高电平的时候,输入端的电平也还是高电平
模拟输入
模拟输入模式下, I/O 端口的模拟信号(电压信号,而非电平信号)直接模拟输入到片上外设模块,比如 ADC 模块等等,不会经过施密特触发器
开漏输出
除了模拟输入的这种模式会关闭数字输入功能其他七种模式都可以通过输入寄存器读取
I/O状态,例:在模拟I2C实验中把GPIO的工作模式配置为开漏输出时同时也可以读取引脚电平状态
当输出寄存器输出高电平,则引脚输出高阻态
![]()
当输出寄存器输出低电平,则引脚输出低电平
![]()
推挽输出
什么是推挽结构和推挽电路?
推挽电路是 两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务。电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度
开漏输出和推挽输出的区别?
开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极。适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内);
推挽输出:可以输出强高、低电平,连接数字器件
- 推挽输出的驱动能力与芯片的IO电流特性有关
当输出寄存器输出高电平,则引脚也输出高电平
![]()
当输出寄存器输出低电平,则引脚也输出低电平
![]()
复用推挽/开漏输出
复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式, 但是 输出信号源于其它外设
输出数据寄存器 GPIOx_ODR 无效;输入可用,通过输入数据寄存器可获取 I/O 实际状态,但一般直接用外设的寄存器来获取该数据信号
这里我们用串口举例:
![]()
![]()
总结:用功能的推挽与开漏输出与普通的推挽与开漏输出只是输出的寄存器换成外设的寄存器
作为片上外设(USART、I2C、SPI等)专用引脚,即一个引脚有多种用途但同一时刻一个引脚只能使用复用功能中的一个;
I2C---复用开漏输出 PWM/USART---复用推挽输出
F1和F4的初始化区别
F1系列
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//结构体初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //LED0-->PA.1 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.1
GPIO_SetBits(GPIOA,GPIO_Pin_1); //PA.1 输出高
}F4系列
void GPIO_Init(void)
{
//声明一个GPIO结构体变量
GPIO_InitTypeDef GPIO_InitStructure;
delay_init(84);
//使能GPIO所在的总线的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//定义该结构体
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; //使用的IO口 (总共有16个IO口)
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //设置IO的模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉
//初始化该结构体
GPIO_Init(GPIOA,&GPIO_InitStructure);
}时钟树讲解
什么是时钟?
时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。 时钟系统就是CPU的脉搏,决定cpu速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC),时钟的重要性不言而喻。
为什么 STM32 要有多个时钟源呢?
STM32本身十分复杂,外设非常多,但我们实际使用的时候只会用到有限的几个外设, 使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速, 如果都用高速时钟,势必造成浪费 , 同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。所以便有了STM32的 时钟系统和时钟树
总结:
① STM32时钟系统主要的目的就是 给相对独立的外设模块提供时钟,也是为了 降低整个芯片的耗能
② 系统时钟是处理器运行时间基准(每一条机器指令一个时钟周期)
③ 时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令
④ 一个单片机内 提供多个不同的系统时钟,可以适应更多的应用场合
⑤ 不同的功能模块会有不同的时钟上限,因此提供不同的时钟,也能在一个单片机内放置更多的功能模块。 对不同模块的时钟增加开启和关闭功能,可以降低单片机的功耗
⑥ STM32为了低功耗,他将所有的外设时钟都设置为 Disable(不使能),用到什么外设,只要打开对应外设的时钟就可以, 其他的没用到的可以还是 Disable(不使能),这样耗能就会减少。 这就是为什么不管你配置什么功能都需要先打开对应的时钟的原因
F1系列时钟图:
从左到右可以简单理解为: 各个时钟源--->系统时钟来源的设置--->各个外设时钟的设置
| 问 | 答 |
|---|---|
| STM32 有4个独立时钟源 | HSI、HSE、LSI、LSE,PLL(严格来说不是独立的) |
| HSI | 高速内部时钟,RC振荡器,频率为 8MHz ,精度不高 |
| HSE | 高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz~16MHz |
| LSI | 低速内部时钟,RC振荡器,频率为 40kHz,提供低功耗时钟 |
| LSE | 低速外部时钟,接频率为 32.768kHz 的石英晶体 |
| 知识点1 | LSI是作为 IWDGCLK(独立看门狗)时钟源和RTC时钟源 而独立使用 |
| 知识点2 | 而 HSI高速内部时钟 HSE高速外部时钟 PLL锁相环时钟 这三个经过分频或者倍频 作为 系统时钟来使用 |
| PLL | PLL为锁相环倍频输出,其时钟输入源可选择为 HSI/2、HSE或HSE/2。倍频可选择为 2~16 倍,但是其输出频率 最大不得超过72MHz。通过倍频之后作为系统时钟的时钟源 |
| 默认的72MHz是怎么来的? | 外部晶振(HSE)提供的 8MHz(与电路板上的晶振的相关)通过PLLXTPRE分频器后,进入PLLSRC选择开关,进而通过PLLMUL锁相环进行 倍频(x9) 后,为系统提供 72MHz的系统时钟(SYSCLK)。之后是AHB预分频器对时钟信号进行分频,然后为低速外设提供时钟8/1*9=72 |
F4系列时钟图:
| 问 | 答 |
|---|---|
| STM32 有4个独立时钟源 | HSI、HSE、LSI、LSE,PLL(严格来说不是独立的) |
| HSI | 高速内部时钟,RC振荡器,频率为 16MHz 可以直接作为系统时钟或者用作PLL输入 |
| HSE | 高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。我们的开发板接的是 25M 的晶振 |
| LSI | 低速内部时钟,RC振荡器,频率为 32kHz,提供低功耗时钟 |
| LSE | 低速外部时钟,接频率为 32.768kHz 的石英晶体 |
例如我们的外部晶振选择25MHz。同时我们设置相应的分频器M=25,倍频器倍频系数N=360,分频器分频系数P=2,那么主PLL生成的第一个输出高速时钟PLLP为:PLL=25MHz * N/ (M*P)=25MHz* 360 /(25*2) = 180MHz |
注意问题汇总
__weak修饰符(弱函数):用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak声明的函数,并且编译器不会报错
如果开发板上STM32采用外部晶振,那么就不能选择
BYPASS Clock Source(旁路时钟源)模式,否则STM32将会工作不正常
HAL库的
延时函数有一个局限性,在中断服务函数中使用HAL_Delay会引起混乱,因为它是通过中断方式实现,而Systick的中断优先级是最低的,所以在中断中运行HAL_Delay会导致延时出现严重误差
GPIO模式配置最大速率时F4系列增多一个
very High(F1没有)
如果发现下载时弹出
Error: Flash Download failed - “Cortex-M4“,说明可能读写保护被打开了,可以试试魔术棒—Debug—Settings— 把verify去掉勾试试;如果不行得去ST官网下载STM32 ST-LINK Utility软件,烧代码时弹出黄色窗口点击确定即可解除读写保护;如不会用此软件见另一篇《STM32物联网入门(1-30)》第4章 (2022/11/6遇到的问题)
F4系列已经去掉了GPIO寄存器BRR了,剩下BSRR
菜鸟电路分析
参考视频:B站郭天祥
简单的直流电路也会暗藏玄机,KCL真好用
问题1:U点电压等于多少?答:流入的电流等于流出电流,通过下面计算出来
问题2:5V高于3.3V,那电流会不会从5V那流到3.3V那?答:其实关注点应该看U点求出的电压,可以看到是2/56V低于3.3V,所以是不会流到3.3V那的,同理如果把5V改成10V,通过上面计算结果此时是3.93V,高于3.3V,此时电流一定会流向3.3V的,但是这是理想中的,现实电路此时3.3V因为电流的倒灌导致损坏(相当于给一个不能充电的电池强行充电一样)
所以看电流是否经过某个元器件,首先需要看这个元器件的两端的电位,还有是否构成回路
还有两点之间有电流流过必然这两点有电势差
可以把两边电路看做两个节点,只有流出没有流入所以ab之间一定没有电流



























