0%

stm32F407HAL库学习-USART串口

stm32F407HAL库学习-USART串口

前期准备:

  • stm32F407ZGT6开发板
  • STM32CubIDE软件
  • 串口:使用USART1,PA9、PA10

1、工程创建

  • 1、设置RCC

    • 设置为告诉外部时钟HSE ,选择外部时钟源

      ![](屏幕截图 2025-02-28 092839.png)

  • 2、设置串口

    • 点击USART1

    • 设置Mode为异步通信(Asynchronous)

      • 其他模式选项包括:
        • Synchronous(同步模式):用于需要时钟信号的通信,适合与 SPI 或其他同步设备通信。
        • Single Wire (Half-Duplex)(单线半双工):使用单根数据线进行双向通信,常见于节省引脚的应用。
        • Multiprocessor Communication(多处理器通信):用于多个设备通过同一总线通信。
        • IrDA(红外数据通信):用于红外通信。
        • LIN(本地互联网络):用于汽车电子领域的低速通信。
        • SmartCard(智能卡):用于与智能卡通信。
    • 基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1 接收和发送都使能

    • GPIO引脚设置 USART1_RX/USART_TX

    • NVIC Settings 一栏使能接收中断

      ![](屏幕截图 2025-02-28 093134.png)

  • 3、设置时钟

    ![](屏幕截图 2025-02-28 093359.png)

  • 4、生成代码

重新定义Printf函数

  • 在main.c文件中加入下面代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include "stdio.h"

    #ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif

    PUTCHAR_PROTOTYPE
    {
    HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    return ch;
    }

中断方式接收数据

1、USART中断接收的基本原理

USART(通用同步异步收发器)shi为控制器常用的通信模块,通过串口与外部设备进行通信。中断接收是当USART接收到数据时,硬件会触发一个中断,通知CPU执行预定义的回调函数来处理数据。

2、stm32HAL库中的中断接收

  • 函数原型:

    1
    HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
    • huart:UART 句柄,例如 &huart1,指向已初始化的 UART 实例。
    • pData:接收数据的缓冲区指针。
    • Size:期望接收的字节数。
  • 工作机制:

    • 调用HAL_UART_Receive_IT后,硬件会收指定size的数据后触发中断
    • 当中断发生后,HAL库会调用回调函数,HAL_UART_RxCpltCallback。
    • 在回调函数中,可以处理接收到的数据,并重新调用HAL_UART_Receive_IT 继续接收。

3、中断接收实现

实现一个基于环形缓冲区的UART中断接收机制。

  • 初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int main(void)
    {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init(); // 初始化 USART1
    uint8_t rxBuffer[1] = {0};
    HAL_UART_Receive_IT(&huart1, rxBuffer, 1); // 启动中断接收
    while (1)
    {
    while (!UART_RX_IsEmpty()) {
    uint8_t data = UART_RX_GetByte();
    HAL_UART_Transmit(&huart1, &data, 1, 100); // 回显数据
    }
    }
    }
    • MX_USART1_UART_Init:初始化USART1
    • HAL_UART_Receive_IT(&huart1, rxBuffer, 1);:启动中断接收,每次接收1字节并存储到rxBuffer中。
  • 中断回调处理

    1
    2
    3
    4
    5
    6
    7
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    if (huart->Instance == USART1) {
    UART_RX_PutByte(rxBuffer[0]); // 将接收到的字节存入环形缓冲区
    HAL_UART_Receive_IT(&huart1, rxBuffer, 1); // 重新启动接收
    }
    }
    • 数据储存:将接收的数据存入环形缓冲区。
    • 持续接收
  • 环形缓冲区管理

    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
    31
    32
    typedef struct {
    uint8_t buffer[UART_RX_BUFFER_SIZE];
    volatile uint16_t head; // 写指针
    volatile uint16_t tail; // 读指针
    } UART_RX_CircularBuffer;

    // 判断缓冲区是否满
    bool UART_RX_IsFull(void) {
    return ((uart_rx_buffer.head + 1) % UART_RX_BUFFER_SIZE) == uart_rx_buffer.tail;
    }
    // 判断缓冲区是否空
    bool UART_RX_IsEmpty(void) {
    return (uart_rx_buffer.head == uart_rx_buffer.tail);
    }

    // 写入一个字节到缓冲区
    void UART_RX_PutByte(uint8_t data) {
    if (!UART_RX_IsFull()) {
    uart_rx_buffer.buffer[uart_rx_buffer.head] = data;
    uart_rx_buffer.head = (uart_rx_buffer.head + 1) % UART_RX_BUFFER_SIZE;
    }
    }

    // 从缓冲区读取一个字节
    uint8_t UART_RX_GetByte(void) {
    uint8_t data = 0;
    if (!UART_RX_IsEmpty()) {
    data = uart_rx_buffer.buffer[uart_rx_buffer.tail];
    uart_rx_buffer.tail = (uart_rx_buffer.tail + 1) % UART_RX_BUFFER_SIZE;
    }
    return data;
    }
    • 环形缓冲区:通过head和tail指针实现循环使用,避免数据覆盖。

    • 中断安全:head和tail使用volation修饰,请避免了错误

    • UART_RX_IsFull :判断缓冲区是否已满。

    • UART_RX_IsEmpty:判断缓冲区是否已空

    • UART_RX_PutByte:写入一个字节到缓冲区,如果未满,则写入head当前的下一个位置,并且用%的方法来循环写入缓冲区。

    • UART_RX_GetByte:读取一个字节到缓冲区,如果未满,则读取tail当前的下一个位置,并且用%的方法来循环读取缓冲区。