[RISC-V MCU 应用开发] 十三、CH32V103应用教程——DMA(存储器到存储器)

[复制链接]
 楼主| RISCVLAR 发表于 2020-11-18 19:20 | 显示全部楼层 |阅读模式
本帖最后由 RISCVLAR 于 2020-11-18 19:19 编辑

CH32V103应用教程——DMA(存储器到存储器)

本章教程讲解DMA存储器到存储器模式。存储器到存储器模式可以实现数据在两个内存的快速拷贝。程序中,首先定义一个静态的源数据,存放在内部 FLASH,然后使用DMA传输把源数据拷贝到目标地址上(内部SRAM),最后对比源数据和目标地址的数据,判断传输是否准确。

1、DMA简介及相关函数介绍
直接存储器访问控制器(DMA)提供在外设和存储器之间或者存储器和存储器之间的高速数据传输方式,无须CPU干预,数据可以通过DMA快速地移动,以节省CPU的资源来做其他操作。

DMA控制器有7个通道,每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各通道之间的优先级。

关于DMA具体信息,可参考CH32V103应用手册。DMA标准库函数具体内容如下:
  1. void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
  2. void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
  3. void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
  4. void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
  5. void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
  6. void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
  7. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
  8. FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
  9. void DMA_ClearFlag(uint32_t DMAy_FLAG);
  10. ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
  11. void DMA_ClearITPendingBit(uint32_t DMAy_IT);
1.1、void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx)
功  能:将DMAy Channelx寄存器初始化为其默认重置值。
输  入:DMAy_Channelx:这里y可以是1或2来选择DMA,x可以是1到7对于DMA1,1到5对于DMA2选择DMA频道。

1.2、void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
功  能:根据DMA_InitStruct中指定的参数初始化DMAy Channelx。
输  入:DMAy_Channelx:这里y可以是1或2来选择DMA,x可以是1到7对于DMA1,1到5对于DMA2选择DMA通道。DMA_InitStruct:指向包含指定DMA频道的配置信息的DMA_InitTypeDef结构的指针。

1.3、void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct)
功  能:用默认值填充每个DMA_InitStruct成员。
输  入:DMA_InitStruct:指向要初始化的DMA_InitTypeDef结构的指针,对于DMA1,它是1到7,对于DMA2是1到5。

1.4、void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState)
功  能:启用或禁用指定的DMAy Channelx。
输  入:DMAy_Channelx:其中y可以是1或2来选择DMA,x可以是1到7(对于DMA1)和1到5(对于DMA2)来选择DMA通道。NewState:DMAy Channelx的新状态(启用或禁用)。

1.5、void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState)
功  能:启用或禁用指定的DMAy Channelx中断。
输  入:DMAy_Channelx:其中y可以是1或2来选择DMA,x可以是1到7(对于DMA1)和1到5(对于DMA2)来选择DMA通道。NewState:DMAy Channelx的新状态(启用或禁用)。

1.6、void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)
功  能:设置当前DMAy Channelx传输中的数据单元数。
输  入:DMAy_Channelx:其中y可以是1或2来选择DMA,x可以是1到7(对于DMA1)和1到5(对于DMA2)来选择DMA通道。DataNumber:当前DMAy Channelx传输中的数据单元数。

1.7、uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
功  能:返回当前DMAy Channelx传输中剩余的数据单元数。
输  入:DMAy_Channelx:其中y可以是1或2来选择DMA,x可以是1到7(对于DMA1)和1到5(对于DMA2)来选择DMA通道。

1.8、FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
功  能:检查是否设置了指定的DMAy Channelx标志。
输  入:DMAy_FLAG:指定要检查的标志。

1.9、void DMA_ClearFlag(uint32_t DMAy_FLAG)
功  能:清除DMAy Channelx的挂起标志。
输  入:DMAy_FLAG:指定要检查的标志。

1.10、ITStatus DMA_GetITStatus(uint32_t DMAy_IT)
功  能:检查指定的DMAy Channelx中断是否发生。
输  入:DMAy_IT:指定要检查的DMAy中断源。

1.11、void DMA_ClearITPendingBit(uint32_t DMAy_IT)
功  能:清除DMAy Channelx的中断挂起位。
输  入:DMAy_IT:指定要清除的DMAy中断源。

以上函数在程序编写时直接调用即可。

2、硬件设计
本章教程所用资源均为CH32V103开发板内部资源,无需进行其他硬件连接,只需进行程序配置即可。

3、软件设计
DMA存储器到存储器模式程序如下:
dma.h文件
  1. #ifndef __DMA_H
  2. #define __DMA_H

  3. #include "ch32v10x_conf.h"

  4. /* Global define */
  5. #define Buf_Size 32       //定义要发送的数据大小

  6. /* Global Variable */
  7. u32 SRC_BUF[Buf_Size];    //定义SRC_BUF数组作为DMA传输数据源
  8. u32 DST_BUF[Buf_Size];    //定义DMA传输目标存储器,存储在内部SRAM中

  9. void DMA1_CH3_Init(void); //DMA传输参数初始化配置
  10. u8 BufCmp( u32* buf1,u32* buf2,u16 buflength); //源数据与目标地址数据对比

  11. #endif
dma.h文件主要包含相关定义及函数声明。

dma.c文件
  1. #include "dma.h"

  2. //定义SRC_BUF数组作为DMA传输数据源,并将数据存储在内部FLASH中
  3. u32 SRC_BUF[Buf_Size]=
  4.                       {      0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
  5.                              0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
  6.                              0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
  7.                              0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
  8.                              0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
  9.                              0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
  10.                              0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
  11.                              0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80
  12.                       };
  13. //定义DST_BUF数组作为DMA传输目标存储器,并将数据存储在内部SRAM中
  14. u32 DST_BUF[Buf_Size]={0};

  15. /*******************************************************************************
  16. * Function Name  : DMA1_CH3_Init
  17. * Description    : Initializes Channel3 of DMA1 collection.
  18. * Input          : None
  19. * Return         : None
  20. *******************************************************************************/
  21. //DMA1通道3传输参数配置
  22. void DMA1_CH3_Init(void)
  23. {
  24.   DMA_InitTypeDef DMA_InitStructure;
  25.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);            //使能DMA1时钟

  26.   DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(SRC_BUF);    //设置源数据地址
  27.   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DST_BUF;          //设置目标地址
  28.   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;            //设置传输方向:外设到存储器(此处外设为内部FLASH)
  29.   DMA_InitStructure.DMA_BufferSize = Buf_Size*4;                //设置传输大小
  30.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;         //指定外设地址寄存器递增。
  31.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //指定内存地址寄存器递增。
  32.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设数据单位
  33.   DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;     //设置存储器数据单位
  34.   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                 //设置对应DMA工作模式为正常模式
  35.   DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;       //DMA1通道3优先级高
  36.   DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;                   //使能DMA存储器到存储器的传输方式
  37.   DMA_Init(DMA1_Channel3, &DMA_InitStructure);                  //根据DMA_InitStruct中指定的参数初始化DMA1通道3
  38.   DMA_ClearFlag(DMA1_FLAG_TC3);   //清除DMA1通道3传输完成挂起标志

  39.   DMA_Cmd(DMA1_Channel3, ENABLE); //使能DMA1通道3
  40. }

  41. /*******************************************************************************
  42. * Function Name  : BufCmp
  43. * Description    : Compare the  buf
  44. * Input          : buf1:pointer of buf1
  45. *                  buf2:pointer of buf1
  46. *                  buflength: length of buf
  47. * Return         : 1:Two arrays are identical
  48. *                  0:Two arrays are inconsistent
  49. *******************************************************************************/
  50. //源数据与目标地址数据对比
  51. u8 BufCmp( u32* buf1,u32* buf2,u16 buflength)
  52. {
  53.   while(buflength--)
  54.     {
  55.       if(*buf1 != *buf2)  //判断两个数据源是否对应相等
  56.         {
  57.           return 0;       //对应数据源不相等马上退出函数,并返回 0
  58.         }
  59.         /* 递增两个数据源的地址指针 */
  60.         buf1++;
  61.         buf2++;
  62.     }
  63.     return 1;             //完成判断并且对应数据相对 ,返回1
  64. }
dma.c文件主要包括DMA1通道3传输参数初始化配置以及数据比较两个函数,DMA1_CH3_Init函数主要进行DMA1时钟使能、DMA1通道3参数配置、DMA1通道3传输完成挂起标志清除以及DMA1通道3使能。BufCmp函数主要进行数据对比,并根据对比结果返回相应值。


main.c函数
  1. /*******************************************************************************
  2. * Function Name  : main
  3. * Description    : Main program.
  4. * Input          : None
  5. * Return         : None
  6. *******************************************************************************/
  7. int main(void)
  8. {
  9.     u8 i=0;
  10.     u8 Flag=0;

  11.     Delay_Init();
  12.     USART_Printf_Init(115200);
  13.     DMA1_CH3_Init();

  14.     printf("SystemClk:%d\r\n",SystemCoreClock);
  15.     printf("DMA MEM2MEM TEST\r\n");

  16.     while(DMA_GetFlagStatus(DMA1_FLAG_TC3)==RESET) //等待DMA1通道3传输完成
  17.     {
  18.     }
  19.     Flag=BufCmp(SRC_BUF,DST_BUF,Buf_Size);         //源数据与传输后数据比较结果
  20.     if(Flag==0)                                    //判断源数据与传输后数据比较结果
  21.     {
  22.       printf("DMA Transfer Fail\r\n");
  23.     }
  24.     else
  25.     {
  26.       printf("DMA Transfer Success\r\n");
  27.     }

  28.     printf("SRC_BUF:\r\n");
  29.     for(i=0;i<Buf_Size;i++)
  30.     {
  31.         printf("0x%08x\r\n",SRC_BUF[i]);
  32.     }

  33.     printf("DST_BUF:\r\n");
  34.     for(i=0;i<Buf_Size;i++)
  35.     {
  36.         printf("0x%08x\r\n",DST_BUF[i]);
  37.     }
  38.     while(1)
  39.     {
  40.     }
  41. }
main.c文件主要进行相关函数初始化以及DMA传输状态及结果显示。


4、下载验证
将编译好的程序下载到开发板并复位,串口打印情况具体如下:
图片1.png


12、DMA.rar

468.91 KB, 下载次数: 78

LYHYYDS 发表于 2021-10-13 10:01 | 显示全部楼层
赞一个
dianjvnan 发表于 2024-3-6 00:27 | 显示全部楼层
您好。。。我试着将例程的传输数据单位由DMA_PeripheralDataSize_Byte改为DMA_PeripheralDataSize_Word,,传输大小由Buf_Size*4改为Buf_Size。。。理论正好以 字 为单位传输 ,,,可结果数据是不对的。。。看了数据手册也没看出啥原因

评论

注意存储器和外设的数据单位都要改成Word  发表于 2024-3-14 18:56
AdaMaYun 发表于 2024-3-12 20:00 | 显示全部楼层
感觉现在编程都是使用标准库文件进行的,一目了然
亚瑟 发表于 2024-3-14 17:02 来自手机 | 显示全部楼层
dma优先级是不是比CPU的还要高啊
 楼主| RISCVLAR 发表于 2024-3-14 18:54 | 显示全部楼层
亚瑟 发表于 2024-3-14 17:02
dma优先级是不是比CPU的还要高啊

可以这么理解,访问主存时DMA的优先级是高于CPU的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部