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_CLK_ENABLE();
USART1_GPIO_CLK_ENABLE();
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);
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);
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 的注释取消;
二、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;
#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;
}
优化建议
- 使用 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));
if (HAL_UART_Receive(&huart1, rxData, sizeof(rxData), HAL_MAX_DELAY) == HAL_OK)
{
str[i]=rxData[0];
if(rxData[0]=='A')
{
j=100;
}
i++;
}
if(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);
}
启动接收中断:
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();
rxComplete = 1;
}
}
使用示例:
UART_Receive_IT();
if (rxComplete)
{
rxComplete = 0;
}
特点:
- 不阻塞 CPU。
- 适合实时性要求较高的场景。
- 需要在回调函数中处理接收逻辑。