STM32Cube HAL 库

STM32Cube HAL 库指南

更新日期:2025.04.21

持续更新中...

By ziFly

本文章使用 ARM Cortex-M3 内核的 STM32F103C8T6 作为演示平台

创建HAL库模板

STM32Cube HAL库启动文件(例如:startup_stm32f103xb.s)

\Drivers\CMSIS\Device\ST\STM32F1xx\Source\Templates\arm

后缀缩写

释义

Flash容量

x6

低容量

16KB~32KB

xb

中容量

64KB~128KB

xe

高容量

256KB~512KB

xg

超大容量

>512KB

STM32F103C8T6的FLASH容量为64KB,所以选择startup_stm32f103xb.s

手动新建STM32 HAL库模板

①导入startup_stm32f103xb.s

②导入system_stm32f1xx.c 文件(位于\Drivers\CMSIS\Device\ST\STM32F1xx\Source\Templates

③拷贝官方模板文件夹中的stm32f1xx_hal_conf.h stm32f1xx.h stm32f1xx_it.h 到User文件夹中

下面是HAL库工程模板

注意:需要在Keil中全局定义 USE_HAL_DRIVER STM32F103xB (在Options -> C/C++ -> Define 里)

点亮一颗LED灯

__HAL_RCC_GPIOA_CLK_ENABLE();   //使能GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;     //设置为推挽输出模式
GPIO_InitStruct.Pull = GPIO_PULLUP;             //设置为上拉模式(即高电平)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;   // 高速
GPIO_InitStruct.Pin = GPIO_PIN_13;              //使用PIN13
​
HAL_GPIO_Init(GPIOA, &GPIO_InitSrtuct);         //初始化GPIO口

通用输入/输出

GPIO

GPIO (General - Purpose Input/Output) 通用输入/输出

GPIO 有以下两种功能:

输出(用于控制)

输入(用于采集)

复用是通过片上外设来控制的

通用是通过寄存器来控制的

上拉(复用)输入模式:

下拉(复用)输入模式:

开漏(复用)输出模式:

​ 上、下拉电阻关闭;施密特触发器打开。

​ 往ODR寄存器写0时,N-MOS管导通,P-MOS管截止,输出低电平

​ 往ODR寄存器写1时,N-MOS管截止,P-MOS管截止,输出高阻态

​ 若要输出高电平,需要在外部接上拉电阻

推挽(复用)输出模式:

​ 上、下拉电阻关闭;施密特触发器打开。

​ 往ODR寄存器写0时,N-MOS管导通,P-MOS管截止,输出低电平

​ 往ODR寄存器写1时,N-MOS管不导通,P-MOS管截止,输出高电平

​ 特点:可以输出高低电平,驱动能力强。电平翻转速度快(因为没有上下拉电阻)。

操作GPIO

stm32f1xx_hal_gpio.h中,定义了以下枚举:


typedef enum
{
  GPIO_PIN_RESET = 0u,
  GPIO_PIN_SET
} GPIO_PinState;


HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

在枚举类型GPIO_PinState 中,GPIO_PIN_SET = 1u,GPIO_PIN_RESET = 0u;


GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

HAL_GPIO_ReadPin 函数的返回类型是 GPIO_PinState 。对于 GPIO_PinState ,它有两个参数,分别是GPIO_PIN_RESETGPIO_PIN_SET

中断

GPIO口电平变化中断(外部中断EXTI)

中断触发类型:上升沿触发中断、下降沿触发中断、双边沿触发中断

aka外部中断线

GPIO_PIN_x对应EXTIx

事件(event)->送往相应的外设

中断 ->送往处理器

软件中断事件寄存器:可以由软件产生一个模拟中断

请求挂起寄存器:

中断屏蔽寄存器:

NVIC嵌套向量中断控制器在所有的外部中断线中,只有EXTI[4:0]有独立的中断处理函数;

EXTI[9:5]共享中断向量处理函数EXTI9_5_IRQHandler()

EXTI[15:10]共享中断向量处理函数EXTI15_10_IRQHandler()

若不清除请求挂起寄存器,则单片机会反复执行中断。需要使用以下函数来清除请求挂起寄存器:


__HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__);

其中,__EXIT_LINE__ 是要清除的外部中断线,可以填GPIO_PIN_x


HAL_GPIO_EXTI_Callback()

在STM32中,中断向量的优先级分为两层,分为 抢占优先级响应优先级 。优先级的数字越小代表越优先。

两个中断同时发生时,先比较抢占优先级,再比较响应优先级。

若某个中断正在执行中,另外一个中断突然发生,只比较二者的抢占优先级

串口

在单片机中最常见的串口:TTL串口(异步通信)

TX

RX

两机通常要共地

USART 通用同步或异步接收器和发送器

stm32f1xx_hal_uart.c中,有以下定义:


HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);

对于枚举类型HAL_StatusTypeDef,它的结构是这样的


typedef enum
{
  HAL_OK       = 0x00U,
  HAL_ERROR    = 0x01U,
  HAL_BUSY     = 0x02U,
  HAL_TIMEOUT  = 0x03U
} HAL_StatusTypeDef;

第一个参数是需要操作的串口的结构体指针

第二个参数是需要发送的信息的指针。

第三个参数是发送数据的长度,

第四个参数是超时时间(ms)可以使用HAL_MAX_DELAY 来设置最大超时时间。

接收串口数据:


HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

第二个参数是存放接收到的数据的指针,

第三个参数是接收数据的长度

其余和HAL_UART_Transmit()函数一样

轮询模式会造成程序阻塞,建议使用中断模式或者DMA模式。

中断发送模式下不会阻塞程序,可以节约CPU时间,

中断发送模式相比于轮询发送模式,少了个超时时间


HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

每当移位寄存器将一帧数据转移到发送数据寄存器(TDR) 中,会触发一次TDR非空中断。

其中,中断处理函数为:


void USART2_IRQHandler(void);

但是,由于每一次发送或者接收都要调用这个中断,我们一般使用下面这个中断回调函数


void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);

当数据发送完之后,单片机会自动调用此函数

同理,当数据接收完成之后,单片机会自动调用下面这个函数


void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);

DMA发送/接收模式与中断发送/接收模式类似。

串口空闲中断 ——用于接收不定长数据


HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

第一个参数:用于接收的串口指针

第二个参数:存放数据的数组

第三个参数:最大接收长度(一般填写数组的最大长度)

HAL_UARTEx_ReceiveToIdle_DMA(); 的回调函数


void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);

用于接收不定长数据,需要传入接收字节的长度

注意:DMA传输过半也会触发 HAL_UARTEx_RxEventCallback 函数

可以使用


__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);

第一个参数:DMA通道的指针地址

确认是谁触发了回调函数


if (huart == &huart1) function();

IIC 通讯

IIC分为 SDA 数据线SCL 时钟线。SDA用于传输数据,SCL用于传输时钟信号

半双工通讯 主从模式,可以连接多个设备 | 总线协议

IIC通讯例子(以AHT20温湿度传感器为例,STM32和AHT20)

  1. 通讯未开始时,SDA、SCL由外部上拉电阻拉至高电平。

  2. 主机(STM32)将SDA下拉,发送IIC通信启动信号,I2C通讯开始。

  3. 主机(STM32)在SCL为低电平时,设置SDA的电平;从机(AHT20)在SCL为高电平时,读取SDA的电平。

  4. 循环8次(即传输 1字节)后,从机(AHT20)在SCL为低电平的时候,将SDA拉低(发送ACK信号)

  5. 此时,从机(AHT20)地址发送完成。

  6. 双方开始通讯,此时,STM32为从机,AHT20为主机,双方重复步骤4,传输数据。

  7. 主机在SCL为1的时候,将SDA拉高,完成数据的传输。

主机发送从机地址(0x71):

第7位

第6位

第5位

第4位

第3位

第2位

第1位

第0位

0

1

1

1

0

0

0

0

如果主机发起通信的目的是为了设置(写)从机,第0位0

如果主机发起通信的目的是为了从从机读取数据,第0位1


HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);


HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)

第一个参数:外设操纵指针

第二个参数:需要读取的从机地址

接收数据变量的指针

读取多少位数据

超时时间


#include "aht20.h"
#define AHT20_ADDRESS 0x70
​
void AHT20_Init(void) {
    uint8_t readBuffer;
    HAL_Delay(40);
    HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, &readBuffer, 1, HAL_MAX_DELAY);
    if ((readBuffer & 0x08) == 0) {
        uint8_t sendBuffer[3] = {0xBE, 0x08, 0x00};
        HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
    }
}

状态机


void AHT20_Measure() {
    static uint8_t sendBuffer[3] = {0xBE, 0x08, 0x00};
    HAL_I2C_Master_Transmit_IT()
}

状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。有限状态机简写为FSM(Finite State Machine),主要分为2大类:

第一类,若输出只和状态有关而与输入无关,则称为Moore状态机

第二类,输出不仅和状态有关而且和输入有关系,则称为Mealy状态机

typedef enum _Car_StateTypeDef{
    INIT,
    SENDING,
    SENT,
    READING,
    READ
} Car_StateTypeDef;
typedef enum _Car_EventTypeDef{
    INIT,
    SENDING,
    SENT,
    READING,
    READ
} Car_StateTypeDef;

转载请遵循协议:LICENSED UNDER CC BY-NC-SA 4.0
Comment