【DMA】DMA数据转运

需要用到的DMA配置函数
DMA_Init();//DMA初始化
DMA_StructInit();//给DMA结构体赋默认初始值
DMA_Cmd();//使能/失能DMA
DMA_SetCurrDataCounter();//设置当前数据寄存器(给传输计数器写数据)
DMA_GetCurrDataCounter();//获取当前数据寄存器(返回传输计数器的值)

配置DMA时参考该图流程
任务:让DMA把DataA的数据转运到DataB里(存储器SRAM到存储器SRAM)
1、完成DMA驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//将【MyDMA】驱动文件放在【System】文件夹中(DMA不涉及外围硬件电路)
//【MyDMA】名称是为了防止与库函数里面的DMA重复
2、在MyDMA.c中初始化函数MyDMA_Init
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
//第一步:RCC开启DMA的时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//DMA是AHB总线的设备
    
    
//第二步:初始化DMA参数
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//外设【起始地址】32位,
    //SRAM数组地址由编译器分配,并不固定,因此不写绝对地址,而是通过数组名来获取地址
    //此处把这个地址提取为初始化函数的参数,在初始化时需要转运哪个数组,传数组地址就行
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设【数据宽度】
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设【地址是否自增】
    DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器【起始地址】
    //与外设【起始地址】设置同理,也设置一个参数
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器【数据宽度】
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器【地址是否自增】
    DMA_InitStructure.DMA_BufferSize = Size;//缓存区大小【传输计数器】(传输多少次)
    //设置一个参数用于直接配置【传输计数器】的寄存器(0~65535)
    DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//硬件触发or软件触发
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否使用自动重装器
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//按照参数要求给定优先级
    DMA_Init(DMA1_Channel1,&DMA_InitStructure);
    
    
//第三步:开关控制【DMACmd】
    DMA_Cmd(DMA1_Channel1,ENABLE);
    
//如果选择的是【硬件触发】,要在对应外设调用【PPP_DMACmd】开启触发信号的输出
}
3、在MyDMA.h中声明初始化函数MyDMA_Init
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
4、在主程序main.c中#include "MyDMA.h"
#include "MyDMA.h"
5、在主循环中编写程序主体
//定义DMA转运的【源端数组】和【目的数组】
uint8_t DataA[] = {0x01 , 0x02 , 0x03 , 0x04};//源端数组
uint8_t DataB[] = {0 , 0 , 0 , 0}; //目的数组
//数组名就是地址,因此传地址时不用加取地址符号“&”
int main(void)
{
    OLED_Init();
//显示转运前DataA和DataB的数据
    OLED_ShowHexNum(1,1,DataA[0],2);
    OLED_ShowHexNum(1,4,DataA[1],2);
    OLED_ShowHexNum(1,7,DataA[2],2);
    OLED_ShowHexNum(1,10,DataA[3],2);
    OLED_ShowHexNum(2,1,DataB[0],2);
    OLED_ShowHexNum(2,4,DataB[1],2);
    OLED_ShowHexNum(2,7,DataB[2],2);
    OLED_ShowHexNum(2,10,DataB[3],2);
    
//DMA立刻工作,把DataA数组的数据转运到DataB里面
    MyDMA_Init( (uint32_t)DataA , (uint32_t)DataB , 4);
    //参数1:源端地址(传入DataA数组的首地址)
    //参数2:目的地址(传入DataB数组的首地址)
    //参数3:传输次数(有4个数据,传输4次)
    
//显示转运后DataA和DataB的数据
    OLED_ShowHexNum(3,1,DataA[0],2);
    OLED_ShowHexNum(3,4,DataA[1],2);
    OLED_ShowHexNum(3,7,DataA[2],2);
    OLED_ShowHexNum(3,10,DataA[3],2);
    OLED_ShowHexNum(4,1,DataB[0],2);
    OLED_ShowHexNum(4,4,DataB[1],2);
    OLED_ShowHexNum(4,7,DataB[2],2);
    OLED_ShowHexNum(4,10,DataB[3],2);
                    
    while(1)
    {
        
    }
}
实现功能:上电后OLED进行初始化,先显示转运前DataA和DataB的数据,经过DMA转运一次后显示转运后DataA和DataB的数据,DataA的数据成功转运(复制)到了DataB,源端数据不发生变化

上面的程序是DMA初始化后立刻开始转运,并且转运一次后DMA停止
如果DataA的数据发生变化,需要再转运:给传输计数器重新赋值
(修改MyDMA_Init部分配置)
2、在MyDMA.c中初始化函数MyDMA_Init
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
    MyDMA_Size = Size;
    
//第一步:RCC开启DMA的时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//DMA是AHB总线的设备
    
    
//第二步:初始化DMA参数
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//外设【起始地址】32位,
    //SRAM数组地址由编译器分配,并不固定,因此不写绝对地址,而是通过数组名来获取地址
    //此处把这个地址提取为初始化函数的参数,在初始化时需要转运哪个数组,传数组地址就行
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设【数据宽度】
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设【地址是否自增】
    DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器【起始地址】
    //与外设【起始地址】设置同理,也设置一个参数
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器【数据宽度】
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器【地址是否自增】
    DMA_InitStructure.DMA_BufferSize = Size;//缓存区大小【传输计数器】(传输多少次)
    //设置一个参数用于直接配置【传输计数器】的寄存器(0~65535)
    DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//硬件触发or软件触发
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否使用自动重装器
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//按照参数要求给定优先级
    DMA_Init(DMA1_Channel1,&DMA_InitStructure);
    
    
//第三步:开关控制【DMACmd】
    DMA_Cmd(DMA1_Channel1,DISABLE);
    //不让DMA初始化后立刻进行转运
    //等调用MyDMA_Transfer函数后再进行转运
    
//如果选择的是【硬件触发】,要在对应外设调用【PPP_DMACmd】开启触发信号的输出
}
编写连续转运函数MyDMA_Transfer
//调用一次这个函数,就再启动一次DMA转运
void MyDMA_Transfer(void)
{
    //先给DMA失能
    DMA_Cmd(DMA1_Channel1,DISABLE);
    
    //给传输计数器重新赋值
    DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
    //获取初始化的Size参数(定义全局变量以跨函数传递)
    
    //使能DMA
    DMA_Cmd(DMA1_Channel1,ENABLE);
    
    //等待转运完成
    while( DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET );
    //转运完成后标志位置1(没有完成一直while循环,转运完成跳出循环)
    //手动清除标志位
    DMA_ClearFlag(DMA1_FLAG_TC1);
}
3、在MyDMA.h中声明初始化函数MyDMA_Init和连续转运函数MyDMA_Transfer
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
void MyDMA_Transfer(void);
4、在主程序main.c中#include "MyDMA.h"
#include "MyDMA.h"
5、在主循环中编写程序主体
//定义DMA转运的【源端数组】和【目的数组】
uint8_t DataA[] = {0x01 , 0x02 , 0x03 , 0x04};//源端数组
uint8_t DataB[] = {0 , 0 , 0 , 0}; //目的数组
//数组名就是地址,因此传地址时不用加取地址符号“&”
int main(void)
{
    OLED_Init();
    MyDMA_Init( (uint32_t)DataA , (uint32_t)DataB , 4);
    //参数1:源端地址(传入DataA数组的首地址)
    //参数2:目的地址(传入DataB数组的首地址)
    //参数3:传输次数(有4个数据,传输4次)    
    
    //显示字符
    OLED_ShowString(1,1,"DataA");
    OLED_ShowString(3,1,"DataB");
    //显示地址
    OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
    OLED_ShowHexNum(3,8,(uint32_t)DataB,8);
    
                      
    while(1)
    {
        //变换DataA的源端测试数据
        DataA[0] ++;
        DataA[1] ++;
        DataA[2] ++;
        DataA[3] ++;
        
        //转运前的DataA数据和DataB数据
        OLED_ShowHexNum(2,1,DataA[0],2);
        OLED_ShowHexNum(2,4,DataA[1],2);
        OLED_ShowHexNum(2,7,DataA[2],2);
        OLED_ShowHexNum(2,10,DataA[3],2);
        OLED_ShowHexNum(4,1,DataB[0],2);
        OLED_ShowHexNum(4,4,DataB[1],2);
        OLED_ShowHexNum(4,7,DataB[2],2);
        OLED_ShowHexNum(4,10,DataB[3],2);
        
        Delay_ms(1000);
        
        //开始转运
        MyDMA_Transfer();
        
        //转运后的DataA数据和DataB数据
        OLED_ShowHexNum(2,1,DataA[0],2);
        OLED_ShowHexNum(2,4,DataA[1],2);
        OLED_ShowHexNum(2,7,DataA[2],2);
        OLED_ShowHexNum(2,10,DataA[3],2);
        OLED_ShowHexNum(4,1,DataB[0],2);
        OLED_ShowHexNum(4,4,DataB[1],2);
        OLED_ShowHexNum(4,7,DataB[2],2);
        OLED_ShowHexNum(4,10,DataB[3],2);
        
        Delay_ms(1000);
    }
}
实现功能:上电后OLED进行初始化并显示Data字符和地址,DMA初始化函数确定传输配置,DataA数据不断加1,并每隔一秒传入DataB中通过OLED显示

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的DMA数据转移代码示例,使用STM32CubeMX和HAL库: ```c /* 配置DMA */ /* 定义DMA句柄 */ DMA_HandleTypeDef hdma_memtomem_dma1_stream0; /* DMA内存到内存模式配置 */ hdma_memtomem_dma1_stream0.Instance = DMA1_Stream0; hdma_memtomem_dma1_stream0.Init.Channel = DMA_CHANNEL_0; hdma_memtomem_dma1_stream0.Init.Direction = DMA_MEMORY_TO_MEMORY; hdma_memtomem_dma1_stream0.Init.PeriphInc = DMA_PINC_ENABLE; hdma_memtomem_dma1_stream0.Init.MemInc = DMA_MINC_ENABLE; hdma_memtomem_dma1_stream0.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_memtomem_dma1_stream0.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_memtomem_dma1_stream0.Init.Mode = DMA_NORMAL; hdma_memtomem_dma1_stream0.Init.Priority = DMA_PRIORITY_LOW; hdma_memtomem_dma1_stream0.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_memtomem_dma1_stream0); /* 配置源和目标地址 */ uint32_t SrcAddress = (uint32_t)/* 源地址 */; uint32_t DstAddress = (uint32_t)/* 目标地址 */; /* 配置数据长度 */ uint32_t DataLength = /* 数据长度 */; /* 启动DMA传输 */ HAL_DMA_Start(&hdma_memtomem_dma1_stream0, SrcAddress, DstAddress, DataLength); /* 等待DMA传输完成 */ while (HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_stream0, HAL_DMA_FULL_TRANSFER, 100) != HAL_OK); ``` 在这个示例中,我们使用了`hdma_memtomem_dma1_stream0` DMA句柄和`DMA1_Stream0`数据流来执行内存到内存的数据传输。在初始化DMA句柄时,我们配置了DMA通道、数据传输方向、地址自增选项、数据对齐、传输模式、优先级和FIFO模式。然后,我们将源地址、目标地址和数据长度传递给DMA,并使用`HAL_DMA_Start`函数启动数据传输。最后,我们使用`HAL_DMA_PollForTransfer`函数等待数据传输完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值