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