现象:在子函数中,定义了一个局部变量sendbuf[8] ={1,2,3,4,5,6,7,8},然后分别利用普通串口发送函数发送可以正常发送和利用DMA发送,并利用串口调试助手查看,发现助手可以正确接收普通串口发送函数所发出的数据,而对于DMA发送的数据,接收到的是一堆乱码。
原因:
1.普通串口发送函数是阻塞型的,比如将上述sendbuf[8]每一个字节发送出去,低层代码逻辑是将1移位到串口的TX寄存器里,硬件检测到后在适当的时间发送出去。在未发送出去前,需要判断TX寄存器是否为空,即判断标志位USART_FLAG_TXE或者USART_FLAG_TC,如果没有发送成功就while阻塞(hal库中升级了,hal库设定超时机制,如果等待时间过长,就直接返回超时错误),一直阻塞到1发送出去后,再将2放到TX的寄存器里,一直将sendbuf[8]里的数发完为止。因此只要没有发送完,子函数就无法返回,在发送期间局部变量数组sendbuf是一直存在的。直到发送完成后子函数返回。
2.DMA发送是非阻塞的。在子函数中调用DMA发送一个数组时,实际上是将数组的地址给DMA,告诉单片机,从这个地址开始的N位数是需要发的,你有空的时候发出去就行。因此当你调用DMA发送函数的时候,你只是下达了一个需要发送的命令,但是数据并没有即时发送出去,程序也不会阻塞在DMA发送函数,子函数直接返回了。返回以后局部变量sendbuf将失去意义。sendbuf所指向的地址里的数据是啥你根本无法知晓。当单片机闲下来了觉得自己可以去按照地址去寻这些数的时候,这些数早就物是人非了。发出去的数也就是乱码的
一些BUG现象:
对于不清楚DMA是非阻塞机制的朋友,有的会尝试打断点一行一行往下顺,发现一行一行顺的时候,发出来的数据是对的;但是全速运行的时候就是错的。错的原因上面已经解释了,对于一行一行顺的时候为什么成功,因为你人工形成了程序阻塞,逼着单片机在子函数返回之前去读地址里的数,不发出去你不返回,单片机没办法只能照办。但一全速就飞了。
解决方案:
如果了解了DMA非阻塞机制,怎么解决其实很简单,切入点就是,寻址数要在。当单片机闲下来,去寻找地址里的数时,那个地址要还是那个数。不能因为子函数返回就没了。所以解决方案很简单,发送数组设置成全局变量数组就行。当子函数返回时,你不用催着单片机发送,数组里的数也不会飞。等到单片机想发送的时候,去寻数也能找到。