有人发现当DMA配置为normal模式,一轮传输完成后再使能同一DMA数据流时,即使有DMA请求产生,DMA根本不进行数据传输。这里不妨以STM32F4为例聊聊该话题。
其实,在非循环模式下配置的DMA数据流传输结束后(即要传输的数据数目达到零),除非软件重新对数据流编程并使能该数据流(通过将 DMA_SxCR 寄存器中的 EN 位置 1),否则DMA 会停止传输(硬件会将 DMA_SxCR 寄存器中的EN 位清零)并不再响应任何 DMA 请求。也就是说,当一轮DMA传输完成后,简单来一句 __HAL_DMA_ENABLE(hdma)并不能再次重启DMA传输,必须先行对数据流重新进行初始化配置,然后使能该DMA数据流。
不过,要对STM32F4系列内部各DMA数据流进行配置,首先要保证 DMA_SxCR 寄存器中的 EN 位已被清零。一般来讲,在进行DMA数据流配置前,先对该EN位进行写0去禁用该DMA流,然后读取此位以确认没有正在进行的数据流传输操作。将此位写 0 可能不会立即生效,因为只有当前所有传输都已完成时才会将其写为 0。当读取到 EN 位为 0 时,才可以配置DMA数据流。配置完成后,如果是再次配置的话,记得将先前的DMA 传输中在状态寄存器(DMA_LISR 和 DMA_HISR)中置 1 的所有数据流专用位置0, 然后重新使能数据流,即将 DMA_SxCR 寄存器中的 EN位置 1 激活数据流。
OK,这里拿个实际案例看看以加深下印象。
有人用STM32F4芯片做产品,他是基于STM32CUBE库做应用开发。用DMA做USART通信的数据传输,DMA配置为循环模式。 他发现HAL_UART_Transmit_DMA()这个函数在做了一次DMA配置后,第二次使用它更新配置时无法生效。比如按下面步骤操作:
【1】HAL_UART_Transmit_DMA(&huart1,DataBuff, 10); 【2】HAL_UART_DMAPause(&huart1); 【3】 HAL_UART_Transmit_DMA(&huart1,&DataBuff[5],5); 【4】HAL_UART_DMAResume(&huart1);
开发者的目的是希望先将DataBuff[0]至DataBuff[9]10个数据传到串口上去。中途调整DMA配置[即第【3】句],然后发送DataBuff[5]至DataBuff[9]的5个数据到串口助手上去。
结果发现经过2次配置后的结果没变,发送的都是DataBuff[0]至DataBuff[9]的10个数据。为什么呢?不妨一起看看。 上面第【1】句做usart TX的DMA传输配置。循环从DataBuf处开始取10个数据送往串口;
接着第【2】句是在DMA完成中断里进行。暂停USART TX传输的DMA请求,并延时2S; 然后第【3】句再做USART TX的DMA配置,循环从&DataBuf【5】处开始取5个数据送往串口; 最后第【4】句重新开启USART TX的DMA传输;
按照上面几步也貌似条理清晰,有根有据。可结果为什么发现第【3】句的二次配置没法生效呢?
从前面的描述我们得知,如果需要对某STREAM进行DMA配置的话,首先须DISABLE该STREAM,即先对DMA_SxCR寄存器的EN位清零。而且这个清零并不一定立即生效,必须保证该STREAM当前没有在进行DMA传输。所以,做清零操作后,还要去读该EN位,直至读到该位为0后方能对该DMA STREAM 进行再次配置。 HAL_UART_Transmit_DMA()函数里有对DMA_SxCR的EN位清零的动作,不过没有读取确认的动作。复位后DMA_SxCR寄存器的EN位默认是0毫无疑问,所以复位后第【1】句用HAL_UART_Transmit_DMA()函数配置后生效自然没问题。
上面的第【2】句在DMA完成中断里进行,暂停UART TX的DMA请求,还延时了2S。本意是想让DMA停下来,为后面二次配置做准备。但他这里只是暂停了DMA请求,加上这里配置DMA为循环模式,也就是说在进入DMA完成中断时,DMA又做了下一轮传输的准备,其中包括传输数据项数目寄存器DMA_SxNDTR的重装。此时尽管暂停了DMA请求,但该DMA流是处于传输未完成状态。
那么紧跟着的第【3】句对USART TX的DMA二次配置,HAL_UART_Transmit_DMA()函数里虽然有对DMA_SxCR的EN位清零,但由于此时该STREAM处于传输未完成状态,所以无法实现最终清零。
既然无法对EN位清零,相关配置就无法写入生效。加上该函数没有对EN位是否为0的未做进一步确认判断,自然而然地下行到第【4】句重新开启USART TX的DMA传输。最后的结果当然还是按照老配置运行了。【有心的话,可以去STM32F4参考手册看看数据流 x 配置寄存器 DMA_SxCR,里面多个参数只有在EN位为0时方可进行写操作】 好了,就此打住。MCU开发中不少问题往往可能就卡在技术手册看得不到位。在开发过程中遇到难解问题时,有时细心看看手册的相关部分,或许会有踏破铁鞋无觅处,得来全不费工夫的感觉。 |