STM32/STM8技术论坛
直播中

阳羊羊

未满1年用户 5经验值
擅长:可编程逻辑 测量仪表 嵌入式技术 处理器/DSP 控制/MCU RF/无线
私信 关注
[讨论]

记录我的stm32开发学习过程

STM32F103程序开发——串口UART篇(HAL库)

本次实验目标内容:

  • 从零编写UART库函数,分别为UART.c和UART.h文件;
  • 实现stm32单片机串口数据发送功能(UartTransmit()和printf());
  • 实现stm32单片机串口数据接收功能(阻塞接收和中断接收);

条件声明:

本实验的软件开发工具为Keil,基础代码由STM32CubeMX生成。基础代码并没有包括UART的启动初始化代码;

本实验使用的是stm32f103的UART1,对应的引脚为PA9和PA10;


一、UART初始化

UART1初始化:

  • 波特率:115200,可以根据需求修改。
  • 数据位: 配置为 8 位 数据,无奇偶校验。
  • 停止位: 配置为 1 位
  • 时钟和引脚初始化:
    • TX 引脚(PA9) 配置为复用推挽输出。
    • RX 引脚(PA10) 配置为浮空输入。
GPIO_InitTypeDef GPIO_InitStruct = {0};
​
    // 使能 USART1 和 GPIOA 时钟
    USART1_CLK_ENABLE();
    USART1_GPIO_CLK_ENABLE();
​
    // 配置 TX 引脚为复用推挽输出
    GPIO_InitStruct.Pin = USART1_TX_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(USART1_GPIO_PORT, &GPIO_InitStruct);
​
    // 配置 RX 引脚为浮空输入
    GPIO_InitStruct.Pin = USART1_RX_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(USART1_GPIO_PORT, &GPIO_InitStruct);
​
    // 配置 USART1 参数
    huart1.Instance = USART1_INSTANCE;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
​
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        Error_Handler_u();
    }

由于程序中用的宏定义变量和HAL函数都是在“stm32f1xx_hal_uart.c”中,所以需要包含stm32f1xx_hal_uart.h来启动uart库。

需在“stm32f1xx_hal_conf.h”中将#define HAL_UART_MODULE_ENABLED 的注释取消;

image-20241119114212754.png

二、UART发送函数

1.直接调用HAL串口发送函数:

  • huart:使用的串口名;
  • pData:存储发送数据的数组;
  • Size:发送数据的大小;
// 串口发送数据 
void UART1_Transmit(uint8_t *pData, uint16_t Size) 
{    
    if (HAL_UART_Transmit(&huart1, pData, Size, HAL_MAX_DELAY) != HAL_OK)   
    {                
        Error_Handler_u();    // 发送失败,等待
    } 
}

2.将UART1的串口发送映射到printf()

在UART.c 文件中添加下列程序,可以将uart1的输出映射到printf();

#include <stdio.h>
​
UART_HandleTypeDef huart1;
​
// 定义 PUTCHAR_PROTOTYPE 宏,兼容 GCC 和其他编译器
#ifdef __GNUC__
  /* GCC 编译器 */
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  /* 非 GCC 编译器 */
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif// 重定向 printf 输出到 UART
PUTCHAR_PROTOTYPE
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}

优化建议

  • 使用 DMA 模式
    • 如果需要频繁输出大量数据,可以使用 DMA 模式代替阻塞发送,提升性能

三、UART接收数据

1. 阻塞模式接收

在阻塞模式下,程序会等待接收到指定数量的数据后才继续执行。这种方式简单但效率较低,会阻塞 CPU。

实现代码:

void UART_Receive_Blocking(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
    if (HAL_UART_Receive(huart, pData, Size, HAL_MAX_DELAY) != HAL_OK)
    {
        // 接收失败,执行错误处理
        Error_Handler();
    }
}

使用示例:

uint8_t rxData[10];
UART_Receive_Blocking(&huart1, rxData, sizeof(rxData));  // 接收 10 字节数据
//一个测试的数据处理程序
if (HAL_UART_Receive(&huart1, rxData, sizeof(rxData), HAL_MAX_DELAY) == HAL_OK)
{
str[i]=rxData[0];
        if(rxData[0]=='A')
{
j=100;
}
        i++;
}  // 判断是否接收到字母Aif(j==100)
{
UART1_Transmit(str, i);
  GPIO_Toggle_LED();
i=0;
j=0;
}

特点:

  • 简单易用。
  • 适合少量数据接收。
  • 阻塞式等待可能导致 CPU 资源浪费,不适合实时性要求高的场景。

2. 中断模式接收

中断模式更高效,程序在等待数据时不会阻塞,可以执行其他任务。数据接收完成后触发中断,用户可以在回调函数中处理数据。

实现代码:

串口中断配置:

** **UART.c添加变量:

uint8_t rxBuffer[RX_BUFFER_SIZE];  // 数据接收缓冲区
volatile  uint8_t rxComplete = 0;  // 标志位,指示接收完成

** **UART.h添加:

// 接收缓冲区
#define RX_BUFFER_SIZE 10
extern uint8_t rxBuffer[RX_BUFFER_SIZE];  // 数据接收缓冲区
// 标志位,指示接收完成
extern  volatile uint8_t rxComplete;

** **UART初始化部分添加:

HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);  // 设置中断优先级
  HAL_NVIC_EnableIRQ(USART1_IRQn);          // 启用中断

** **在 stm32f1xx_it.c 文件中,将 USART1 的中断处理加入:

// 中断处理函数
void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&huart1);  // 调用 HAL 库的中断处理函数
}

启动接收中断

//USART1 中断开启
void UART1_Start_Receive_IT(void)
{
    if (HAL_UART_Receive_IT(&huart1, rxBuffer, RX_BUFFER_SIZE) != HAL_OK)
    {
        // 启动中断失败,处理错误
        while (1);
    }
}

接收完成回调函数: 当数据接收完成时,HAL 库会调用 HAL_UART_RxCpltCallback(),用户可以在此函数中处理接收到的数据。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        //进入回调函数后中断会停止,如果想要继续使用需要再次启动
        UART1_Start_Receive_IT();
        // 处理 USART1 接收到的数据
        // 比如将接收到的数据存储到一个全局变量
        rxComplete = 1;  // 设置接收完成标志位,可以将复杂的函数放在主函数中通过判断标志位调用。

    }
}

使用示例:

UART_Receive_IT();  // 启动中断接收

// 在主循环中检查接收完成标志
if (rxComplete)
{
    rxComplete = 0;  // 清除标志位
    // 处理接收到的数据
}

特点:

  • 不阻塞 CPU。
  • 适合实时性要求较高的场景。
  • 需要在回调函数中处理接收逻辑。

更多回帖

发帖
×
20
完善资料,
赚取积分