stm32F407HAL库学习-USART串口
前期准备:
- stm32F407ZGT6开发板
- STM32CubIDE软件
- 串口:使用USART1,PA9、PA10
1、工程创建
1、设置RCC
设置为告诉外部时钟HSE ,选择外部时钟源

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 一栏使能接收中断

3、设置时钟

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
16int 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
7void 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
32typedef 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当前的下一个位置,并且用%的方法来循环读取缓冲区。