用户工具

站点工具


icore3_arm_hal_14
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-03-28 gingko 初次建立



STM32CubeMX教程十四——DMA实验

1. 在主界面选择File–>New Project 或者直接点击ACCEE TO MCU SELECTOR 2. 出现芯片型号选择,搜索芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各种查找方式,可以选择芯片内核、型号等,可以帮助用户查找芯片。本实验选取的芯片型号为:STM32F407IGTx。 3. 配置RCC,使用外部时钟源 4. 配置调试引脚 5. 将LED对应的3个引脚(PI5,PI6,PI7)设置为GPIO_Output 6. 引脚模式配置 7. 配置DMA 8. 时钟源设置,选择外部高速时钟源,配置为最大主频 9. 工程文件的设置, 这里就是工程的各种配置,我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK5 10. 点击Code Generator,进行进一步配置

  • 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 创建工程 创建成功,打开工程。



实验十四:DMA实验——储存器到存储器的传输

一、 实验目的与意义

  1. 了解STM32 DMA结构。
  2. 了解STM32 DMA特征。
  3. 掌握DMA的使用方法。
  4. 掌握STM32 HAL库中DMA属性的配置方法。
  5. 掌握KEIL MDK集成开发环境使用方法。

二、 实验设备及平台

  1. iCore3 双核心板。点击购买
  2. JLINK(或相同功能)仿真器。点击购买
  3. Micro USB线缆。
  4. Keil MDK 开发平台。
  5. STM32CubeMX开发平台。
  6. 装有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 对应的寄存器地址

五、 实验步骤

  1. 把仿真器与iCore3的SWD调试口相连(直接相连或者通过转接器相连);
  2. 把iCore3通过Micro USB线与计算机相连,为iCore3供电;
  3. 打开Keil MDK 开发环境,并打开本实验工程;
  4. 烧写程序到iCore3上;
  5. 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。

六、 实验现象

实验成功,iCore3 双核心板蓝色LED灯点亮;实验失败,红色LED灯闪烁。

icore3_arm_hal_14.txt · 最后更改: 2022/03/18 15:06 由 sean