creep 发表于 2016-2-21 22:08:21

【说出你的故事】在外设与存储器之间

    偶然看到自己刚来社区时发的一个关于DMA的跟帖 说的是使用DMA写SD。在社区STM32的交流QQ群里也经常看到很多小伙伴在讨论DMA,那今天就说说我常用的几个例子。
参考手册上是这样介绍DMA的:用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。从上面我们可以看到DMA就好比是个“包邮的快递”一样,可以不收钱(不占用CPU)帮我们送快递(传递数据),有这等便宜岂能放过,我们必须要“用用用”!
1、DMA写SD卡我最开始使用DMA是写SD卡,因为STM32的SPI在连续传递数据时字节和字节之间有很大的时间间隔,这导致了写一个扇区需要很长的时间,经过很多测试发现字节之间的间隔没法很好的被优化掉,所以就考虑用DMA写SD卡了。使用DMA写SD卡时只需设置好要写的数据,然后使能DMA传输完成中断,在中断中判断写数据是否成功。如果使用常规的SPI写SD没有问题,那么修改为SPI的DMA模式写SD就卡就比较简单了。只需要修改下面几个函数即可。1) 在写512字节时使用DMA传输
2) 在中断函数中进行判断即可<font size="4" face="Tahoma">void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
                uint8_t t,s;
                //扇区写完后发送校验字节
                SPI2_ReadWriteByte(0xFF);
                SPI2_ReadWriteByte(0xFF);
                for(s = 0;s < 255;s++)
                {
                        //接收响应      
                        t = SPI2_ReadWriteByte(0xFF);
                        if((t&0x1F)==0x05)      
                        {
                              SD_DMA_Write_Status = Status_OK;
                              break;
                        }
                }
                if(s == 0xff)
                {
                        s = s;
                }
                SD_DisSelect();

}</font>2、DMA传输多通道ADC采集数据
这个测试使用NUCLEO-STM32F410RB,我们先初始化4个通道的ADC输出,然后用DMA将转换的数据传到数组中。4个通道配置如下:
ADC1初始化如下:然后在main函数中开始adc的转换:<font size="4" face="Tahoma">#include "main.h"

__IO uint16_t uhADCxConvertedValue;
int main(void)
{
      HAL_Init();
      SystemClock_Config();
      LED_Init();
      USART2_Init();
      ADC1_Init();
      ADC1_Config();
      HAL_ADC_Start_DMA(&AdcHandle, (uint32_t*)&uhADCxConvertedValue, 4);

      while(1)
      {
               
      }               
}
</font>首先悬空4个输入引脚,从转换结果可以看到ADC的值为随机值,这也说明了可以使用悬空ADC引脚的方法来产生随机数是可行的。
将其中的2个通道接地,可以看到ADC的值接近0



3、使用DMA和串口空闲中断接收不定长数据这个测试也使用NUCLEO-STM32F410RB。串口在接收数据时使用空闲中断和DMA接收不定长的数据,在发送时使用DMA发送。使用这个方法能够很方便的接收不定长的数据,但是也有一些限制,实际使用中如果出现问题要根据需要优化。
需要注意的是使用HAL库要自己写个回调函数如下:在main中我们将PC端发送的数据返回到PC端:<font size="4" face="Tahoma">#include "main.h"

int main(void)
{
      HAL_Init();
      SystemClock_Config();
      LED_Init();
      USART2_Init();
      while(1)
      {
                if(U1_Rxlen)
                {
                        HAL_UART_Transmit_DMA(&UARTHandle,aU3RxBuff,U1_Rxlen);
                        U1_Rxlen = 0;
                }
      }               
}
</font>
发送不同长度的数据,可以看到都能够正确接收:
      上面只是DMA的几个最简单的使用,因为比较常用而且简单,所以并没有详细的叙述,具体可以参考代码。测试代码是使用hal写的,明白了原理很容易转换为标准库。DMA在其他的外设也有很多可以使用的场合,合理使用DMA能起到事倍功半的效果。
    周末撸代码不易,希望人品能够给力赶上这个活动的末班车 :)@苏柚
测试代码:

creep 发表于 2016-11-29 14:08:37

honyuan 发表于 2016-11-29 10:52
此贴有BUG
没有标示出UART_IDLE_Callback(&UARTHandle);中断函数的调用位置
我移植到F405上,DMA发送数据结 ...

我在410/429/469上测试过这个代码,没发现特别明显的bug,不清楚是不是在405上有问题。

creep 发表于 2016-3-2 08:59:32

海绵宝宝233 发表于 2016-3-2 08:52
学习了。。。

:handshake                              

honyuan 发表于 2016-11-29 10:52:11

此贴有BUG
没有标示出UART_IDLE_Callback(&UARTHandle);中断函数的调用位置
我移植到F405上,DMA发送数据结束后,不能自动清除寄存器中的数据

lisingch 发表于 2016-2-21 22:24:38

这贴子不错,长知识。

creep 发表于 2016-2-21 23:09:37

lising 发表于 2016-2-21 22:24
这贴子不错,长知识。

感谢支持:)

zhwd102198 发表于 2016-2-23 14:16:08

:):):):):):):):):)

idodoyo 发表于 2016-3-1 17:41:47

这个可以,最开始为难了好久

creep 发表于 2016-3-1 18:41:37

idodoyo 发表于 2016-3-1 17:41
这个可以,最开始为难了好久

是的,刚开始接触的时候的确有点难搞。:)

海绵宝宝233 发表于 2016-3-2 08:52:09

学习了。。。

雪山飞狼 发表于 2016-3-2 21:39:38

谢谢分享,支持一个!

creep 发表于 2016-3-2 23:22:02

雪山飞狼 发表于 2016-3-2 21:39
谢谢分享,支持一个!

感谢支持!!!

xinnian-400985 发表于 2016-3-14 17:32:45

谢谢分享

wofei1314 发表于 2016-10-26 14:58:03

新手学习中.....

creep 发表于 2016-10-26 18:09:17

wofei1314 发表于 2016-10-26 14:58
新手学习中.....

客气了            

peter001 发表于 2016-10-27 12:49:01

这个帖子不错

ynwscfsfi 发表于 2016-10-31 13:52:24

谢谢分享,支持一个!
页: [1] 2
查看完整版本: 【说出你的故事】在外设与存储器之间