前言
stm32 uart常用的收发有轮询、中断、DMA
轮询方式
-
关键函数
HAL_UART_Receive //UART接收函数
HAL_UART_Transmit //UART发送函数 -
使用如下,直接在while循环里调用这两个函数即可实现固定长度数据接受与发送,缺点是需要CPU主动去轮询,非常消耗CPU资源,轮询属于阻塞方式,CPU一直在这里等着(在没有到达超时时间时)
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_UART_Receive(&huart1, receiveData, 2, HAL_MAX_DELAY);
HAL_UART_Transmit(&huart1, receiveData, 2, HAL_MAX_DELAY);
}
中断方式
- 关键函数
HAL_UART_Transmit_IT //中断方式发送
HAL_UART_Receive_IT //中断方式接收
USART1_IRQHandler //USART1全局中断处理函数
HAL_UART_RxCpltCallback //USART1接收完成回调函数 - 使用如下,需要在主程序进入while循环前开启中断接收,由于中断接收是不阻塞CPU的,所以,代码继续执行至while内部。
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, receiveData, 2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//HAL_UART_Receive(&huart1, receiveData, 2, HAL_MAX_DELAY);
//HAL_UART_Transmit(&huart1, receiveData, 2, HAL_MAX_DELAY);
}
/* USER CODE END 3 */
}
while内部不要写中断接收,因为中断接收属于非阻塞型,代码继续往后走,很有可能本次接收还没接收完毕呢,就又开启下次接收了,数据被覆盖掉。
当RX上有数据时,会触发USART1的中断,进而依据中断向量表走进对应的中断处理函数USART1_IRQHandler,在中断处理函数中调用了HAL_UART_IRQHandler(&huart1)
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
HAL这个函数封装的极其高度,我们不需要再像标准库编程那样去查uart寄存器状态,判断是否有数据,清中断,开启中断等底层操作,只用关心业务逻辑即可。这个中断处理函数里面有各种回调函数,发送完成回调,发送半完成回调,接收完成回调,接收半完成回调等等,这些函数都是弱定义,需要我们自己实现,尽量不要在库函数中添加自己的代码
我们直接把这个接口复制出来,放在mian.c文件中定义,这里定义把接收到内容直接原路发送出去,同时再次开启接收中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_IT(&huart1, receiveData, 2);
HAL_UART_Receive_IT(&huart1, receiveData, 2);
}
DMA方式
中断方式还是有CPU参与的,uart上有数据时把CPU叫回来,没数据时,CPU去干其他的事,但是还有一种更好的方式就是给CPU请一个小秘书DMA,同时实现不定长数据的接收
在cubemx中开启uart的tx rx DMA传输通道
如何实现呢,很简单,把之前HAL_UART_Transmit_IT改成HAL_UART_Transmit_DMA,HAL_UART_Receive_IT改成HAL_UART_Receive_DMA即可
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//HAL_UART_Receive_IT(&huart1, receiveData, 2);
HAL_UART_Receive_DMA(&huart1, receiveData, 2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
...
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//HAL_UART_Transmit_IT(&huart1, receiveData, 2);
//HAL_UART_Receive_IT(&huart1, receiveData, 2);
HAL_UART_Transmit_DMA(&huart1, receiveData, 2);
HAL_UART_Receive_DMA(&huart1, receiveData, 2);
}
DMA方式实现不定长度接收
不定长数据的接收主要使用的时空闲中断,即当rx上没有数据时,触发一次DMA传输
关键函数
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, 128);
但是此时的回调函数就不是
HAL_UART_RxCpltCallback 而是 HAL_UARTEx_RxEventCallback
代码实现也很简单
/* USER CODE BEGIN 2 */
//HAL_UART_Receive_IT(&huart1, receiveData, 2);
//HAL_UART_Receive_DMA(&huart1, receiveData, 2);
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, 128);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
...
...
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
HAL_UART_Transmit_DMA(&huart1, receiveData, Size);
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, 128);
}