| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-03-28 | gingko | 初次建立 | \\ \\ ===== STM32CubeMX教程十四——DMA实验 ===== 1. 在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore3:icore3_cube_14_1.png?direct |}} 2. 出现芯片型号选择,搜索芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各种查找方式,可以选择芯片内核、型号等,可以帮助用户查找芯片。本实验选取的芯片型号为:STM32F407IGTx。 {{ :icore3:icore3_cube_14_2.png?direct |}} 3. 配置RCC,使用外部时钟源 {{ :icore3:icore3_cube_14_3.png?direct |}} 4. 配置调试引脚 {{ :icore3:icore3_cube_14_4.png?direct |}} 5. 将LED对应的3个引脚(PI5,PI6,PI7)设置为GPIO_Output {{ :icore3:icore3_cube_14_5.png?direct |}} 6. 引脚模式配置 {{ :icore3:icore3_cube_14_6.png?direct |}} 7. 配置DMA {{ :icore3:icore3_cube_14_7.png?direct |}} 8. 时钟源设置,选择外部高速时钟源,配置为最大主频 {{ :icore3:icore3_cube_14_8.png?direct |}} 9. 工程文件的设置, 这里就是工程的各种配置,我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK5 {{ :icore3:icore3_cube_14_9.png?direct |}} 10. 点击Code Generator,进行进一步配置 {{ :icore3:icore3_cube_14_10.png?direct |}} * **Copy all used libraries into the project folder** * 将HAL库的所有.C和.H都复制到所建工程中 * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便 * 缺点:体积大,编译时间很长 * **Copy only the necessary library files** * 只复制所需要的.C和.H(推荐) * 优点:体积相对小,编译时间短,并且工程可复制拷贝 * 缺点:新增外设时需要重新用STM32CubeMX导入 * **Add necessary library files as reference in the toolchain project configuration file** * 不复制文件,直接从软件包存放位置导入.C和.H * 优点:体积小,比较节约硬盘空间 * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径 * 自行选择方式即可 11. 然后点击GENERATE CODE 创建工程 {{ :icore3:icore3_cube_14_11.png?direct |}} 创建成功,打开工程。 \\ \\ \\ \\ ===== 实验十四:DMA实验——储存器到存储器的传输 ===== ==== 一、 实验目的与意义 ==== - 了解STM32 DMA结构。 - 了解STM32 DMA特征。 - 掌握DMA的使用方法。 - 掌握STM32 HAL库中DMA属性的配置方法。 - 掌握KEIL MDK集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore3 双核心板。[[https://item.taobao.com/item.htm?id=524229438677|点击购买]] - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] - Micro USB线缆。 - Keil MDK 开发平台。 - STM32CubeMX开发平台。 - 装有WIN XP(及更高版本)系统的计算机。 ==== 三、 实验原理 ==== * **DMA简介** * DMA,全称为"Direct Memory Access",即直接存储器访问,是一种不经过CPU而直接从内存存取数据的数据交换模式。在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给CPU,这样在很大程度上减轻了CPU资源占有率,可以极大地节省系统资源。DMA模式又可以分为Single-Word DMA(单字节DMA)和Multi-Word DMA(多字节DMA)两种。 * **DMA工作原理** * DMA 允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把他们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。 * DMA 传输主要地将一个内存区从一个装置复制到另外一个。当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。所以,DMA传输对于高效能嵌入式系统算法和网络是很重要的。 * **DMA类别** * 按键主要有两种类型:单字节DMA、多字节DMA。 * 单字节DMA:一次传送一个字节,效率较低,但它会保证在两次DMA传送之间,CPU有机会获得总线控制权,执行一次CPU总线周期。 * 多字节DMA:一次请求传送一个数据块,效率高,但在整个DMA传送期间,CPU长时间无法控制总线(无法响应其他DMA请求,无法处理其他中断等) * 本实验采用DMA2控制器的数据流0,选用通道0进行数据传输。通过LED的颜色来判断传输是否成功。 ==== 四、 实验程序 ==== === 1. 主函数 === * 初始化之后,使用HAL_DMA_Start函数开启DMA;通过HAL_DMA_GET_FLAG函数判断DMA传输是否完成,待DMA传输完成后,判断DMA传输目标数据与DMA传输源数据是否一致,即dst_buffer数组中的内容与src_buffer数组中的内容是否相等。若不相等,则测试失败,红色LED闪烁;若相等,则测试成功,蓝色LED闪烁。 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); //GPIO初始化 MX_DMA_Init(); //DMA初始化 dma2.initialize(); HAL_DMA_Start(&hdma_memtomem_dma2_stream0,(unsigned long int)src_buffer,(unsigned long int)dst_buffer,(unsigned long int)BUFFER_SIZE); //等待DMA传输完成 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; } } } while (1) //测试成功 { HAL_Delay(500); LED_BLUE_ON; HAL_Delay(500); LED_BLUE_OFF; } } * DMA外设基地址:在此定义src_buffer数组作为DMA传输数据源,const 关键字将src_buffer数组变量定义为常量类型,表示数据存储在内部FLASH中。 * DMA存储器地址:在此定义dst_buffer数组作为DMA传输目标存储器,存储在内部SDAM中。 //DMA外设基地址 //定义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存储器地址 //定义dst_buffer数组作为DMA传输目标存储器,存储在内部SDAM中 unsigned long int dst_buffer[BUFFER_SIZE] = {0}; === 2. DMA相关结构体定义 === DMA_HandleTypeDef hdma_memtomem_dma2_stream0; * DMA的名称定义,这个结构体中存放了DMA所有用到的功能,后面的别名hdma_memtomem_dma2_stream0就是我们所用的DMA的别名,在此采用DMA2控制器的数据流0,选用通道0进行数据传输。 typedef struct __DMA_HandleTypeDef { DMA_Stream_TypeDef *Instance; //DMA寄存器基地址 DMA_InitTypeDef Init; //DMA通信参数 HAL_LockTypeDef Lock; //DMA锁定参数 __IO HAL_DMA_StateTypeDef State; void *Parent; void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma); void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma); void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma); void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma); __IO uint32_t ErrorCode; uint32_t StreamBaseAddress; uint32_t StreamIndex; }DMA_HandleTypeDef; * 上述DMA_HandleTypeDef包含了指向寄存器的指针、互斥锁、一个描述状态的变量、一个保存错误代码的变量、指向DMA结构体的指针等。所有对DMA进行操作的函数都使用这个结构体的指针作为参数。 typedef struct { uint32_t DMA_Channel; //选择通道 uint32_t DMA_PeripheralBaseAddr; //DMA外设基地址 uint32_t DMA_Memory0BaseAddr; //DMA存储器地址 uint32_t DMA_DIR; //DMA传输方向 uint32_t DMA_BufferSize; //数据传输量 uint32_t DMA_PeripheralInc; //外设增量模式选择 uint32_t DMA_MemoryInc; //存储器增量模式 uint32_t DMA_PeripheralDataSize; //设置外设数据宽度 uint32_t DMA_MemoryDataSize; //设置存储器数据宽度 uint32_t DMA_Mode; //运行模式选择 uint32_t DMA_Priority; //优先级选择 uint32_t DMA_FIFOMode; //FIFO模式选择 uint32_t DMA_FIFOThreshold; //FIFO阀值 uint32_t DMA_MemoryBurst; //存储器突发传输 uint32_t DMA_PeripheralBurst; //外设突发传输 }DMA_InitTypeDef; * DMA_Channel: DMA 请求通道选择,可选通道 0 至通道 7,每个外设对应固定的通道。 * DMA_PeripheralBaseAddr:外设地址,一般设置为外设的数据寄存器地址,如果是存储器到存储器模式,则设置为其中一个存储区地址。 * DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设以及存储器到存储器。 * DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能。 * DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能。 * DMA_PeripheralDataSize:外设数据宽度,可选字节(8 位)、半字(16 位)和字(32位)。 * DMA_MemoryDataSize:存储器数据宽度,可选字节(8 位)、半字(16 位)和字(32位)。 * DMA_Priority:软件设置数据流的优先级,有 4 个可选优先级分别为非常高、高、中和低。 * DMA_MemoryBurst:存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。 * DMA_PeripheralBurst:外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。 === 3. DMA相关函数 === * HAL_DMA_Start();开启DMA传输。 * _HAL_DMA_GET_FLAG();获取DMA传输标志位 * _HAL_DMA_CLEAR_FLAG();清除DMA传输完成标志 * _HAL_DMA_GET_COUNTER();得到当前还剩余多少数据 * DMA开始传输 HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength) * 参数: * DMA_HandleTypeDef *hdma为DMA的别名,在本实验中即指DMA2的通道0 * uint32_t SrcAddress DMA外设基地址 * uint32_t DstAddress DMA存储器地址 * uint32_t DataLength 发送的数据长度 * DMA获取传输标志位 __HAL_DMA_GET_FLAG(&hdma_memtomem_dma2_stream0,DMA_FLAG_TCIF0_4) * 参数: * &hdma_memtomem_dma2_stream0在本实验中为 DMA2的通道0 * DMA_FLAG_TCIF0_4 对应的寄存器地址 ==== 五、 实验步骤 ==== - 把仿真器与iCore3的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore3通过Micro USB线与计算机相连,为iCore3供电; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore3上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== 实验成功,iCore3 双核心板蓝色LED灯点亮;实验失败,红色LED灯闪烁。