STM32-UART-中断
UART简介:
UART(Universal Asynchronous Receiver/Transmitter)是一种通用异步收发传输器,作为一种串行、异步通信协议,它允许设备通过两条数据线(TX和RX)实现全双工通信。由于其简单、灵活和低成本的特点,UART被广泛应用于微控制器、计算机外围设备以及各种嵌入式系统中。
协议:
UART帧结构:
协议有关信息参考了如下地址信息:
https://dreamsourcelab.cn/logic-analyzer/uart/
1位低电平(逻辑'0')。标志着数据帧的开始,通知接收方开始采样数据。
实际要传输的数据,低位(LSB)先行。长度可配置。5-9位(通常为8位)
可选,1位,用于简单的错误检测,奇校验、偶校验或无校验
高电平(逻辑'1')。标志着数据帧的结束,并帮助校正时钟同步。
要使两个UART设备成功通信,它们的以下参数必须严格匹配:
- 波特率 (Baud Rate): 这是最重要的参数,表示每秒传输的比特数(bps)。常见值有9600, 19200, 115200等。双方波特率必须一致,且误差通常需控制在10%以内(一些严格要求下需≤3%)。
- 数据位长度: 通常设置为8位。
- 校验位: 根据可靠性需求选择奇校验、偶校验或无校验。
- 停止位长度: 通常设置为1位。
有效数据速率 (Byte/s) = 比特率 (bps) / 每帧总位数
无校验位情况(1起始位 + 8数据位 + 1停止位 = 10位):
有效数据速率 = 波特率 / 10
例如,波特率9600 bps时,有效数据速率 = 9600 / 10 = 960 字节/秒有校验位情况(1起始位 + 8数据位 + 1校验位 + 1停止位 = 11位):
有效数据速率 = 波特率 / 11
例如,波特率9600 bps时,有效数据速率 ≈ 9600 / 11 ≈ 872.73 字节/秒
需要注意通信双方设备支持的波特率必须匹配,如果不匹配会导致如下情况
详细的过程如下:
- 空闲位:当没有信息的情况下为高电平,即高电平时为空闲信号:
- 起始位:当从高电平到低电平,即告知开始传输数据,结束空闲
- 数据位:如果使用了奇偶校验为5~8位,没有则为5~9位,默认为8位,起始位后开始传输数据
- 校验位:提高传输的可靠性
- 停止位:1~2位的高电平表示结束
中断:
stm32f1xx_it.c:USART1_IRQHandler
SART1_IRQHandler 是硬件中断的入口
这里是由硬件触发的中断,在内部调用了stm32f1xx_hal_uart.c的HAL_UART_IRQHandler方法
HAL_UART_IRQHandler
HAL_UART_IRQHandler 就像一个大管家,当中断发生时,它会检查是哪个标志位触发了中断,然后进行相应的处理,比如读取数据、填充下一个要发送的数据或清除标志位等,并在适当的时候调用对应的回调函数(Callback),用户的应用程序逻辑通常就在这些回调函数中实现
回调函数:
stm32f1xx_hal_uart.c内部存在如下回调函数:
__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
__weak void HAL_UART_MspDeInit(UART_HandleTypeDef *huart)
__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
对应解释如下:
函数名称 | 触发时机 | 主要用途 |
---|---|---|
HAL_UART_MspInit(UART_HandleTypeDef *huart) | 在 HAL_UART_Init() 中被调用 | 完成特定MCU的底层初始化(如时钟、GPIO、NVIC) |
HAL_UART_MspDeInit(UART_HandleTypeDef *huart) | 在 HAL_UART_DeInit() 中被调用 | 反初始化MCU相关的底层设置(如关闭时钟) |
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) | 发送完成(包括使用中断和DMA模式) | 处理数据发送完成后的工作,如通知主程序、启动下一次发送 |
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) | 接收完成(达到预设长度,中断或DMA模式) | 处理接收到的数据,如解析协议、存储数据 |
HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) | 发生错误(如溢出、帧错误、噪声、奇偶校验错误等) | 错误处理与恢复,如重新初始化串口、清除错误标志 |
HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) | 接收到特定字节数或检测到总线空闲(IDLE) | 处理不定长数据接收 |
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) | DMA发送完成一半(如100字节才发完50字节) | 可用于双缓冲区操作,在发送一半数据时提前准备后半部分数据 |
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) | DMA接收完成一半 | 可用于双缓冲区操作,在接收一半数据时提前处理前半部分数据 |
HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart) | 中止传输操作完成(如调用 HAL_UART_Abort ) | 处理传输被中止后的清理工作 |
HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart) | 中止发送操作完成 | 处理发送中止后的特定工作 |
HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart) | 中止接收操作完成 | 处理接收中止后的特定工作 |
上述三个对应关系如下:
使用:
阻塞模式:
阻塞模式下,CPU会一直等待数据发送或接收完成,期间无法执行其他任务。
主要使用HAL_UART_Transmit和HAL_UART_Receive两个函数处理
int main(void)
{/* USER CODE END 2 */uint8_t txData[] = "Hello, UART (Blocking)!\r\n";uint8_t rxData[10];/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */// 发送数据,一直等待直到发送完成HAL_UART_Transmit(&huart1, txData, sizeof(txData)-1, HAL_MAX_DELAY);// 接收10字节数据,一直等待直到收齐HAL_UART_Receive(&huart1, rxData, 10, HAL_MAX_DELAY);/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
中断模式:
中断模式允许CPU在等待数据传输时处理其他任务,数据传输完成后由中断通知CPU。
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"/* USER CODE BEGIN PV */
// 通常在初始化后启动一次接收中断,准备接收数据
// 用于中断接收的变量
uint8_t rx_byte; /* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP */// 接收中断回调函数(当数据接收到时自动调用)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart1) { // 判断是哪个串口触发的回调// 处理接收到的数据,例如回显或存入缓冲区// 注意:回调函数中应避免耗时操作 回显收到的字符HAL_UART_Transmit(&huart1, &rx_byte, 1, 100); // 重新启动接收中断,以便持续接收[1,5](@ref)HAL_UART_Receive_IT(&huart1, &rx_byte, 1);}
}// 发送完成回调函数(可选)
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart1) {// 发送完成,可进行后续操作}
}
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_I2C2_Init();MX_SPI2_Init();MX_USART1_UART_Init();// 发送数据(非阻塞,完成后会进入中断回调函数)uint8_t txData[] = "Hello, UART (Interrupt)!\r\n";HAL_UART_Transmit_IT(&huart1, txData, sizeof(txData)-1);// 启动接收中断,期望接收1字节数据[1,5]HAL_UART_Receive_IT(&huart1, &rx_byte, 1);/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
DMA模式(CPU效率最高,适合大数据量)
DMA(直接存储器访问)模式允许外设和内存之间直接传输数据,几乎不占用CPU资源。
对应的设置如下:
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"/* USER CODE BEGIN PV */
// 通常在初始化后启动一次接收中断,准备接收数据
uint8_t receiveData[50];// uint8_t received[2];
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *///检测到总线空闲(IDLE) 或接收满指定长度时,HAL库会自动调用此函数。huart为触发中断的串口句柄,Size为本次实际接收到的数据长度。
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {if (huart == &huart1) {// DMA发送数据。将存储在当前receiveData缓冲区中、长度为Size的数据,通过DMA方式从huart1发送出去。HAL_UART_Transmit_DMA(&huart1, receiveData, Size);// 重启DMA空闲中断接收。这是实现连续不定长接收的关键。它重新配置DMA,等待下一次空闲中断或缓冲区满的事件HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, sizeof(receiveData));// 禁用DMA半传输中断。DMA_IT_HT(Half Transfer)中断在DMA传输完成一半时触发。由于此代码逻辑只在接收完成时处理,不需要在半传输时处理,因此禁用此中断可以避免不必要的干扰__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);}
}/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_I2C2_Init();MX_SPI2_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */// HAL_Delay(500);/* USER CODE END 2 */HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, sizeof(receiveData));__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
其中__HAL_DMA_DISABLE_IT 通过操作 DMA 通道的配置控制寄存器 (CCR) 中的特定中断使能位来实现功能。
它的两个参数分别是:
**__HANDLE__**:指向 DMA_HandleTypeDef 结构体的指针,该结构体包含了所有配置信息。
**__INTERRUPT__:指定要禁用的中断类型**,可以是以下值的组合:
DMA_IT_TC:传输完成中断 (Transfer Complete)
DMA_IT_HT:半传输中断 (Half Transfer)
DMA_IT_TE:传输错误中断 (Transfer Error)