接上文,我们在LED灯的基础上加入串口中断,首先在CubeMX中使能DMA和USART1,串口保持默认配置即可,打开串口中断:
我们更新代码后打开keil软件,在工程中新建一个uart1recv.c文件以及该文件的.h文件:
在uart1recv..h文件中添加以下代码:
#ifndef __UART1RECV_H
#define __UART1RECV_H
void Uart1Recv_Task(void const * argument);
#endif /* __UART1RECV_H */
uart1recv.c文件中添加以下代码:
#include "uart1recv.h"
#include "cmsis_os.h"
#include "usart.h"
uint8_t UART1_RxBuf[256];
uint8_t UART1_TxBuf[256];
volatile uint16_t UART1_RecvLength=0;
volatile uint16_t UART1_SendLength=0;
void Uart1Recv_Task(void const * argument)
{
osEvent event;
osStatus status;
for(;;)
{
event=osSignalWait(0x01,osWaitForever);//接收任务通知
if(event.status==osEventSignal)
{
if(event.value.signals==0x01)
{
UART1_SendLength=UART1_RecvLength;
HAL_UART_Transmit_DMA(&huart1,UART1_TxBuf,UART1_SendLength);
UART1_TxBuf[0]+=1;
}
}
// HAL_UART_Receive_DMA(&huart1,UART1_RxBuf,UART1_RxBuf_SIZE );
// osDelay(100);
}
}
函数解析:
这里有两个新的知识点:任务通知和任务接收函数,在串口中断的回调函数中,我们发出了任务通知,在这里我们收到任务通知后,可以对收到的数据根据自己的需求进行处理,我们这里为了演示方便,返回的数据为输入缓冲区第0个字节每次加1,返回的数据长度等于收到的数据长度,文章末尾我们给出了示例图片。
同样的我们需要在freertos.c文件中添加头文件,定义任务句柄ID,创建线程:
/* USER CODE BEGIN Includes */
#include "led.h"
#include "uart1recv.h"
/* USER CODE END Includes */
/* USER CODE BEGIN Variables */
osThreadId Uart1Recv_Handle;
osThreadId LED_Task_Handle;
/* USER CODE END Variables */
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
osThreadDef(UART1_RECV, Uart1Recv_Task, osPriorityHigh, 0, 128);
Uart1Recv_Handle = osThreadCreate(osThread(UART1_RECV), NULL);
osThreadDef(LED, LED_Task, osPriorityNormal, 0, 128);
LED_Task_Handle = osThreadCreate(osThread(LED), NULL);
/* USER CODE END RTOS_THREADS */
在串口的初始化函数MX_USART1_UART_Init()中,配置接收方式为:
/* USER CODE BEGIN USART1_Init 2 */
__HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_IDLEF);
__HAL_UART_CLEAR_OREFLAG(&huart1);
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, UART1_RxBuf, UART1_RxBuf_SIZE);
/* USER CODE END USART1_Init 2 */
在main.c文件中重写IDLE中断事件函数:
//事件回调函数
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
uint16_t temp;
if (huart->Instance == USART1)
{
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
UART1_RecvLength = UART1_RxBuf_SIZE - temp;
osSignalSet(Uart1Recv_Handle,0x01);//发送任务通知(中断中的任务通知)
}
}
最后在stm32l4xx_it.c文件中还需要重新设置DMA接收方式:
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 */
//重新开启串口空闲中断和DMA接收,一定要放在这里
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,UART1_RxBuf, UART1_RxBuf_SIZE);
/* USER CODE END USART1_IRQn 1 */
}
新版的HAL库更新后,在中断处理函数中增加了接收模式检查,如果采用了DMA+空闲中断的方式(这是我们开始在串口初始化函数中配置的),我们便可以通过重写HAL_UARTEx_RxEventCallback(huart, nb_rx_data)该函数,来实现不定长数据的接收,比以往我们修改底层驱动程序的方法(详见STM32 HAL库串口+DMA接收不定长数据_stm32h743串口不定长dma收发-优快云博客)更简单,更便捷。