(STM32条记)十二、DMA的根本知识与用法 第三部分

打印 上一主题 下一主题

主题 1000|帖子 1000|积分 3000

我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者。
之后的这个系列条记开头未标明的话,用的也是这个板子和教程。
  
  
三、DMA步伐验证

1、DMA 存储器到存储器模式实验

(1)DMA结构体解释

  1. typedef struct
  2. {
  3.         uint32_t DMA_PeripheralBaseAddr;         // 外设地址
  4.         uint32_t DMA_MemoryBaseAddr;                 // 存储器地址
  5.         uint32_t DMA_DIR;                                         // 传输方向
  6.         uint32_t DMA_BufferSize;                         // 传输数目
  7.         uint32_t DMA_PeripheralInc;                 // 外设地址增量模式
  8.         uint32_t DMA_MemoryInc;                         // 存储器地址增量模式
  9.         uint32_t DMA_PeripheralDataSize;         // 外设数据宽度
  10.         uint32_t DMA_MemoryDataSize;                 // 存储器数据宽度
  11.         uint32_t DMA_Mode;                                         // 模式选择
  12.         uint32_t DMA_Priority;                                 // 通道优先级
  13.         uint32_t DMA_M2M;                                         // 存储器到存储器模式
  14. } DMA_InitTypeDef;
复制代码

  • DMA_PeripheralBaseAddr:
    外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为此中一个存储器地址。
  • DMA_Memory0BaseAddr:
    存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自界说存储区的首地址。
  • DMA_DIR:
    传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR 寄存器的DIR[1:0] 位的值。这里并没有存储器到存储器的方向选择,当利用存储器到存储器时,只必要把此中一个存储器当作外设利用即可。

  • DMA_BufferSize:
    设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。
    这个DMA_BufferSize 就是要传输的次数。
    这个并不是指字节巨细,而是指DMA的传输次数,传输的字节巨细由DMA_PeripheralDataSize设置。
    DMA传输数据时,从DMA_MemoryBaseAddr+0地址开始放,直到DMA_MemoryBaseAddr+DMA_BufferSize-1,此时 DMA_CNDTR 变成0,传输结束。
  • DMA_PeripheralInc:
    如果设置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,以是一般不会使能该位。

  • DMA_MemoryInc:
    如果设置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自界说的存储区一般都是存放多个数据的,以是要使能存储器地址自动递增功能。

  • DMA_PeripheralDataSize:
    外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 PSIZE[1:0] 位的值。

  • DMA_MemoryDataSize:
    存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 MSIZE[1:0] 位的值。当外设和存储器之间传数据时,双方的数据宽度应该设置为一致巨细。

  • DMA_Mode:
    DMA 传输模式选择,可选一次传输Normal或者循环传输Circular,它设定 DMA_CCR 寄存器的CIRC 位的值。例程我们的 ADC 收罗是持续循环进行的,以是利用循环传输模式。

  • DMA_Priority:
    软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL[1:0] 位的值。DMA 通道优先级只有在多个 DMA 通道同时利用时才故意义,如果是单个通道,优先级可以任意设置。

  • DMA_M2M:
    存储器到存储器模式,利用存储器到存储器时用到,设定 DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。

(2)DMA结构体初始化

  1.                 DMA_InitTypeDef DMA_InitStructure;
  2.                 // 开启DMA时钟
  3.                 RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
  4.                 // 源数据地址
  5.             DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)ShuZu_YouShu;        //数组有32个数,每个数都是一个字,且使用const定义(存储在FLASH)
  6.                 // 目标地址
  7.                 DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)ShuZu_MeiShu;        //数组有32个数,每个数都是一个字
  8.                 // 方向:外设到存储器(这里的外设是内部的FLASH)       
  9.                 DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;
  10.                 // 传输大小       
  11.                 DMA_InitStructure.DMA_BufferSize                         = 32;                                                //因为数组有32个数,所以设32
  12.                 // 外设(内部的FLASH)地址递增            
  13.                 DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Enable;
  14.                 // 内存地址递增
  15.                 DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  16.                 // 外设数据单位       
  17.                 DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Word;        //因为每个数大小都是一个字,所以宽度设为Word
  18.                 // 内存数据单位
  19.                 DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Word;         //因为每个数大小都是一个字,所以宽度设为Word
  20.                 // DMA模式,一次或者循环模式
  21.                 DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal ;                        //一次传输模式
  22.                 //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  
  23.                 // 优先级:高       
  24.                 DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
  25.                 // 使能内存到内存的传输
  26.                 DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Enable;                        //因为本次是FLASH传输到SRAM,所以是存储器到存储器
  27.                 // 配置DMA通道                  
  28.                 DMA_Init(DMA_CHANNEL, &DMA_InitStructure);
  29.             //清除DMA数据流传输完成标志位
  30.             DMA_ClearFlag(DMA_FLAG_TC);
  31.             // 使能DMA
  32.                 DMA_Cmd(DMA_CHANNEL,ENABLE);
复制代码
此中,

  • 需开启DMA时钟才能利用DMA功能:
  1. // 开启DMA时钟
  2. RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
复制代码

  • ShuZu_YouShu界说如下:
  1. uint32_t ShuZu_YouShu[32]= {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
  2.                                                         0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
  3.                                                         0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
  4.                                                         0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
  5.                                                         0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
  6.                                                         0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
  7.                                                         0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
  8.                                                         0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
复制代码

  • 需清除 DMA 标记位后再利用DMA功能,防止不须要的干扰:
  1. //清除DMA数据流传输完成标志位
  2.     DMA_ClearFlag(DMA_FLAG_TC);
复制代码
(3)步伐验证

DMA传输过后,在DEBUG界面可以看到,ShuZu_YouShu的数字传输到了ShuZu_MeiShu中

2、DMA 存储器到外设模式实验

具体实举动 将数组中的数字 传输至 串口
(1)串口初始化

   代码解释均放入注释中,正文不再复述
  1. /**
  2.   * @brief  USART GPIO 配置,工作参数配置
  3.   * @param  无
  4.   * @retval 无
  5.   */
  6. void USART_Config(void)
  7. {
  8.         GPIO_InitTypeDef GPIO_InitStructure;
  9.         USART_InitTypeDef USART_InitStructure;
  10.         // 打开串口GPIO的时钟
  11.         DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
  12.        
  13.         // 打开串口外设的时钟
  14.         DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
  15.         // 将USART Tx的GPIO配置为推挽复用模式
  16.         GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
  17.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  18.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  19.         GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
  20.   // 将USART Rx的GPIO配置为浮空输入模式
  21.         GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
  22.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  23.         GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
  24.        
  25.         // 配置串口的工作参数
  26.         // 配置波特率 为 115200
  27.         USART_InitStructure.USART_BaudRate = 115200;
  28.         // 配置 数据字长 为 8
  29.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  30.         // 配置 停止位 为 1
  31.         USART_InitStructure.USART_StopBits = USART_StopBits_1;
  32.         // 配置 校验位 为 0
  33.         USART_InitStructure.USART_Parity = USART_Parity_No ;
  34.         // 配置硬件流控制
  35.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  36.         // 配置工作模式,收发一起
  37.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  38.         // 完成串口的初始化配置
  39.         USART_Init(DEBUG_USARTx, &USART_InitStructure);       
  40.        
  41.         // 使能串口
  42.         USART_Cmd(DEBUG_USARTx, ENABLE);            
  43. }
复制代码
(2)DMA结构体初始化

由于数据是从存储器到串口,以是设置存储器为源地址,串口的数据寄存器为目标地址。
要发送的数据有很多且都先存储在存储器中,则存储器地址指针递增;串口数据寄存器只有一个,则外设地址地址不变。
双方数据单元设置成一致,传输模式可选一次或者循环传输,只有一个 DMA 哀求,优先级任意设。
最后调用 DMA_Init 函数把这些参数写到 DMA 的寄存器中,然后使能 DMA开始传输。
  1. void USARTx_DMA_Config(void)
  2. {
  3.                 DMA_InitTypeDef DMA_InitStructure;
  4.        
  5.                 // 开启DMA时钟
  6.                 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  7.                 // 设置DMA源地址:串口数据寄存器地址*/
  8.                 DMA_InitStructure.DMA_PeripheralBaseAddr         = USART_DR_ADDRESS;
  9.                 // 内存地址(要传输的变量的指针)
  10.                 DMA_InitStructure.DMA_MemoryBaseAddr                 = (u32)ShuZu_YouShu;
  11.                 // 方向:从内存到外设       
  12.                 DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;
  13.                 // 传输数量
  14.                 DMA_InitStructure.DMA_BufferSize                         = 5000;                // 传输数量        设为 5000,这个数随便设,只要不超过数组大小就可
  15.                 // 外设地址不增            
  16.                 DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
  17.                 // 内存地址自增
  18.                 DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  19.                 // 外设数据单位       
  20.                 DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;        //因为每次传输1bit,所以设为Byte
  21.                 // 内存数据单位
  22.                 DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;                 //因为每次传输1bit,所以设为Byte
  23.                 // DMA模式,一次或者循环模式
  24.                 DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal ;                                //一次传输模式
  25.                 //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;       
  26.                 // 优先级:中       
  27.                 DMA_InitStructure.DMA_Priority                                 = DMA_Priority_Medium;
  28.                 // 禁止内存到内存的传输
  29.                 DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
  30.                 // 配置DMA通道                  
  31.                 DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);               
  32.                 // 使能DMA
  33.                 DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
  34. }
复制代码
此中,

  • 外设地址DMA_PeripheralBaseAddr为串口地址(需查阅开发手册),即:


  1. // 外设寄存器地址
  2. #define         PERIPH_BASE         ((uint32_t)0x40000000)
  3. #define         APB2PERIPH_BASE     (PERIPH_BASE + 0x10000)
  4. #define          USART1_BASE         (APB2PERIPH_BASE + 0x3800)
  5. #define          USART_DR_ADDRESS    (USART1_BASE+0x04)
复制代码

  • ShuZu_YouShu数组添补为 A-Z共26个字母,再加一个回车:
  1.   /*填充将要发送的数据*/
  2.   for(i=0; i < 5000; i++)
  3.   {
  4.     ShuZu_YouShu[i]         = 'A'+ i%27;
  5.     if(ShuZu_YouShu[i] == '[')
  6.         {
  7.                 ShuZu_YouShu[i] = '\n';
  8.         }
  9.   }
复制代码
(3)步伐验证

利用USART_DMACmd函数打开USART 的DMA哀求。
  1. /* USART1 向 DMA发出TX请求 */
  2.   USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
复制代码
它接收三个参数:


  • 第一个参数用于设置串口外设,可以是 USART1/2/3 和 UART4/5 这 5 个参数可选。
  • 第二个参数设置串口的具体 DMA 哀求,有串口发送哀求 USART_DMAReq_Tx 和接收哀求 USART_DMAReq_Rx 可选。
  • 第三个参数用于设置启动哀求 ENABLE 或者关闭哀求 DISABLE。
运行该函数后 USART 的DMA 发送传输就开始了,根据设置存储器的数据会发送到串口。
验证结果如下:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

不到断气不罢休

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表