【图解八股-嵌入式】
作者简介和专栏内容见专栏介绍:https://www.nowcoder.com/creation/manager/columnDetail/0eL5bM
麻烦看到贴子的伙伴点点赞大家点赞订阅支持下,提前祝各位offer多多,有问题评论区见~~
图解版
文字版
STM32相关
基础
命名规则
片上资源
英文缩写 |
名称 |
功能 |
NVIC |
嵌套向量中断控制器 |
内核中管理中断,配置中断优先级。 |
SysTick |
系统滴答定时器 |
内核内的定时器,为操作系统(RTOS)提供定时服务。实现delay函数。 |
RCC |
复位和时钟控制 |
为了降低功耗,外设上电的情况下默认没有时钟,不给时钟操作外设无效,需要先使能对应时钟。 |
GPIO |
通用IO口 |
|
AFIO |
复用IO口 |
复用功能端口的重定义,中断端口的配置。 |
EXTI |
外部中断 |
引脚有电平变化时,可以触发中断使用CPU处理。 |
TIM |
定时器 |
定时中断,测频率、生成PWM波形、专用编码器接口 |
ADC |
模数转换器 |
|
DMA |
直接内存访问 |
帮CPU搬运数据 |
USART |
同步/异步串口通信 |
|
I2C |
I2C通信 |
和外设通信,SCL+SDA |
SPI |
SPI通信 |
|
CAN |
CAN通信 |
|
USB |
USB通信 |
|
RTC |
实时时钟 |
年月日时分秒的计时 |
CRC |
CRC校验 |
|
PWR |
电源控制 |
|
BKP |
备份寄存器 |
|
IWDG |
独立看门狗 |
单片机由于电磁干扰死机或程序设计不合理出现死循环时,看门狗可复位程序 |
WWDG |
窗口看门狗 |
|
DAC |
数模转换器 |
ADC逆过程 |
SDIO |
SD卡接口 |
|
FSMC |
可变静态存储控制器 |
|
USB OTG |
USB主机接口 |
系统结构
- Icode总线(加载程序指令)和Dcode总线(加载程序数据)连接flash,flash中放程序
- system总线:连接SRAM(运行时数据),复位时钟等
- AHB总线(先进高性能72MHz):挂载外设 APB(先进外设总线),由于格式速率的差异,中间用桥接 ,APB2速率一般是APB1两倍
- DMA:需要搬数据就发送DMA请求,DMA获得总线控制权进行搬运
项目开始前需要准备什么?
- 安装Keil5 MDK 并注册(盗版)
- 安装器件支持包并在keil中选择:arm芯片型号太多
- 安装STLink/jlink驱动:keil5自带:arm/stlink/usbdriver
- 安装USB转串口驱动:CH340驱动
- stm32F10x固件库放置项目目录:封装了库函数
- stm32启动文件startup_stm32f10x_XX.s:程序从启动文件开始
- system文件system_stm32f10x.c/.h:配置时钟的
- 外设寄存器描述文件stm32f10x.h == 51中的头文件REGX52.H, 寄存器和对应地址
- 内核寄存器描述文件core.cm3
- 库函数:内核misc.c/外设库函数放lib(src和inc文件夹下)
- conf.h, _it.c/_it.h:库函数包含关系,存放中断函数 放user
- 建立不同文件夹放不同类文件
- 在include path中添加各个文件夹路径
- 工程选项define添加宏定义 USE_STDPERIPH_DRIVER
启动文件startup_stm32f10x_XX.s功能
- 初始化堆栈指针 SP;
- 初始化程序计数器指针 PC;
- 设置堆、栈的大小;
- 初始化中断向量表;
- 配置外部 SRAM 作为数据存储器(这个由用户配置,一般的开发板可没有外部SRAM);
- 调用 SystemIni() 函数配置 STM32 的系统时钟。
- 设置 C 库的分支入口“__main”(最终用来调用 main 函数);
工程架构
软件
- 串口调试工具XCOM接收电脑
- FLY_MCU使用USB_TTL通过串口向单片机烧录程序 或直接说用STLink烧录 江协有教程
- 通过串口下载时,启动模式选择 系统存储器,系统存储器存储一段BootLoader程序(接收串口的数据并刷新到主闪存中)
main函数前做了什么
主要完成两部分工作,一个是硬件执行环境,如建立中断向量表、初始化堆栈寄存器、关闭看门狗、初始化内部或者外部RAM等,另一个是软件环境,如加载C库环境、ZI(未初始化的内存变量)、初始化堆栈指针等。
GPIO
GPIO(General Purpose Input Output)通用输入输出口,挂载在APB2外设总线
可配置为8种输入输出模式
引脚电平:0V~3.3V,部分引脚可容忍5V
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
基本结构
- 寄存器有32位(stm32),但端口只有16位,故寄存器只有低16位对应的有端口
- 驱动器负责增大驱动能力
- 施密特触发器:对输入电压进行整型,定上下限对电平进行判断,防止波动
- 复用功能输入:连接到其他需要读取端口的外设
- 输入输出数据寄存器:低16位存数,高16位不用
- 位设置寄存器:对输出数据寄存器对应位置进行修改,不影响其他位。高16位用来位清除,低16位用来位设置。
- 修改还可用位带操作具体地址,但用库函数就是操作上边两个寄存器
GPIO输入输出模式配置
模式名称 |
性质 |
特点 |
浮空输入 |
数字输入 |
若引脚悬空,则电平不确定 |
上拉输入 |
数字输入 |
内部连接上拉电阻,悬空时默认高电平 |
下拉输入 |
数字输入 |
内部连接下拉电阻,悬空时默认低电平 |
模拟输入 |
模拟输入 |
GPIO无效,引脚直接接入内部ADC |
开漏输出 |
数字输出 |
IIC中使用,多机通信下课避免相互干扰 |
推挽输出 |
数字输出 |
高电平接VDD,低电平接VSS |
复用开漏输出 |
数字输出 |
由片上外设控制,高电平为高阻态,低电平接VSS |
复用推挽输出 |
数字输出 |
由片上外设控制,高电平接VDD,低电平接VSS |
上拉和下拉是指在GPIO引脚上连接一个电阻,使其在没有外部信号输入时保持一个确定的电平。上拉电阻连接到引脚和正电源之间,下拉电阻连接到引脚和地之间。当引脚没有外部信号输入时,上拉电阻会将引脚拉高至正电平,下拉电阻则会将其拉低至地。这样可以避免引脚因为没有外部信号输入而产生漂移,保证了引脚的稳定性。
浮空是指GPIO引脚没有连接到任何外部电路时的状态,此时引脚处于一个不确定的电平状态。在实际应用中,为了避免引脚漂移或误操作,通常需要将其设置为上拉或下拉状态。
开漏和推挽是指GPIO引脚输出信号的方式。开漏输出是指引脚输出的信号只能拉低,不能拉高,而推挽输出则可以拉高和拉低。开漏输出通常用于连接需要共用一个电源的多个设备,可以避免电平冲突和干扰,同时还可以实现电平共享。推挽输出则可以提供更强的输出电流,适用于需要驱动较大负载的场合。
总的来说,GPIO引脚的上拉/下拉/浮空、开漏和推挽等设置可以根据具体的应用需求进行调整,以确保引脚的稳定性和信号质量,同时也可以满足不同的设备连接和驱动需求。
“复用”是指 STM32 的其它片上外设对 GPIO 引脚进行控制,此时 GPIO 引脚用作该外设功能的一部分,算是第二用途。(LED,蜂鸣器,传感器不在片上,也不需要协议)
对于普通输出,引脚控制权来自输出数据寄存器,如果想让定时器来控制引脚,就要断开输出寄存器,输出控制权转移给片上外设(egTIM2)。比如在使用通用定时器输出比较功能时,引脚就要配置成复用推挽输出。
复用推挽输出还可以让端口接收外部信号,而通用推挽输出则不能。复用推挽输出常用于一些需要双向数据传输或高速数据传输的场合,例如SPI、I2S、USART等通信协议。通用推挽输出常用于一些只需要单向数据传输或低速数据传输的场合,例如LED、蜂鸣器、继电器等控制器。
模式选择:看引脚上外部模块输出的默认电平
- 空闲默认高电平---上拉输入,输入高电平,默认
- 空闲默认低电平---下拉输入,保持一致,防止电平打架
- 不确定或外部信号输出功率很小---浮空输入(引脚悬空时无默认电平,输入受噪声影响)
相关库函数
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
首先根据挂载设备的总线,启动对应时钟
- GPIO_Init : 使用结构体初始化GPIO口
- GPIO_StructInit:结构体变量赋默认值
- uint_8 GPIO_ReadInputDataBit:读取输入寄存器某一端口高低电平
- uint_16 GPIO_ReadInputData:读取整个输入数据寄存器 ,返回16位
- uint_8 GPIO_ReadOutputDataBit:读取输出寄存器某一端口高低电平,看输出的是什么
- uint_16 GPIO_ReadOutputData:读取整个输出数据寄存器 ,返回16位
- GPIO_SetBits:设置指定端口高电平
- GPIO_ResetBits:设置指定端口低电平
- GPIO_WriteBit:上两个的综合,通过值设置高低电平
- GPIO_Write:同时对16个端口写入,写入0x0001 -> 0000 0000 0000 0001 最后一个至高
具体操作
所有接入引脚的外设都要配置下边的步骤
- 使能对应RCC时钟
- 配置GPIO_Init结构体:输入输出模式,引脚,频率
- 配置挂载的其他外设的时钟和结构体
- 使用读取端口的库函数封装写入或读取等相关函数
EXTI外部中断
68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设,都可以产生中断(比如串口中断函数检测对应寄存器可触发中断),这里是其中一种,但NVIC等配置通用
NVIC
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级.
抢占优先级和响应优先级
STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由NVIC_IRQChannelPreemptionPriority 的参数配置。
而响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断, 响应属性由NVIC_IRQChannelSubPriority 参数配置
NVIC 的优先级组
在配置优先级的时候,还要注意一个很重要的问题,即中断种类的数量。NVIC 只可以配置16 种中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个4 位的数字来决定,把这个4 位数字的位数分配成抢占优先级部分和响应优先级部分。有5 组分配方式:
- 第0 组: 所有4 位用来配置响应优先级。即16 种中断向量具有都不相同的响应优先级。
- 第1 组:最高1 位用来配置抢占优先级,低3 位用来配置响应优先级。表示有2 种级别的抢占优先级(0 级,1 级),有8 种响应优先级,即在16 种中断向量之中,有8 种中断,其抢占优先级都为0 级,而它们的响应优先级分别为0~7,其余8 种中断向量的抢占优先级则都为1 级,响应优先级别分别为0~7。
- 第2 组:2 位用来配置抢占优先级,2 位用来配置响应优先级。即4 种抢占优先级,4 种响应优先级。
- 第3 组:高3 位用来配置抢占优先级,最低1 位用来配置响应优先级。即有8 种抢占优先级,2 种响应2 优先级。
- 第4组:所有4 位用来配置抢占优先级,即NVIC 配置的16 种中断向量都是只有抢占属性,没有响应属性。
EXTI简介
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿/下降沿/双边沿/软件触发
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(PA0和PB0不行)
- 通道数:16个GPIO_Pin(主要),外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应/事件响应
AFIO
- 中断引脚选择:每个GPIO外设都有16个引脚,总共16个通道,不能每个引脚占用一个通道
- 复用功能引脚重映射:默认复用功能引脚换到重定义
外部中断适用情况
获取外部驱动很快的突发信号时,例如旋转编码的脉冲,红外的信号
不建议读取按键,由于有抖动,按键也比较慢,可以使用定时器中断读取
相关库函数
AFIO相关:在GPIO文件中定义
- GPIO_PinRemapConfig:引脚重映射
- GPIO_EXTILineConfig:配置AFIO数据选择器,选择对应的引脚
EXTI相关
- EXTI_Init:结构体配置
- 主函数中调用
- 中断中调用
NVIC相关:在misc文件中定义
- NVIC_PriorityGroupConfig:中断分组
- NVIC_Init:结构体初始化
具体操作
- 开启GPIO和AFIO时钟
EXTI本来也应该有时钟,但并未提及;NVIC是内核的外设,不需要开启时钟,RCC管内核外外设
- 配置GPIO_Init:默认上拉或找手册
- 配置AFIO:GPIO_EXTILineConfig 未分配专门库函数,在GPIO的库函数中
- 配置EXTI_Init:指定中断线(对应引脚),新状态(enable), 中断线模式(中断/事件),有效边沿(上升/下降/上升+下降)
- 配置NVIC分组: NVIC_PriorityGroupConfig
- 初始化 NVIC_Init:指定通道,通道使能,两个优先级
- 写对应的中断函数EXTI5_10_IRQHandler:无参无返回值 ,确定标志位,清除标志位
中断响应的流程
中断是一种机制,允许外部设备或软件在CPU正常执行程序时打断程序的执行,执行一个特定的处理程序来响应外部事件。在嵌入式系统中,中断是一种重要的机制,用于处理各种外部事件,例如定时器、I/O操作、传感器等。下面是中断响应的一般流程:
- 中断请求:外部设备或软件在需要CPU处理某个事件时,会向CPU发送中断请求。中断请求通常是通过硬件设备(如GPIO口)或软件(如系统调用)触发的。
- 中断处理器的响应:当CPU接收到中断请求时,它会立即停止当前任务的执行,并跳转到中断向量表中对应中断处理器的入口地址。中断向量表是一个包含所有中断处理器入口地址的表格,该表格是在系统启动时预先设置的。
- 中断处理器的执行:当CPU跳转到中断处理器入口地址时,会执行该中断处理器的代码。中断处理器通常是一个短小精悍的函数,它会对中断请求进行处理,例如读取数据、清除标志位、重置定时器等。
- 中断处理完成:当中断处理器执行完毕后,CPU会将现场恢复到中断请求前的状态,然后继续执行原来的程序。在这个过程中,CPU会处理中断请求,确保中断请求被及时响应和处理。
总之,中断响应的流程包括中断请求、中断处理器的响应、中断处理器的执行和中断处理完成等步骤。中断机制是一种非常重要的机制,可以帮助系统高效地处理各种外部事件,提高了系统的响应速度和可靠性。
定时器
相关知识
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
- 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
- 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
类型 |
编号 |
总线 |
功能 |
高级定时器 |
TIM1、TIM8 |
APB2 |
拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
通用定时器 |
TIM2、TIM3、TIM4、TIM5 |
APB1 |
拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
基本定时器 |
TIM6、TIM7 |
APB1 |
拥有定时中断、主模式触发DAC的功能 |
定时中断功能--基本定时器
功能原理
- 根据晶振产生固定间隔来计数(72MHz),计不同的数来设置不同时间的中断,eg计72000个数,表示1ms
- 分配给我们定时器的时钟是72MHz,我们可以根据自己的需求再设置定时器的分频,设置它的定时值
- 基准时钟到预分频器,计数器对分频后的时钟计数,达到自动重装寄存器的值时触发NVIC。
- 可将上边的中断同时发给DAC,实现主模式触发DAC的功能
- 基本定时器只能选择内部时钟,通用和高级可选外部时钟
具体使用
- 开启TIM1时钟(RCC_APB1PeriphClockCmd)
- TIM_TimeBaseInit结构体配置时基单元:分频(随意)->计数模式(向上)-> ARR自动重装值 -> 预分频器值
- TIM_ClearFlag:配置好后会立即进入中断,故清除中断标志位
- TIM_ITConfig:使能更新中断,开启更新中断到NVIC的通路
- NVIC_PriorityGroupConfig:配置NVIC优先级分组
- NVIC_Init 结构体配置NVIC : 指定通道,通道使能,两个优先级
- TIM_Cmd:启动定时器
- 使用定时器中断
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)
例如计数1s: 可以选择以高频率记大数或小频率记小数 72M / 7200 / 10000
有一个数的偏差
TIM_GetCounter:获得计数值
输出比较
实验:TB6612直流电机驱动电机
功能原理
- 可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形,主要用在电机上
- 输入捕获和输出比较不能同时使用,共用中间的定时器和引脚
PWM:脉冲宽度调制
- PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
- PWM占空比: Duty = CCR / (ARR + 1)
- PWM分辨率: Reso = 1 / (ARR + 1)
具体使用
- 开启TIM2时钟(RCC_APB1PeriphClockCmd)和GPIO时钟(连接外设舵机)
- TIM_TimeBaseInit结构体配置时基单元:分频(随意)->计数模式(向上)-> ARR自动重装值 -> 预分频器值
- TIM_ClearFlag:配置好后会立即进入中断,故清除中断标志位
后续不需要中断,只需要在比较器中输出高低电平,所以不用配置NVIC
- TIM_OC1Init结构体配置输出比较单元:默认配置,比较模式,比较极性:不翻转,输出使能,CCR:改变占空比来实现亮暗
- GPIO配置:复用推挽输出,输出控制来自片上外设(TIM2),而不是输出数据寄存器
- 可以选择重映射引脚配置TIM2_CH1输出引脚
- TIM_Cmd:启动定时器
- TIM_SetCompare1: 封装函数调用,通过设置比较值改变比较值CCR来改变频率
输入捕获
实验:自己输出PWM检测自己
功能原理
- 当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
- 可配合主从触发模式,实现硬件全自动测量
- 配置好时基单元启动启动定时器,CNT是测周法计数计时的,经过预分频后的时钟频率就是驱动CNT的标准频率fc
- 输入捕获GPIO口输入方波信号,经过滤波器和边沿检测,选择TI1FP1为上升沿触发,直连通道不分频
- TI1FP1产生上升沿通道时,CNT当前技术值转运到CCR1中,同时触发源选择,选中TI1FP1为触发信号,从模式进行复位cnt
具体使用
- 开启TIM3时钟(RCC_APB1PeriphClockCmd)和GPIO时钟(输入模式)
- GPIO配置:GPIO初始化,把GPIO配置成输入模式
- TIM_TimeBaseInit结构体配置时基单元:让CNT计数器在内部时钟的驱动下自增
- TIM_ClearFlag:配置好后会立即进入中断,故清除中断标志位
- TIM_ICInit配置输入捕获单元:通道,滤波,极性,分频,触发输入引脚,直连
- TIM_SelectInputTrigger:选择从模式触发源TIM_TS_TI1FP1
- TIM_SelectSlaveMode:选择触发后执行的操作,从模式TIM_SlaveMode_Reset
- TIM_Cmd:启动定时器
- TIM_GetCapture1: 封装函数调用,读取频率
编码器接口
功能原理
- 硬件接口来检测,之前的旋钮是用中断检测的。可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度(测评法)。
- 两个输入引脚(AB相)借用了输入捕获的通道1和通道2(定时器CH1和CH2)
- 正交编码器可抗噪声,通过AB相上升沿,下降沿对应关系来判断方向(可选择个别或全部边沿计数)
一个信号受影响,电平不变,在另一个相的边沿检测时会不断增减cnt,使得cnt不变
- 输入捕获的前两个通道,通过GPIO口接入编码器A、B相。。。
- 产生TI1FP1和TI1FP2触发源,通向编码器接口
- 编码器接口通过预分频器控制CNT计数器的时钟,同时通过旋转方向控制CNT计数
- 一般设置ARR最大量程65535(利用补码特性得到负数,0反转时变为65535,将16位无符号数转为有符号数就变成了负数-1)
具体使用
- 开启TIM3时钟(RCC_APB1PeriphClockCmd)和GPIO时钟(输入模式)
- GPIO配置:GPIO初始化,把GPIO配置成输入模式
- TIM_TimeBaseInit结构体配置时基单元:让CNT计数器在内部时钟的驱动下自增
- TIM_ClearFlag:配置好后会立即进入中断,故清除中断标志位
- TIM_ICInit配置输入捕获单元的两个通道CH1|CH2:通道,滤波
- TIM_EncoderInterfaceConfig:配置编码器接口模式:TI12都计数,通道不反相,反向可以改旋钮增减方向
- TIM_Cmd:启动定时器
- 使用对应函数
主从触发模式--自己起的
STM32定时器一大特色,可 动运行,减轻CPU负担。
ADC模数转换 DAC数模转换
实验:电位器旋钮显示电压值,多个传感器数字信号
功能原理
- ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
PWM控制亮度和电机即DAC的功能 ,在直流电机调速这种大功率场景,使用PWM来等效模拟量比DAC更好
- 输入电压范围:0~3.3V,转换结果范围:0~4095(12位,寄存器16位,选择左右对齐)
- 18个输入通道,可测量16个外部和2个内部信号源
- 模拟看门狗自动监测输入电压范围
- STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道
- 传感器只需要配置AD通道即可,自己不需
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
作者简介:2个月时间逆袭嵌入式开发,拿下理想汽车-ssp、小米汽车-sp、oppo-sp、迈瑞医疗、三星电子等八家制造业大厂offer~ 专栏内容:涵盖算法、八股、项目、简历等前期准备的详细笔记和模板、面试前中后的各种注意事项以及后期谈薪、选offer等技巧。保姆级全阶段教程帮你获得信息差,早日收到理想offer~