KeySking笔记总结:
串口通讯三大方式
1.串口轮询
2.串口中断
3.DMA处理串口交互
1.串口轮询
轮询模式内部的底层:每个stm32接收和发送端口各存在有两个寄存器,一个为等待发送寄存器(TDR)
,一个为发送移位寄存器,我们调用HAL_UART_Transmit函数将数据在发送时,cpu会将数据先放在发送寄存器中,发送寄存器将数据给到发送移位寄存器,发送移位寄存器中的数据按照我们设置的比特率转换为高低电平从TX引脚输出。
在此期间,CPU不断去查询,发送寄存器中的数据是否已经移到了发送移位寄存器中。移动了就再把数据给到发送数据寄存器。
位于接收端,cpu不断的去查询接收数据寄存器中是否有新的数据可以读,一旦检测到就马上把数据从接收数据寄存器中移到内部变量里面去。
此模式的缺点,CPU一直被用来检测寄存器中有无数据,直到接收到我们想要的字节数,或者接收超时,否则一直会阻塞程序正常运行。
其中调用的函数第二个参数为变量地址,由于变量创建的为数组,直接就是地址就不需要添加取地址符号了。
2.串口的中断模式
串口的中断模式机制:与串口轮询不同的是,CPU不会反复去检查数据寄存器是否有发出或接收到,而是发送/接收寄存器在处理完1字节的数据后,会触发中断,cpu回来将数据在塞入/取出后继续处理主程序的其他任务。。如此反复完成数据的交互。
cubmx配置:(配置同步/异步模式;波特率等基础配置)
开启NVIC 使能USART2中断
关键代码如下:
注: HAL_UART_Receive_IT(&huart2, &message, size); HAL_UART_Transmit_IT(&huart2,&message, size);
是中断形式的UART接收/发送数据的函数,由于不阻塞主程序因此不需要设置超时时间。
在初始化过程中先要调用一次
HAL_UART_Receive_IT(&huart2, recvDate, 2);
然后在Rx回调函数中进行数据处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { HAL_UART_Transmit_IT(&huart2,recvDate,2); GPIO_PinState pinstate= GPIO_PIN_RESET; if(recvDate[1] == '1'){ pinstate = GPIO_PIN_SET; } if(recvDate[0] == 'R'){ HAL_GPIO_WritePin(redLED_GPIO_Port, redLED_Pin, pinstate); } else if(recvDate[0] == 'B'){ HAL_GPIO_WritePin(blueLED_GPIO_Port,blueLED_Pin, pinstate); } else if(recvDate[0] == 'G'){ HAL_GPIO_WritePin(greenLED_GPIO_Port,greenLED_Pin, pinstate); } HAL_UART_Receive_IT(&huart2, recvDate, 2); }
3、DMA 模式
由于CPU频繁中断,DMA是一个特殊的功能,可以将数据从附加设备中直接发送至计算机内存中。
配置DMA通道方法如下图所示:
将传输和发送数据的函数修改为:
HAL_UART_Receive_DMA(&huart2,&message,size); HAL_UART_Transmit_DMA(&huart2,&message,size);
使用DMA进行数据传输的过程中应使用HAL_UARTEx_RxEventCallback函数,但数据过半也会触发该中断函数,应该手动关闭DMA传输过半中断。
// 不定长数据接收完成回调函数 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart->Instance == USART2) { // 使用DMA将接收到的数据发送回去 HAL_UART_Transmit_DMA(&huart2, rx_data, Size); // 重新启动接收,使用Ex函数,接收不定长数据 HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rx_data, sizeof(rx_data)); // 关闭DMA传输过半中断(HAL库默认开启,但我们只需要接收完成中断) __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT); } }