实现:控制小灯亮灭 发送R1亮 ## 一、搬运小助手DMA cpu每次触发非空中断就要跑回来搬运数据,也会占用cpu的性能,所以可以调用DMA(直接内存访问)来代替cpu进行搬运 **开启DMA方法:** 1.串口端口中添加DMA TX和RX的通道,然后 2..使用发送**HAL\_UART\_Transmit\_DMA(&huart1, receiveDate, 2);** 接收:**HAL\_UART\_Receive\_DMA(&huart1, receiveDate, 2);** 这2个函数就是在串口中断收发IT上改成DMA,就变成了中断DMA收发,但是中断回调函数还是**void HAL\_UART\_RxCpltCallback**,这个就不是串口的中断了,DMA会调用自己的传输完成中断。 ## 代码示例: /* USER CODE BEGIN PV */ uint8\_t receiveDate\[2\]; \* USER CODE END PV *\ /* USER CODE BEGIN 0 */ void HAL\_UART\_RxCpltCallback(UART\_HandleTypeDef \*huart){ HAL\_UART\_Transmit\_DMA(&huart1, receiveDate, 2); GPIO\_PinState state = GPIO\_PIN\_SET; //定义gpio\_pinstate类型的state变量,默认成高电平 if(receiveDate\[1\] == '0'){ state = GPIO\_PIN\_RESET; } //判断receivedate的第0位数字是不是0,如果是0就设置成低电平,如果不是则保持高电平 if(receiveDate\[0\] == 'R'){ HAL\_GPIO\_WritePin(LED\_RED\_GPIO\_Port, LED\_RED\_Pin, state); }else if(receiveDate\[0\] == 'G'){ HAL\_GPIO\_WritePin(LED\_RED\_GPIO\_Port, LED\_RED\_Pin, state); }else if(receiveDate\[0\] == 'B'){ HAL\_GPIO\_WritePin(LED\_RED\_GPIO\_Port, LED\_RED\_Pin, state); } //判断第二位是R,G,B,则输出相应端口高电 HAL\_UART\_Receive\_DMA(&huart1, receiveDate, 2); //继续接收 } /* USER CODE END 0 */ /\* USER CODE BEGIN 2 \*/ HAL\_UART\_Receive\_DMA(&huart1, receiveDate, 2); /\* USER CODE END 2 \*/ ## 二、接收不定长数据 **1.原理** 主要是靠 串口空闲中断(ldle) 来接收不定长数据,只有当串口从忙碌转为空闲时才会触发。空闲中断发生时,就判断数据接收完了,然后进行数据处理。 **2.使用方法** 2.1接收数据函数:HAL\_UARTEx\_ReceiveToIdle\_DMA(&huart1, receiveDate, sizeof(receiveDate)); 后面长度是判断变量声明时的长度,HAL\_UART\_Transmit\_DMA(huart, receiveDate, sizeof(receiveDate));//把接收的数据发送出去 2.2.这个触发的中断函数是:void HAL\_UARTEx\_RxEventCallback(UART\_HandleTypeDef \*huart, uint16\_t Size) 与上面中断触发的函数唯一区别是传参多了size, 2.3DMA在数据传送过半时也会触发中断,就会导致数据传输不完整,要在每次触发时关闭一下传输过半中断,关闭过半中断代码:\_\_HAL\_DMA\_DISABLE\_IT(&hdma\_usart1\_rx,DMA\_IT\_HT); 2.4. 代码示例: /\* USER CODE BEGIN PV \*/ uint8\_t receiveDate\[50\]; //全局变量长度50 /\* USER CODE END PV \*/ /\* USER CODE BEGIN 0 \*/ void HAL\_UARTEx\_RxEventCallback(UART\_HandleTypeDef \*huart, uint16\_t Size){ //中断处理函数 if(huart == &huart1){//确认是谁触发了回调函数 HAL\_UART\_Transmit\_DMA(huart, receiveDate, sizeof(receiveDate));//把接收的数据发送出去 HAL\_UARTEx\_ReceiveToIdle\_DMA(huart, receiveDate, sizeof(receiveDate));//再次启用接收函数 \_\_HAL\_DMA\_DISABLE\_IT(&hdma\_usart1\_rx, DMA\_IT\_HT); } } /\* USER CODE END 0 \*/ /\* USER CODE BEGIN 2 \*/ HAL\_UARTEx\_ReceiveToIdle\_DMA(&huart1, receiveDate, sizeof(receiveDate)); \_\_HAL\_DMA\_DISABLE\_IT(&hdma\_usart1\_rx,DMA\_IT\_HT); /\* USER CODE END 2 \*/ 错误提示:hdma\_usart2\_rx没找到 如果接收过半中断对你的逻辑判断是没有影响的话, 可以不禁用. 2. 按照视频里的讲解,是没有问题的, 但是如果你在CubeMX的Project Manager->Code Generator里勾选了Generate peripheral initialization as a pair of '.c/.h. files per peripheral, hdma\_usart2\_rx就会定义到usart.c, 并且usart.h中没有声明, 你只需要自己在usart.h中添加extern DMA\_HandleTypeDef hdma\_usart3\_rx; 就好了,或者去MX里取消勾选 ##遇到的问题 1.串口调试输出出现乱码,就是无论你在芯片里面输出什么,在调试助手里看到的要么是半段,要么就是乱码,像下面这样 ![WX20240913-121428@2x.png][1] 解决办法就是:我的j-link下载是连接了3.3v供电的,串口调试ch340也是连接了3.3供电的,两个同时插在电脑上使用,我拔掉串口供电后就正常了,后来发现是我把芯片的3.3接到了串口的5v上面,![WX20240913-123827@2x.png](https://www.hikevin.cc/usr/uploads/2024/09/4286263995.png)把它连接成这样就是正确的,下面就是该插上蓝牙透传模块试试有没有问题了。 ## 学习教程视频地址: [【工作STM32】第10集 STM32串口DMA模式与收发不定长数据 | keysking的stm32教程](https://www.bilibili.com/video/BV1do4y1F7wt/?spm_id_from=333.788&vd_source=4c968b6ff27b9364c4470ab3a17ebc3d) [1]: https://www.hikevin.cc/usr/uploads/2024/09/361635699.png 最后修改:2024 年 09 月 13 日 05 : 00 PM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 ×Close 赞赏作者 扫一扫支付 支付宝支付 微信支付