STM32外设工作原理(基于HAL库)
1. 通用定时器生成PWM波
1.1 功能概述
通用定时器TIM2-TIM5,TIM9-TIM14,计数器位数,通道数不同
1.2 生成PWM波的原理
PWM(Pulse Width Modulation)就是脉冲宽度调制,PWM波就是具有一定占空比的方波信号,通过定时器的设置可以控制方波的频率和占空比。
1.3 与生成PWM波相关的HAL函数
1.4 STM32CUBEMX配置(固定频率PWM)
对于STM32F407ZGT6的TIM14的CH1可以对应引脚到PF9(LED0),用来控制灯的亮度
定时器TIM14各选项的意义:
- Disable ,禁用通道
- Input Capture Direct Mode,直接模式输入捕获
- Output Compare No Output,输出比较,不输出到通道引脚
- Output Compare CH1,输出比较,输出到通道引脚CH1
- PWM Generation No Output,生成PWM,不输出到通道引脚
- PWM Generation CH1,生成PWM,输出到通道引脚CH1
- Forced Output CH1,强制通道引脚CH1输出某个电平
PWM模式,选项有PWM Mode 1和PWM Mode 2
PWM Mode 1->递增计数模式:CNT<CCR(有效状态),CNT>CCR(无效状态)
PWM Mode 2->递减计数模式:CNT<CCR(无效状态),CNT>CCR(有效状态)
Pulse,PWM脉冲宽度,就是设置16位的捕获/比较寄存器CCR的值。脉冲宽度的值应该小于计数周期的值
eg:设置为50,因为计数器的时钟频率是10kHz(对应定时器APB的值,经过Prescaler分频之后的时钟频率(进入计数器的时钟频率)),所以脉冲宽度为5ms
Output compare preload,输出比较预装载。若Enable,修改CCR值(修改占空比)后立即生效;若Disable,修改CCR之后要等到下一个UEVA事件才会生效。
Fast Mode,是否使用输出比较快速使能
CH Polarity,通道极性,就是CCR与CNT比较输出的有效状态,可以设置为高电平(High)或低电平((Low)
主程序(固定频率PWM):
必须调用函数启动定时器,再启动定时器的PWM输出
1
2
3 HAL_TIM_Base_Start_IT(&htim14); //以中断方式启动TIM14
HAL_TIM_PWM_Start_IT(&htim14,TIM_CHANNEL_1);
//TIM14通道1, 启动生成PWM主程序(可变频率PWM):
重新实现回调函数HAL_TIM_PWM_PulseFinishedCallback(),在此回调函数里编写代码改变占空比。
1
2 uint8_t pulseWidth=50;//脉宽
uint8_t dirInc=1;//脉宽变化方向,1=递增,0=递减
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance != TIM14)
return;
if(dirInc==1)
{
pulseWidth++;
if(pulseWidth>=195)
{
pulseWidth = 195;
dirInc = 0;
}
}
else
{
pulseWidth--;
if(pulseWidth<=5)
{
pulseWidth = 5;
dirInc=1;
}
}
__HAL_TIM_SET_COMPARE(&htim14,TIM_CHANNEL_1,pulseWidth);//设置CCR的值
}
2. 输入PWM
一个定时器产生PWM波,另一个定时器测量这个PWM波,本次采用TIM14的CH1通道(即PF9)来产生PWM波TIM9_CH1测量pwm波。
2.1 cubemx的配置
使用定时器TIM9的PWM输入功能测量PWM波参数。TIM9有2个通道,在输入PWM模式下,使两个通道都映射到TIM9_CH1的复用引脚上。
由于需要在TIM9的捕获比较中断里读取CCR的值,因此打开TIM9的中断。
3.基于HAL库的按键检测代码
https://wwqe.lanzouo.com/iuE7i0kvztyf
4.CUBEMX配置LCD屏幕
检查FSMC自动分配的GPIO引脚是否与电路图一致,若不一致直接修改即可。
设置背光对应的IO
5.基于HAL库改版正点原子TFTLCD驱动程序(HAL)
基于型号9413,stm32f407zgt6
https://wwqe.lanzouo.com/igS5X0nv67cf
6.基于HAL库改版正点原子HAL库TFTLCD驱动程序(最新版V3)
改良版程序
https://wwqe.lanzouo.com/i3nHP0nvqm8b
基于CUBEIDE的LCD实现程序:
https://wwqe.lanzouo.com/ipmAU0qm5dob
6.1 重点
在最新版的TFTLCD驱动程序中,需要把lcd_ex.c文件在MDK中设置为永不编译的情况
具体说明连接:https://blog.csdn.net/qq_25144391/article/details/118959208
在CUBEIDE的配置中:
7.实时时钟RTC
工程连接https://wwqe.lanzouo.com/iy3Ac0nyb2de
RTC(Real-time Clock,实时时钟)是由时钟信号驱动的日历时钟,提供日期和时间数据。
- RTC能提供BCD或二进制的秒、分钟、小时(12或24小时制)、星期几、日期、月份、年份数据
- RTC模块和时钟配置都使用备用存储区域,使用VBAT备用电源,即使主电源断电或系统复位也不影响RTC的工作
- RTC有两个可编程闹钟中断,可以设定任意组合和重复性的闹钟
- 有一个可周期性唤醒的中断,唤醒中断可以当做一个普通定时器使用
- 具有时间戳和入侵检测功能
8.串口USART
工程连接:https://wwqe.lanzouo.com/iOpDW0o4r0ri
8.1常用宏函数
1 | __HAL_UART_ENABLE(__HANDLE__),使能一个UART口,如 |
注意:注意,HAL_UART_Receive_IT()完成一次数据接收后就关闭了串口接收中断,不会自动进行下一次的接收,需要再次调用HAL_UART_Receive_IT()以启动下一次的接收,但不能在回调函数HAL_UART_RxCpltCallback()里调用HAL_UART_Receive_IT()。
为了能连续进行中断方式的串口接收,程序的处理方法是:在完成一次接收,并且串口状态为空闲,也就是发生UART_IT_IDLE中断时,对接收到的指令数据进行处理,然后再次调用HAL_UART_Receive_IT()以启动下一次的接收。
9.ADC
- ADC转换电压的输入范围范围是VREF-≤VIN≤VREF+,由于VREF-必须与VSSA连接,也就是VREF-总是0,所以STM32F407的片上ADC只能转换正电压。
- 选择的多个模拟输入通道可以分为两组:规则通道和注入通道,每个组的通道构成一个转换序列。
- 规则转换序列最多可设置16个通道,一个规则转换序列规定了多路复用转换时的顺序。
- 注入通道就是可以在规则通道转换过程中插入进行转换的通道,类似于中断的现象。每个注入通道还可以设置一个数据偏移量,每次转换结果自动减去这个偏移量,所以转换结果可以是负数。
ADC时钟与转换时间:ADCCLK最高42MHz。一个通道一次ADC转换的总时间是N+12个ADCCLK周期,N是设置的采样次数。

9.1软件启动ADC转换
工程链接:https://wwqe.lanzouo.com/izvuZ0qapo8j
1 | HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc); |
其中,参数hadc是ADC外设对象指针,Timeout是超时等待时间,单位是节拍数。
1 | 在mian()函数的while循环里,每500毫秒以软件触发方式进行一次ADC转换方式 |
9.2定时器触发ADC转换
工程链接:https://wwqe.lanzouo.com/imGIH0qapobc
- ExternalTriggerConversionSource,设置启动ADC转换的外部触发信号源,这里选择Timer3TriggerOutEvent,也就是定时器TIM3的TRGO信号
- External Trigger Conversion Edge,设置触发转换的跳变沿,这里选择上跳沿,因为
TRGO是一个短时正脉冲信号 - 开启ADC1的全局中断,在转换完成事件(ADC_IT_EOC)中断里读取转换结果。

TIM3的设置:
- 使TIM3定时周期为500ms
- 触发事件选择(Trigger Event Selection)设置为Update Event,也就是以UEV事件信号作为TRGO信号、
回调函数
注:无须开启TIM3的全局中断,TGRO信号也是正常输出的
1 | /* USER CODE BEGIN 4 */ |
9.3多通道和DMA传输
工程链接:https://wwqe.lanzouo.com/iX7ip0qapo4f
这里以ADC1的三个通道为例
- 当规则转换组有多个通道时,应该使用扫描模式(Scan Conversion Mode)
注:在配置ADC时要使能扫描模式的DMA
设置Rank的输入通道和采样时间——每个通道的采样时间可以不一样,三个Ran里模拟通道出现的顺序就是规则组转换的顺序。
特别注意: 外设使用DMA时是否需要开启外设的全局中断,不同的外设情况不一样。例如,UART 使用DMA时就必须开启UART的全局中断,虽然可以禁止UART的两个主要中断事件源。在外设使用DMA时,建议尽量不开启外设的全局中断,若必须开启,也要禁止外设 的主要事件源产生硬件中断,因为DMA的传输完成事件中断使用外设的回调函数,若开启外 设的中断事件源,则可能导致一个事件发生时回调函数被调用两次。
1 |
|
1 | lcd_init(); |
1 | void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) |
9,4双ADC同步转换
工程链接:https://wwqe.lanzouo.com/ia6qr0qapoij
开启TIM3定时器
双ADC同步转换时CUBEMX的配置
多重ADC模式,只能使用DMA方式传输数据。
具体完整配置:
注:在很多版本的CUBEMX中,当ADC1和ADC2配置为同步规则转换时,ADC2的DMA流不能设置为Enable,这是软件自带的一个小bug,需要在程序中去进行修改。但每次重新生成程序,依然会重置回去,要注意每次都要修改。
程序代码:
.c文件中
1 |
|
1 | lcd_init(); |
1 | //DMA传输完成事件中断的回调函数 |
在双ADC同步模式下,MCU自动将ADC1和ADC2一次转换的数据组合成一个32位数据,高16位是ADC1的数据,低16位是ADC2的数据。
10.DMA
工程链接:https://wwqe.lanzouo.com/ipB8z0qapiej
DMA(Direct Memory Access,直接存储器访问)是实现存储器与外设、存储器与存储器之间高效数据传输的方法
- DMA控制器
- DMA流
- DMA请求
- 仲裁器
一个DMA流配置一个DMA请求后,就构成一个单方向的DMA数据传输链路,DMA传输属性就由DMA流的参数配置决定
- DMA流和通道
- DMA流的优先级别
- 源和目标的数据宽度
- 传输数据量的大小
- 源和目标地址指针是否自增加
- DMA工作模式:Normal或Circular
- DMA传输方向
- 是否使用FIFO
DMA传输模式:
- 外设到存储器(Peripheral To Memory),例如ADC采集的数据存入内存中的缓存区
- 存储器到外设(Memory To Peripheral),例如将内存中的数据通过USART接口发出
- 存储器到存储器(Memory To Memory),例如外部SRAM中的数据复制到内存中,只有DMA2控制器有这种传输模式
数据宽度:
数据宽度(Data width)是源和目标传输的基本数据单元的大小,有字节(Byte)、半字(Half word)和字(word)三种大小。
DMA工作模式:
- 正常(Normal)模式是指传输完一个缓存区的数据后,DMA传输就停止了。
- 循环(Circular)模式是指启动一个缓存区的数据传输后,会循环执行这个DMA数据传输任务
例如,HAL_UART_Receive_DMA(),正常模式只传输一次,循环模式就能连续接收
DMA流的优先级别:
每个DMA流有一个可设置的软件优先级别,有4种:
- Very high(非常高)
- High(高)
- Medium(中等)
- Low(低)
如果两个DMA流的软件优先级别相同,则流编号更小的优先级别更高,流编号就是DMA流的硬件优先级。
DMA流有自己的中断号和ISR函数
USART+DMA主程序:
1 | int main(void) |
11.SPI接口通信
SPI硬件接口:
SPI是串行外设接口(Serial Peripheral Interface)
SPI接口的设备分为主设备(Master)和从设备(Slave),一个主设备可以连接一个或多个从设备
SPI传输协议:
SPI通讯有4种时序模式,由SPI控制寄存器SPI_CR1中的CPOL和CPHA位控制。
- CPOL(Clock Polority)时钟极性,控制SCK引脚在空闲状态时的电平。如果CPOL=0,则空闲时SCK为低电平;若CPOL=1,则空闲时SCK为高电平。
- CPHA(Clock Phase)时钟相位,若CPHA=0,则在SCK的第1个边沿对数据采样;如果CPHA=1,则在SCK的第2个边沿对数据采样。
CPHA=0表示在SCK的第1个边沿读取数据,即图中虚线表示的时刻。读取数据的时刻发生在SCK的下跳沿(CPOL=1)时刻或上跳沿(CPOL=0)时刻。MISO、MOSI上的数据变化在读取数据的SCK前一个跳变沿时刻发生变化。
CPHA=1表示在SCK的第2个边沿读取数据,即图中的虚线表示的时刻。读取数据的时刻发生在SCK上跳沿(CPOL=1)时刻或下跳沿(CPOL=0)时刻。MISO、MOSI上的数据在读取数据的SCK前一个跳变沿时刻发生变化。
注:SPI主机和从机必须使用相同的SPI时序。




























