用户工具

站点工具


icore3l_15

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
icore3l_15 [2020/11/11 10:03]
zgf
icore3l_15 [2020/11/11 10:26] (当前版本)
zgf
行 49: 行 49:
 \\ \\
 \\ \\
 +===== 实验十五:DMA实验——储存器到存储器的传输 =====
 +==== 一、实验目的与意义 ====
 +  - 了解STM32 DMA结构
 +  - 了解STM32 DMA 特征
 +  - 掌握DMA的使用方法
 +  - 掌握STM32 HAL库中MDA属性的配置方法
 +  - 掌握KEILMDK 集成开发环境使用方法
 +==== 二、实验设备及平台 ====
 +  - iCore3L 双核心板
 +  - JLINK(或相同功能)仿真器
 +  - Micro USB线缆
 +  - Keil MDK 开发平台
 +  - STM32CubeMX开发平台
 +  - 装有WIN XP(及更高版本)系统的计算机
 +==== 三、实验原理 ====
 +=== 1、DMA简介 ===
 +  * 直接存储器访问(DMA),全称Direct Memory Access,用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速移动数据。这样节省的CPU资源可供其它操作使用。
 +  * DMA控制器基于复杂的总线矩阵架构,将功能强大的双AHB主总线架构与独立的FIFO结合在一起,优化了系统带宽。两个DMA控制器总共有16个数据流(每个控制器 8 个),每一个DMA控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或称请求)。每个通道都有一个仲裁器,用于处理DMA请求间的优先级。
 +=== 2、DMA工作原理 ===
 +  * DMA 允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把他们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。
 +  * DMA 传输主要地将一个内存区从一个装置复制到另外一个。当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。所以,DMA传输对于高效能嵌入式系统算法和网络是很重要的。
 +=== 3、DMA主要特性 ===
 +  - 该器件具有两个通用双端口 DMA(DMA1和DMA2)
 +  - 每个通用端口都有 8 个流,它们能够管理存储器到存储器、外设到存储器、存储器到外设的传输
 +  - 支持独立的8、16、32位存储器和外设传输
 +  - 它们具有用于 APB/AHB 外设的专用FIFO,支持突发传输,其设计可提供最大外设带宽 (AHB/​APB)
 +  - 这两个DMA控制器支持循环缓冲区管理,当控制器到达缓冲区末尾时,无需专门代码
 +  - 这两个DMA控制器还有双缓冲特性,可自动使用和切换两个存储器缓冲,而不需要特殊代码
 +  - 每个数据流都与专用的硬件 DMA 请求相连,同时支持软件触发
 +  - 通过软件进行相关配置,并且数据源和数据目标之间传输的数据量不受限制
 +  - 支持的外设:定时器,ADC,SPI,I2C,USART,DAC,I2S,SDIO和DCI
 +  - 可编程的数据传输数目范围为1-65535
 +  - 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),DMA通道的优先级请求由软件配置和硬件通道号确定
 +  - 转让源和目标的大小是独立且可配置的
 +=== 4、DMA工作流程图 ===
 +{{ :​icore3l:​icore3l_arm_hal_15_1.png?​direct |}}
 +=== 5、DMA传输 ===
 +  * DMA传输由给定数目的数据传输序列组成。要传输的数据项的数目及其宽度(8位、16位或32位)可用软件编程。
 +每个DMA传输包含三项操作:
 +  - 通过DMA_SxPAR或DMA_SxM0AR寄存器寻址,从外设数据寄存器或存储器单元中加载数据
 +  - 通过DMA_SxPAR或DMA_SxM0AR寄存器寻址,将加载的数据存储到外设数据寄存器或存储器单元
 +  - DMA_SxNDTR计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数。在产生事件后,外设会向DMA控制器发送请求信号。DMA控制器根据通道优先级处理该请求。只要DMA控制器访问外设,DMA控制器就会向外设发送确认信号。外设获得DMA控制器的确认信号后,便会立即释放其请求。一旦外设使请求失效,DMA控制器就会释放确认信号。如果有更多请求,外设可以启动下一个事务。
 +==== 四、实验程序 ====
 +=== 1.主函数 ===
 +<code c>
 +//​定义src_buffer数组作为DMA传输数据源 ​
 +//​const关键字将src_buffer数组变量定义为常量类型 ​
 +//​表示数据存储在内部的FLASH中
 +const unsigned long int src_buffer[BUFFER_SIZE] = {
 +  0x01020304,​0x05060708,​0x090A0B0C,​0x0D0E0F10,​
 +  0x11121314,​0x15161718,​0x191A1B1C,​0x1D1E1F20,​
 +  0x21222324,​0x25262728,​0x292A2B2C,​0x2D2E2F30,​
 +  0x31323334,​0x35363738,​0x393A3B3C,​0x3D3E3F40, ​  
 +  0x41424344,​0x45464748,​0x494A4B4C,​0x4D4E4F50,​
 +  0x51525354,​0x55565758,​0x595A5B5C,​0x5D5E5F60,​
 +  0x61626364,​0x65666768,​0x696A6B6C,​0x6D6E6F70,​
 +  0x71727374,​0x75767778,​0x797A7B7C,​0x7D7E7F80};​
 +//​定义DMA传输目标存储器 ​
 +//​存储在内部的SRAM中
 +unsigned long int dst_buffer[BUFFER_SIZE] = {0};
 +int main(void)
 +{
 +  int i;
 +  HAL_Init();
 +  SystemClock_Config();​
 +  MX_GPIO_Init();​
 +  MX_DMA_Init();​
 +  HAL_DMA_Start(&​hdma_memtomem_dma2_stream0,​(unsigned long int)src_buffer,​(unsigned long int)dst_buffer,​(unsigned ​
 +  long int)BUFFER_SIZE);​
 +  while(__HAL_DMA_GET_FLAG(&​hdma_memtomem_dma2_stream0,​DMA_FLAG_TCIF0_4) == SET);
 +  for(i = 0;i < BUFFER_SIZE;​i++)
 +  {
 +     ​if(dst_buffer[i] != src_buffer[i]) ​ //​测试失败
 +     {
 +         ​while(1)
 + {
 +           ​HAL_Delay(500);​
 +           ​LED_RED_ON;​
 +           ​HAL_Delay(500);​
 +           ​LED_RED_OFF;​
 +         }
 +     ​}  ​
 +  }
 +  LED_RED_ON; ​  //​测试成功
 +  While(1)
 +  {
 +  }
 +}
 +</​code>​
 +=== 2.DMA初始化函数 ===
 +<code c>
 +void MX_DMA_Init(void) ​
 +
 +  //​使能DMA控制器时钟 ​
 +  __HAL_RCC_DMA2_CLK_ENABLE();​ //​配置DMA工作方式 ​
 +  hdma_memtomem_dma2_stream0.Instance = DMA2_Stream0; ​
 +  hdma_memtomem_dma2_stream0.Init.Request = DMA_REQUEST_MEM2MEM;​
 +  hdma_memtomem_dma2_stream0.Init.Direction = DMA_MEMORY_TO_MEMORY;//​方向为存储器到存储器 ​
 +  hdma_memtomem_dma2_stream0.Init.PeriphInc = DMA_PINC_ENABLE;​
 +  hdma_memtomem_dma2_stream0.Init.MemInc = DMA_MINC_ENABLE;​
 +  hdma_memtomem_dma2_stream0.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; ​  //​外设数据宽度 ​
 +  hdma_memtomem_dma2_stream0.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; ​     //​存储器数据宽度
 +  hdma_memtomem_dma2_stream0.Init.Mode = DMA_NORMAL;
 +  hdma_memtomem_dma2_stream0.Init.Priority = DMA_PRIORITY_HIGH; ​    //​优先级别为高
 +  hdma_memtomem_dma2_stream0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;​
 +  hdma_memtomem_dma2_stream0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;​
 +  hdma_memtomem_dma2_stream0.Init.MemBurst = DMA_MBURST_SINGLE; ​    //​存储器突发单次传输 ​
 +  hdma_memtomem_dma2_stream0.Init.PeriphBurst = DMA_PBURST_SINGLE; ​ //​外设突发单次传输 ​
 +  if (HAL_DMA_Init(&​hdma_memtomem_dma2_stream0) != HAL_OK) ​
 +  { 
 +  Error_Handler(); ​
 +  } 
 +  //​DMA2中断配置 ​
 +  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn,​ 0, 0);
 +  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);​
 +}
 +</​code>​
 +=== 3.DMA寄存器结构体 ===
 +DMA相关的寄存器是通过HAL库中的结构体DMA_TypeDef 和DMA_Stream_TypeDef定义的
 +<code c>
 +typedef struct ​
 +{
 +  __IO uint32_t CR; /* DMA stream x 配置寄存器 */ 
 +  __IO uint32_t NDTR; /* DMA stream x 数据寄存器数 */
 +  __IO uint32_t PAR; /* DMA stream x 外设地址寄存器 */ 
 +  __IO uint32_t M0AR; /* DMA stream x 存储器0地址寄存器 */ 
 +  __IO uint32_t M1AR; /* DMA stream x 存储器1地址寄存器 */ 
 +  __IO uint32_t FCR; /* DMA stream x FIFO控制寄存器 */ 
 +
 +DMA_Stream_TypeDef;​   
 +typedef struct ​
 +{
 +  __IO uint32_t LISR; /* DMA低中断状态寄存器,地址偏移量:0x00 */ 
 +  __IO uint32_t HISR; /* DMA高中断状态寄存器,地址偏移量:0x04 */ 
 +  __IO uint32_t LIFCR; /* DMA低中断标志清除寄存器,地址偏移量:0x08 */ 
 +  __IO uint32_t HIFCR; /* DMA高中断标志清除寄存器,地址偏移量:0x0C */ 
 +}
 +DMA_TypeDef;​
 +</​code>​
 +=== 4.DMA句柄结构体DMA_HandleTypeDef ===
 +HAL库在DMA_TypeDef的基础上封装了一个结构体DMA_HandleTypeDef,定义如下:
 +<code c>
 +typedef struct __DMA_HandleTypeDef ​
 +
 +  void *Instance; /* 注册基地址 */ 
 +  DMA_InitTypeDef Init; /* DMA通讯参数 */ 
 +  HAL_LockTypeDef Lock; /* DMA锁定对象 */ 
 +  __IO HAL_DMA_StateTypeDef State; /* DMA传输状态 */ 
 +  void *Parent; /* 父对象状态 */ 
 +  void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma); ​
 +  /* DMA传输完成回调 */ 
 +  void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); ​
 +  /* DMA半传输完成回调 */ 
 +  void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma); ​
 +  /* DMA传输完成Memory1回调 */ 
 +  void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); ​
 +  /* DMA传输半完成Memory1回调 */ 
 +  void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma); ​
 +  /* DMA传输错误回调 */ 
 +  void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma); ​
 +  /* DMA传输中止回调 */ 
 +  __IO uint32_t ErrorCode; /* DMA错误代码 */ 
 +  uint32_t StreamBaseAddress;​ /* DMA流基址 */ 
 +  uint32_t StreamIndex;​ /* DMA流索引 */ 
 +  DMAMUX_Channel_TypeDef *DMAmuxChannel;​ /* DMAMUX通道基地址 */
 +  DMAMUX_ChannelStatus_TypeDef *DMAmuxChannelStatus; ​
 +  /* DMAMUX通道状态基地址 */ 
 +  uint32_t DMAmuxChannelStatusMask;​ /* DMAMUX通道状态掩码 */
 +  DMAMUX_RequestGen_TypeDef *DMAmuxRequestGen;​ /* DMAMUX请求生成器基地址 */
 +  DMAMUX_RequestGenStatus_TypeDef *DMAmuxRequestGenStatus;​
 +  /* DMAMUX请求生成器状态地址 */ 
 +  uint32_t DMAmuxRequestGenStatusMask;​ /* DMAMUX请求生成器状态掩码 */ 
 +}DMA_HandleTypeDef;​
 +</​code>​
 +==== 五、实验步骤 ====
 +  - 把仿真器与iCore3L的SWD调试口相连(直接相连或者通过转接器相连);
 +  - 把iCore3L通过Micro USB线与计算机相连,为iCore3L供电;
 +  - 打开Keil MDK 开发环境,并打开本实验工程;
 +  - 烧写程序到iCore3L上;
 +  - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。
 +==== 六、实验现象 ====
 +实验成功红色LED灯常亮,实验失败红色LED灯闪烁。
icore3l_15.1605060225.txt.gz · 最后更改: 2020/11/11 10:03 由 zgf