免责声明
提示:写本文章的缘由:本人在秋招时复习STM32有关的知识点,便顺势记录下来。本文章的知识均属于各大论坛的大佬回答,其中也有我的一些补充,本文主要以自己对STM32的理解作为框架,并积极整理各个大佬的文章,因此属于借花献佛,也不存在任何牟利,分享的初衷是便于小伙伴们的求职和学习。
文章目录
# 前言 借花献佛,不存在牟利,希望共勉和得到大家的支持。
一.MCU启动过程:
上电——>主时钟起振——>启动代码——>用户程序(main函数)。
MCU上电(复位)时,从固定的地址启动,一般是地址0x00000000,如ARM7;个别特殊的如STM32默认启动地址为0x8000000(flash区启动)。启动过程主要完成两部分工作,一个是硬件执行环境,如中断向量表、寄存器、看门狗等,另一个是软件环境,如C库环境、ZI(未初始化的内存变量)等。
硬件环境工作:
a.初始时钟:
初始化内核时钟,主时钟,各个外设的时钟。
b.关闭看门狗
看门狗是用来监控程序的异常跑飞而复位CPU,在初始化阶段,由于没有“喂狗”这一动作,有可能导致CPU不断复位,因此,首先会关闭看门狗,初始化完,再开启。看门狗是用来监控程序的异常跑飞而复位CPU,在初始化阶段,由于没有“喂狗”这一动作,有可能导致CPU不断复位,因此,首先会关闭看门狗,初始化完,再开启。
c.建立中断向量表
中断向量表,中断源的识别标志,可用来形成相应的中断服务程序的入口地址,或者中断服务程序入口地址的偏移量和段基值。CPU利用中断向量表转入中断服务程序处理相关事务。
d.初始化堆栈寄存器
堆栈的作用一个就是保存现场,如函数调用或者中断发送时,将当前执行地址压栈,调用完成再返回此处执行程序。另一个作用就是保存参数,如临时变量。因此,在启动阶段需初始化堆栈寄存器、堆栈的大小、起始地址等。
e.内存初始化
选择内部或者外部RAM。
软件环境工作
1.把RO,RW从它们的加载域复制到它们的运行域中去。
2.初始化(清零)ZI域。
3.初始化堆栈指针
4.初始化C库环境,包括C库所需的内存空间、程序执行所需资源、C库初始化。
*STM32启动方式:
1、 BOOT1=1 BOOT0=1
中断向量表定位于SRAM区,即起始地址为0x2000000,同时复位后PC指针位于0x2000000处。
2、 BOOT1=x BOOT0=0
中断向量表定位于FLASH区,即起始地址为0x8000000,同时复位后PC指针位于0x8000000处。
3、 BOOT1=0 BOOT0=1
中断向量表定位于内置Bootloader区,此时可通过串口下载程序的二进制文件到flash区。
二.GPIO基础知识(工作:2-3.6V,识别:-0.3-+1.16V,输出25ma)
组成:保护二极管、上下拉电阻、施密特触发器(整形成方波),P/Nmos(通过vgs实现导通和关闭)
2.1.GPIO的八种模式
输入:
1.外部上拉:空闲时,io口呈现高电平
2.外部下拉:空闲时,io口呈现低电平
3.浮空输入:空闲时io口状态不确定;一般用于读取数据
4.模拟输入:ADC/DAC
输出:
1.开漏输出:通常用于IIC
2.推挽输出:可以输出高低电平,驱动能力较强
3.开漏复用
4.推挽复用功能
2.2.GPIO的使用
1.使能时钟:RCC APB2RSTR寄存器 HAL_RCC_GPIOC_CLK ENABLE()
2.设置工作模式
3.设置输出状态:ODR,BSRR
三.Systick滴答定时器
SysTick 为一个 24 位递减计数器,SysTick 设定初值并使能后,每经过 1 个系统时钟周期,计数值就减 1。计数到 0 时,SysTick 计数器自动重装初值并继续计数,同时内部的COUNTFLAG 标志会置位,触发中断 (如果中断使能情况下)。
组成:
STM32 中的 Systick 部分内容属于 NVIC 控制部分,一共有 4 个寄存器,名称和地址分别是:
STK_CSR 0xE000E010 – 控制寄存器
STK_LOAD 0xE000E014 – 重载寄存器
STK_VAL 0xE000E018 – 当前值寄存器
STK_CALRB 0xE000E01C – 校准值寄存器
四.中断NIVC
CM3支持256个中断,16个内核中断,240个外部中断,256级可编程中断设置。
STM32的中断向量有抢占属性和响应属性,属性编号越小,优先级别越高。
抢占属性是打断其他中断的属性,有这个属性就有嵌套中断。由库函数NVIC_IRQChannelPreemptionPriority() 进行参数配置。
响应属性应用在抢占属性相同时(前提),两个中断向量的抢占优先级相同时,如果同时到达,则优先处理响应优先级高的中断。由库函数NVIC_IRQChannelSubPriority() 进行参数配置。
4.1库函数配置(中断函数在misc.c)
1.中断优先级分组函数 NVIC_PriorityGroupConfig()。此函数在系统工程中只能被调用一次!一旦确定好中断分组就不能更改。
补充:
assert_param(),断言机制函数,在stm32f10x_conf.h文件中可以看到 assert_param() 是一个宏定义,作用就是检测传递给函数的参数是否是有效的参数,返回值是 0假 1真。
2.中断初始化函数
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
typedef struct
{
uint8_t NVIC_IRQChannel; //初始化的中断对象,例如USART1_IRQn
uint8_t NVIC_IRQChannelPreemptionPriority;//定义这个中断的抢占优先级别,级别数不超过2的“抢占优先级位数”次方。(一个是位数,一个是级别数)
uint8_t NVIC_IRQChannelSubPriority; //中断的响应优先级别,级别数不超过2的“响应优先级位数”次方。
FunctionalState NVIC_IRQChannelCmd; //该中断是否使能
} NVIC_InitTypeDef;
4.2中断初始化配置示例
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 响应优先级为 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure);
五.STM32 外部IO的中断
5.1 文件: stm32f10x_exti.c/h
每个 IO 口都可以作为外部中断的中断输入口,支持19个外部中断/事件请求,每个中断设有状态位、独立的触发/屏蔽设置。
9个外部中断为:
线 0-15:对应外部 IO 口的输入中断 (供 IO 口使用的中断线)
线 16 :连接到PVD输出(Programmable Votage Detector,可编程电压监测器)
线 17 :连接到RTC闹钟事件
线 18 :连接到USB唤醒事件
映射方式:
每个中断线对应最多7个IO口,但每次只能连接到一个IO口上,利用选择器的配置来决定中断线配置到哪个IO口上去。
5.2 使用 IO 口外部中断的一般步骤
1.初始化 IO 口为输入。
2.开启 IO 口复用时钟,设置 IO 口与中断线的映射关系。
3.初始化线上中断(EXTI),设置触发条件等。
4.配置中断分组(NVIC),并使能中断。
5.编写中断服务函数。
5.3 库函数配置
- 映射关系配置 GPIO_EXTILineConfig()
外部中断端口映射配置示例 (GPIOE_2 与 EXTI_2):
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
- 中断初始化 EXTI_Init()
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //结构体函数参数
typedef struct
{
uint32_t EXTI_Line; //中断线标号,EXTI_Line0~EXTI_Line15
EXTIMode_TypeDef EXTI_Mode; //中断模式,中断 EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event
EXTITrigger_TypeDef EXTI_Trigger; //触发方式,下降沿触发 EXTI_Trigger_Falling,上升沿触发 EXTI_Trigger_Rising,任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling
FunctionalState EXTI_LineCmd; //使能,ENABLE
}EXTI_InitTypeDef;
- 设置中断线2的中断优先级
- 中断服务函数
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)!=RESET)
//判断某个线上的中断是否发生
{
中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line2);
//清除 LINE 上的中断标志位
}
}
抢占优先级可以理解为多个中断共存的时候抢占优先级高的中断可以打断正在运行的中断抢先运行,决定一个中断是否先运行首先看的是抢占优先级,抢占优先级高则先运行该中断,当抢占优先级一样的时候才会看响应优先级,此时响应优先级高的中断先运行,(数字越小则优先级越高)
六.Time定时器
6.1 定时器种类
6.2 通用定时器,通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器
1.组成:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动装载寄存器(TIMx_ARR)
2.计数器模式:
向上计数模式、向下计数模式、中央对齐模式(向上/向下计数)
3.时钟选择
内部时钟(TIMx_CLK)
外部时钟模式1:外部捕捉比较引脚(TIx)
外部时钟模式2:外部引脚输入(TIMx_ETR)
内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
从图中可以看出:由AHB时钟经过APB1预分频系数转至APB1时钟,再通过倍频器转至TIMxCLK时钟(即内部时钟CK_INT、CK_PSC),最终经过PSC预分频系数转至CK_CNT。
当APB1的预分频器系数为1时,这个倍频器就不起作用了,定时器的时钟频率等于APB1的频率;
当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1时钟频率的两倍。
6.3 涉及到的time计算
默认情况下:
AHB总线时钟——STM32F103是72MHz,APB1时钟=36M。
所以APB1的分频系数=AHB/APB1时钟=2。
通用定时器时钟CK_INT=2*36M=72M。
最终经过PSC预分频系数转至CK_CNT。
6.4 有关寄存器的配置
计数器当前值寄存器(TIMx_CNT)、预分频寄存器(TIMx_PSC)、自动重装载寄存器(TIMx_ARR)、控制寄存器(TIMx_CR1)、DMA/中断使能寄存器(TIMx_DIER)
相关配置库函数:
一个初始化函数:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//作用:用于对预分频系数、计数方式、自动重装载计数值、时钟分频因子等参数的设置。
2个使能函数
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); //使能定时器
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); //使能定时器中断
4个状态标志位获取函数
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);//获取状态标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);//清除状态标志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);//获取中断状态标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);//清除中断状态标志位
6.5 具体步骤
使能定时器时钟。调用函数:RCC_APB1PeriphClockCmd();
初始化定时器,配置ARR、PSC。调用函数:TIM_TimeBaseInit();
开启定时器中断,配置NVIC。调用函数:void TIM_ITConfig();NVIC_Init();
使能定时器。调用函数:TIM_Cmd();
编写中断服务函数。调用函数:TIMx_IRQHandler()。
七.看门狗
7.1 看门狗概念
看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就回给出一个复位信号到MCU,是MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。
看门狗主要是用来监测单片机运行状态和解决程序引起的故障的模块。根据功能不同,通常分为独立看门狗和窗口看门狗。因为独立看门狗受工作场合因素的影响会多一点,仅适用于一些对时间精度要求较低的场合;窗口看门狗适用于对时间精度高的场合。
看门狗使系统复位:在看门狗函数中设置一个函数,每间隔一段时间发送给CPU一次复位信号,CPU进行复位操作。如果系统正常运行不想让系统产生复位怎么办?需要给看门狗函数设置一个定时计数器,计数器开始计数,初始值减小,判断初始值是否为0,如果为0,给CPU发送复位信号,反之,计数器重置重新计数,这个过程称为清狗操作。如果计数器为0,CPU复位,计数器计数的过程称为喂狗,当没有东西可以喂狗时,CPU就会收到复位信号,系统复位重新运行。
7.2 看门狗流程
7.3 硬看门狗、软看门狗
硬看门狗独立于CPU之外,由独立的时钟驱动。计数器初始值及外部时钟驱动频率由器件本身决定,外部无法尽心更改,硬看门狗输出信号引脚和CPU上的RESET引脚连接,使CPU可以产生复位操作。软看门狗使CPU内置的一个计数器模块,时钟频率由CPU本身决定,计数器初值由软件进行设置,超时时间可以在一定范围内变化。因为软看门狗位于处理器内部,需要产生CPU可以识别的信号,通过中断或异常,引起系统进行重启。
7.4 看门狗、定时器以及计数器的关系
1.看门狗本质和定时器差别不大,区别在于不仅可以产生中断,还可以使CPU系统复位;
2.定时器计时通过计数实现,定时器内部有一个计数器。计数器根据时钟来工作。时钟频率来源于ARM上的APB总线,经过时钟模块的分频器分频计算得出。
3.RTC作为定时时钟和定时器的区别在于:定时器好似与闹钟,RTC好似钟表。
八.低功耗和RTC
8.1 STM32F10x系列低功耗模式
STM32 的低功耗模式有 3种: 1)睡眠模式(CM3内核停止,外设仍然运行) 2)停止模式(所有时钟都停止) 3)待机(standby)模式(1.8V内核电源关闭) 从待机模式唤醒后的代码执行等同于复位后的执行 进入Standby模式后,只能有Wake-up脚和RTC唤醒,特别是唤醒后,程序将从最开始运行,也就是相当于软件复位。不同系列的会略有不同,一般新出的芯片功能会增加一些。
STM32L系列有五种低功耗模式,分别是standby,stop,sleep,low power sleep,low power run.
1.standby模式下功耗最低,实测电流为1uA,此种模式下数据不保存。
2.stop模式实测功耗为1.3uA,此种模式下数据保存。
3.sleep模式下功耗为1mA左右,主要受频率,温度等影响,功耗发生变化。
另外,mcu在运行模式(run)下也可以通过以下方式降低功耗:降低系统时钟频率。关闭一些不使用的外设的时钟。
8.2降低功耗的方式:run、sleep、stop、standby
run模式下:
1、Slowing down system clocks(run模式下降低功耗)
run模式下,我们可以通过对预分频器编程来降低系统时钟(SYSCLK, HCLK, PCLK1, PCLK2);在进入sleep模式之前,我们也可以对预分频器编程来降低外设时钟。详细请查阅RCC_CFGR寄存器,这里不展开描述。
2、Peripheral clock gating(外围时钟门控)
在Run模式下,每个外设和存储器的HCLK和PCLKx可以随时停止,以降低功耗。在sleep模式下,在执行WFI或者WFE内核指令之前我们可以关闭一些不需要使用的外设时钟来进一步降低功耗。
低功耗模式下(分为sleep、stop、standby)
1.sleep mode
进入sleep模式:通过执行WFI (Wait For Interrupt)或WFE (Wait For Event)指令进入Sleep模式。
退出sleep模式:如果使用WFI指令进入睡眠模式,任何被NVIC确认的外设中断都可以从睡眠模式唤醒设备。如果使用WFE指令进入Sleep模式,则在事件发生时,MCU将立即退出Sleep模式。唤醒事件可以通过以下任意一种方式产生:
在外设控制寄存器中启用中断,但在NVIC中不启用,并在Cortex®-M3系统控制寄存器中启用SEVONPEND位。当MCU从WFE恢复时,必须清除外设中断悬挂位(pending)和外设NVIC IRQ通道悬挂位(pending)(在NVIC中断清除悬挂寄存器中)。
在事件模式下配置外部或内部EXTI line。当CPU从WFE恢复时,由于事件行对应的挂起位没有设置,不需要清除外设中断挂起位或NVIC IRQ通道挂起位。
2.stop mode
停止模式是基于Cortex®-M3深度睡眠模式结合外围时钟门控。在停止模式下,1.8 V域中的所有时钟都停止,锁相环、HSI和HSE RC振荡器都被禁用。SRAM和寄存器的内容被保留。
进入停止模式:
为了进一步减少功耗,可以设置电压调节器工作在低功耗模式,这需要配置PWR_CR的LPDS位。
如果闪存编程正在进行,停止模式的进入将被延迟,直到内存访问完成。
如果对APB域的访问正在进行,停止模式的进入将延迟到对APB的访问完成。
退出停止模式:
当通过发出中断或唤醒事件退出Stop模式时,HSI RC振荡器被选为系统时钟。
当电压调节器在低功率模式下运行时,当从停止模式唤醒时,会产生额外的启动延迟。通过在停止模式期间保持内部调节器(这里是指电压调节器)处于ON状态,虽然减少了启动时间,但消耗更高。进入停止模式之前,要清除所有中断挂起位、唤醒之后记得要重新配置系统时钟。
3.Standby mode
“待机”模式可以实现最低的功耗。它基于Cortex®-M3深度睡眠模式,禁用电压调节器。导致1.8 V域下电。锁相环、HSI振荡器和HSE振荡器也处于关闭状态。除了备份域和备用电路中的寄存器外,SRAM和寄存器的内容都会丢失。当外部复位(NRST引脚)、IWDG复位、WKUP引脚上升沿或RTC告警上升沿发生时,微控制器退出待机模式。除电源控制/状态寄存器(PWR_CSR)外,所有寄存器都在Standby唤醒后重置。
8.3 RTC基础知识
RTC (Real Time Clock):实时时钟
RTC是个独立的定时器。RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。
九.串口
9.1 串口通信基础
串口为并行通信和串行通信,如今基本都采用串行通信,按照是否有同步时钟和收发间的数据传输方向分成两大类;其中,按照是否有同步时钟分成同步和异步:
同步通信:带时钟同步信号,发送方和接收方在同一时钟的控制下,实现同步传输。
异步通信:不带时钟同步信号,使用各自的时钟控制。 但需要双方相互约定好数据传输速率。
传输速率的衡量方式——波特率—单位:bps(位/秒)
按照数据传输方向分成单工、半双工和全工:
单工通信:数据只沿着一个方向传输,只需要一根数据线。
半双工通信:数据可以沿着两个方向传输,但不能同时进行,需要两根数据线。
全工通信:数据可以沿着两个方向传输,可以同时进行,需要两根数据线。
传输速率的衡量方式——波特率—单位:bps(位/秒)
按照数据传输方向分成单工、半双工和全工:
单工通信:数据只沿着一个方向传输,只需要一根数据线。
半双工通信:数据可以沿着两个方向传输,但不能同时进行,需要两根数据线。
全工通信:数据可以沿着两个方向传输,可以同时进行,需要两根数据线。
9.2 常见的串行通信接口简介
对于以上常见的四种接口,可以有两种方式的分类:
一是按照是否有同步时钟,同步通信:SPI 和 I2C;异步通信:UART 和 1-wire
二是按照数据传输方向,全双工:UART 和 SPI;半双工:1-wire 和 I2C
十.文件下载
由于最近工作比较忙,可能来不及更新,可以点下关注私聊我,我给小伙伴们发完整版的面试经。