Stm32学习汇总

本文最后更新于:2024年3月7日 下午

前言

得到这块板子是源于学校的一个口袋实验室的实验课程。每小组分一块正点原子的阿波罗Stm32F4的板子并要求自学。由于个人也有一定的嵌入式开发的意向,于是将Stm32学习的部分于此记录成博客。

初步介绍

全面的板子介绍在开发指南上写有。在此补充一些初学者对板子的认知,可能会方便理解一些。
Stm32的芯片其实在学校发的这块正点原子的板子只是很小的一个部分,这块板子上的绝大多数配件是配套设施很全的外设。
大概是这样的
板子的供电有两种方式,一种是直接用右上角的充电接口连接电源,另一种是通过USB SLAVE接口连接到电脑。
其中直接供电的话最高电压会高一些(16v→12v)USB SLAVE链接的是电脑usb接口所以在跑一些耗电量较大的工程时可能会带不太动。
与电脑的交互部分,我们可以通过USB SLAVE来直接下载代码以及向电脑发送串口信号。还可以通过STLink来进行电脑的下载。(关于STLink的介绍及配置
然后是代码编辑器。使用的是keil5,需要注册。没注册的话只能编译大小受限的代码,解决方法是在网上找到了注册软件。(注意,我的这个是2020版的,网上大多是比2020更早的版本,若是后来者发现注册码无法激活,可能需要找到最新版的软件来生成。)
编辑器具体配置可以去看正点原子的官方教程,一步一步来就不会出错。


开始学习

注明:我学习的是HAL库写法,HAL库封装的功能很多并且对于初学者很友好。

Hello World

大多数东西初学的时候都会由Hello World来引入。Stm32的hello world 就是点亮第一盏LED灯。
main.c 中,首先得初始化HAL库 HAL_Init(); ,将HAL库引入程序。之后设置系统时钟。有一定电路基础的话就知道,时钟提供了周期性变化的电压,给整个系统确立了最小的单位时间以及维持着整个系统的时间轴,对于任和想实现的功能来说时钟是极为重要的。设置系统时钟的代码为 Stm32_Clock_Init(360,25,2,8) 。括号里的参数可以自己配置,不过对于这块板子(STM32F429)来说这些数据是固定的。(关于时钟配置的详细讲解移步这里)。
下一步是确定自己所要使用的接口,我们自己可以配置的接口也就是通用性接口是GPIO接口,这些接口可供自由使用。 GPIO_InitTypeDef GPIO_Initure 可以初始化一个GPIO类型的结构体,我们可以通过这个结构体来对GPIO接口来做相应的配置。(关于GPIO的详细讲解移步这里)。
点亮LED灯,所需的接口是GPIOB 的 Pb0 | Pb1 接口。首先开启GPIOB的时钟 语句是 __HAL_RCC_GPIOB_CLK_ENABLE(); 然后给GPIO类型的结构体赋值,大概是这样:

1
2
3
4
GPIO_Initure.Pin=GPIO_PIN_5; //PB1,0
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

其中第一条确定的是哪个或者哪些接口,后面的则都是在规范IO类型。
然后就是将这个结构体赋值到接口上 HAL_GPIO_Init(GPIOB,&GPIO_Initure); 这句话的意思是把我们所赋值的这个结构体复制到GPIOB的接口上。

完成这一步,就已经把所用接口的准备工作做完了。
然后就是主程序部分,主程序是一个while循环,我们将在这里完善自己的程序逻辑。
点亮LED灯所需要的是给LED的两级接上高低不同的电压。通过原理图可以知道,当输出电平为低电平时LED单向导通发亮。因此假如发亮就得让GPIO口输出电平为低,反之则为高。
原理图是这样的
我们可以通过 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); 来将GPIOB的PB1接口设置为高电平。 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET); 将GPIOB的PB1接口设置为低电平。只要在函数里加上这两条语句,就能使LED亮灭了。
但是假如直接这么写的话你会发现LED好像是恒亮的,这是因为由于人眼的视觉暂留因素,高频率的亮灭无法让人区分。因此我们可以在这两条亮灭语句后各添一条延时函数,让LED保持状态一段时间。常用的是 delay() 函数,当然也可以自己写一个简单的循环,按照之前打OI的经历,大概2e8次空循环是1s。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
void Delay(__IO uint32_t nCount);
void Delay(__IO uint32_t nCount){while(nCount--){}}
int main(void)
{
HAL_Init();//HAL库初始化
Stm32_Clock_Init(360,25,2,8);//系统时钟确立

__HAL_RCC_GPIOB_CLK_ENABLE();//GPIOB口时钟确立
GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; //PB1,0
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_Initure.Pull=GPIO_PULLUP;
GPIO_Initure.Speed=GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_Initure);

while(1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
Delay(0x7FFFFF);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
Delay(0x7FFFFF);
}
}


一些要点

GPIO

介绍

GPIO代表“通用输入/输出”。是集成电路上的一种引脚,没有特定功能。虽然大多数引脚都有特定用途,例如向某个组件发送信号,但 GPIO 引脚的功能是可定制的,并且可以通过软件控制。
GPIO 接口变化很大。在某些情况下,一组可以作为一组切换到输入或输出的引脚。在其他情况下,每个引脚都可以设置为接受或提供不同的逻辑电压,具有可配置的驱动强度和上拉/下拉。输入和输出电压通常受限于带有 GPIO 的设备的电源电压,并且可能会被更高的电压损坏。(所以要小心输入)

GPIO 功能可能包括:
GPIO 引脚可配置为输入或输出
GPIO 引脚可以启用/禁用
输入值是可读的(通常是高低电平)
输出值是可写/可读的
输入值通常可以用作IRQ(通常用于唤醒事件)

Stm32中的GPIO配置

首先是开启时钟 __GPIOA_CLK_ENABLE();//使能GPIOA时钟 以及配置GPIO输入输出模式。
GPIO_InitTypeDef结构体定义有一些赋值项,在HAL库中我们通过为这些元素赋值来控制GPIO。

1
2
3
4
5
6
7
8
typedef struct
{
uint32_t Pin; //指定 IO 口
uint32_t Mode; //模式设置
uint32_t Pull; //上下拉设置
uint32_t Speed; //速度设置
uint32_t Alternate;//复用映射配置
}GPIO_InitTypeDef;

(1) uint32_t Pin 指定了输出的IO口,可以是一个可以是多个,多个IO口用或(|)符号隔开。
(2) uint32_t Mode 设置了输入输出的模式,模式一共有以下八种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GPIO_Mode_AIN             /*模拟输入,特点:应用ADC模拟输入,或者低功耗下省电*/

GPIO_Mode_IN_FLOATING /*浮空输入,特点:浮空输入一般多用于外部按键输入,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的*/

GPIO_Mode_IPD /*下拉输入,特点:默认低电平*/

GPIO_Mode_IPU /*上拉输入,特点:默认高电平*/

GPIO_Mode_Out_OD /*开漏输出,特点:IO输出0接GND,IO输出1,悬空;接上拉电阻才可以输出高电平,上升沿速度慢。适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内);可以将多个开漏输出的Pin,连接到一条线上。通过一只上拉电阻,在不增加任何器件的情况下,形成“与逻辑”关系。*/

GPIO_Mode_Out_PP /*推挽输出,特点:可以输出高,低电平,导通损耗小,效率高,读输入值是未知的*/

GPIO_Mode_AF_OD /*复用开漏输出,特点:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用),如:片内外设功能(TX1,MOSI,MISO.SCK.SS)*/

GPIO_Mode_AF_PP /*复用推挽输出,特点:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用),如:片内外设功能(I2C的SCL,SDA)*/

GPIO引脚的各种IO模式电路

(3) uint32_t Pull 区分这个GPIO口用于输入还是输出,输出模式的话一般是 no pull 。这样可以正确输出目标值。
当输入模式的时候,需要根据输入值来配置,若默认输入为高电平,则需要 pull up ,反之 pull down
上拉(pull up)是对器件注入电流,下拉是输出电流,上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起到限流作用;而下拉就是将不确定的信号通过一个电阻钳位在低电平,电阻同时起到限流作用。
(4) uint32_t Speed 规定输出电路的响应速度,可以理解为输出电路的带宽,涉及到信号失真问题。同时,速度配置越高,噪声越大,功耗越大。
(5) uint32_t Alternate 接口的复用功能,可作为I2C,SPI,USART等通讯接口。此时GPIO的作用不是通用的输入输出。此时端口的输出模式需要配置为复用状态。

GPIO常用函数汇总

GPIO接口初始化
1
2
3
4
5
6
7
 void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);
/************************************
功能:根据指定参数初始化GPIOx外设
参数定义:
GPIOx:其中x可以是(A..H),用于选择STM32L4系列的GPIO外设
GPIO_Init:指向GPIO_InitTypeDef结构的指针,该结构包含指定GPIO外设的配置信息。
***********************************/
GPIO接口去初始化
1
2
3
4
5
6
7
void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin);
/************************************
功能:将GPIOx外设寄存器解除初始化为其默认复位值。
参数定义:
GPIOx:其中x可以是(A..H),用于选择STM32L4系列的GPIO外设
GPIO_Pin:指定要写入的端口位;此参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
***********************************/
读取GPIO的值
1
2
3
4
5
6
7
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
/************************************
参数定义:
GPIOx:其中x可以是(A..H),用于选择STM32L4系列的GPIO外设
GPIO_Pin:指定要写入的端口位;此参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
返回值:端口引脚值
***********************************/
向指定接口写入值
1
2
3
4
5
6
7
8
9
10
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
/************************************
参数定义:
GPIOx:其中x可以是(A..H),用于选择STM32L4系列的GPIO外设
GPIO_Pin:指定要写入的端口位;此参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。
PinState:指定要写入的值
*此参数可以是GPIO_PinState枚举值之一:
* GPIO_PIN_RESET:清0
* GPIO_PIN_SET:置1
***********************************/
对GPIO值取反和锁定
1
2
3
4
5
6
7
8
9
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

/************************************
参数定义:
GPIOx:其中x可以是(A..H),用于选择STM32L4系列的GPIO外设
GPIO_Pin:指定要写入的端口位;此参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)。

***********************************/
GPIO中断
1
2
3
4
5
6
7
8
9
void  HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
/***************************************
功能:
HAL_GPIO_EXTI_IRQHandler: 中断请求标志位清零并进入回调函数
HAL_GPIO_EXTI_Callback: 中断服务函数
EXTI线路检测回调。
__weak:__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。
****************************************/

时钟

时钟是电路的脉搏,这个形容我感觉很贴切,系统时钟的频率很高,统一使用系统时钟(高频)会造成功耗的无意义消耗。STM32F429的时钟有很多源,可以支持许多繁杂的外设。
这是STM32F429的系统时钟图
STM32有五个时钟源,分别是:HSI、HSE、LSI、LSE、PLL。
①、LSI 是低速内部时钟,RC 振荡器,频率为 32kHz 左右。供独立看门狗和自动唤醒单元使用。
②、LSE 是低速外部时钟,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。
③、HSE 是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz~26MHz。
我们的开发板接的是 25M 的晶振。HSE 也可以直接做为系统时钟或者 PLL 输入。
④、HSI 是高速内部时钟,RC 振荡器,频率为 16MHz。可以直接作为系统时钟或者用作 PLL
输入。
⑤、PLL 为锁相环倍频输出。STM32F4 有三个 PLL:

1) 主 PLL(PLL)由 HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。
第一个输出 PLLP 用于生成高速的系统时钟(最高 180MHz)
第二个输出 PLLQ 为 48M 时钟,用于 USB OTG FS 时钟,随机数发生器的时钟和 SDIO
时钟。
2) 第一个专用 PLL(PLLI2S)用于生成精确时钟,在 I2S 和 SAI1 上实现高品质音频性能。其
中,N 是用于 PLLI2S vco 的倍频系数,其取值范围是:192~432;R 是 I2S 时钟的分频系
数,其取值范围是:2-7;Q 是 SAI 时钟分频系数,其取值范围是:2-15;P 没用到。
3) 第二个专用 PLL(PLLSAI)同样用于生成精确时钟,用于 SAI1 输入时钟,同时还为 LCD_TFT
接口提供精确时钟。其中,N 是用于 PLLSAI vco 的倍频系数,其取值范围是:192~432;
Q 是 SAI 时钟分频系数,其取值范围是:2~15;R 是 LTDC 时钟的分频系数,其取值范
围是:2~7;P 没用到

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!