✅作者简介:热爱科研的嵌入式开发者,修心和技术同步精进
❤欢迎关注我的知乎:对error视而不见
代码获取、问题探讨及文章转载可私信。
☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。
🍎获取更多嵌入式资料可点击链接进群领取,谢谢支持!👇
一、引言
在嵌入式系统开发中,串口通信是一种常见且重要的通信方式,它可以实现微控制器与外部设备(如计算机、传感器等)之间的数据传输。STM32系列微控制器以其高性能、低功耗等特点被广泛应用,而HAL(Hardware Abstraction Layer)库则为开发者提供了一种简单、高效的方式来操作STM32的硬件资源。本文将详细介绍如何使用STM32的HAL库以中断方式实现串口通信。
二、串口通信原理
串口通信是一种按位顺序传输数据的通信方式,分为同步和异步两种类型。在异步串口通信中,数据的发送和接收双方不需要使用共同的时钟信号,而是通过起始位、数据位、校验位和停止位来实现数据的同步。
2.1 数据帧格式
- 起始位:标志着一帧数据的开始,通常为低电平。
- 数据位:要传输的数据,常见的有 5、6、7、8 位。
- 校验位:用于检测数据传输过程中是否发生错误,有奇校验、偶校验和无校验等方式。
- 停止位:标志着一帧数据的结束,通常为高电平,有 1 位、1.5 位或 2 位。
2.2 通信速率
串口通信的速率用波特率来表示,即每秒传输的比特数。常见的波特率有 9600、115200 等。
三、硬件连接
以 STM32F103C8T6 为例,使用 USART1 进行串口通信。硬件连接如下:
- TX(发送端):连接到外部设备的 RX(接收端)。
- RX(接收端):连接到外部设备的 TX(发送端)。
- GND(接地):与外部设备的 GND 相连,确保共地。
四、HAL 库配置
4.1 时钟配置
首先,需要配置系统时钟和串口时钟。在 CubeMX 中,选择合适的外部晶振,配置系统时钟为 72MHz,并使能 USART1 的时钟。
4.2 串口配置
在 CubeMX 的“Pinout & Configuration”选项卡中,选择 USART1,将其模式设置为“Asynchronous”(异步模式),并配置波特率、数据位、校验位和停止位等参数。例如,波特率设置为 115200,数据位为 8 位,无校验位,停止位为 1 位。
4.3 中断配置
在 CubeMX 中,使能 USART1 的接收中断。在“NVIC Settings”选项卡中,勾选“USART1 global interrupt”,并设置中断优先级。
五、代码实现
5.1 生成代码
完成 CubeMX 的配置后,生成代码并导入到 Keil 或其他开发环境中。
5.2 主函数代码
#include "main.h"
#include "usart.h"
#include "gpio.h"
#define BUFFER_SIZE 256
uint8_t rx_buffer[BUFFER_SIZE];
uint8_t rx_index = 0;
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
// 开启串口接收中断
HAL_UART_Receive_IT(&huart1, &rx_buffer[rx_index], 1);
while (1)
{
// 主循环可以处理其他任务
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
5.3 中断处理函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
// 处理接收到的数据
if (rx_index < BUFFER_SIZE - 1)
{
rx_index++;
// 继续开启下一次接收中断
HAL_UART_Receive_IT(&huart1, &rx_buffer[rx_index], 1);
}
else
{
// 缓冲区已满,可进行相应处理
rx_index = 0;
HAL_UART_Receive_IT(&huart1, &rx_buffer[rx_index], 1);
}
}
}
5.4 发送数据函数
void send_data(const char *data)
{
HAL_UART_Transmit(&huart1, (uint8_t *)data, strlen(data), HAL_MAX_DELAY);
}
六、代码解释
6.1 主函数
在主函数中,首先进行系统初始化,包括 HAL 库初始化、系统时钟配置、GPIO 初始化和串口初始化。然后调用 HAL_UART_Receive_IT
函数开启串口接收中断,使能串口接收一个字节的数据,并在接收到数据后触发中断。
6.2 中断处理函数
HAL_UART_RxCpltCallback
是 HAL 库提供的串口接收完成回调函数。当接收到一个字节的数据后,会自动调用该函数。在函数中,首先判断是否是 USART1 产生的中断,然后将接收到的数据存储到缓冲区中,并更新缓冲区索引。如果缓冲区未满,则继续开启下一次接收中断;如果缓冲区已满,则清空缓冲区并重新开始接收。
6.3 发送数据函数
send_data
函数用于向串口发送数据。通过调用 HAL_UART_Transmit
函数,将指定的数据发送出去。
七、注意事项
- 缓冲区溢出:在使用中断方式接收数据时,要注意缓冲区的大小,避免缓冲区溢出。可以根据实际需求调整缓冲区的大小。
- 中断优先级:合理设置中断优先级,避免高优先级中断长时间占用 CPU,导致低优先级中断无法及时处理。
- 数据处理:在中断处理函数中,尽量避免进行复杂的操作,以免影响中断响应时间。可以将数据处理任务放到主循环中进行。
八、总结
通过使用 STM32 的 HAL 库以中断方式实现串口通信,可以提高系统的实时性和效率。在实际应用中,可以根据需求扩展功能,如添加数据解析、错误处理等。同时,要注意代码的优化和调试,确保系统的稳定性和可靠性。