当前位置:   article > 正文

STM32实验DMA数据搬运小助手

STM32实验DMA数据搬运小助手

本次实验做的是将一个数组的内容利用DMA数据搬运小助手搬运到另外一个数组中去。

最后的实验结果:

可以看到第四行的数据就都不是0了,成功搬运了过来。

DMA实现搬运的步骤其实不是很复杂,复杂的是结构体参数:

整个步骤为:

第一步:RCC开启DMA的时钟

第二步:调用DMA_Init,初始化各个参数 (包括外设和存储器的起始地址,数据宽度,地址是否自增,方向,传输计数器,是否需要自动重装,选择触发源,通道优先级)

第三步:调用DMA_CMD,通道使能(要在对应的外设调用XXX_DMACmd开启一下触发信号的输出,如果需要DMA的中断,就调用DMA_ITConfig,开启中断输出,再在NVIC里,配置中断通道,最后写中断函数就行了)

下面介绍一下DMA相关的库函数:

void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);

//恢复缺省配置

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct); //DMA初始化

void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);

//DMA结构体初始化

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

//DMA使能/失能

void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);

//中断输出使能/失能

void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //DMA设置当前数据寄存器

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

//DMA获取当前数据寄存器的值

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);  //获取标志位状态 void DMA_ClearFlag(uint32_t DMAy_FLAG);  //清除状态标志位

ITStatus DMA_GetITStatus(uint32_t DMAy_IT);  //获取中断标志位状态 void DMA_ClearITPendingBit(uint32_t DMAy_IT);//清除中断标志位状态

下面是MyDMA.c的文件:

  1. #include "stm32f10x.h" // Device header
  2. void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
  3. {
  4. //第一步:RCC开启DMA的时钟
  5. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  6. //第二步:调用DMA_Init,初始化各个参数(包括外设和存储器的起始地址,数据宽度,地址是否自增,方向,传输计数器,是否需要自动重装,选择触发源,通道优先级)
  7. DMA_InitTypeDef DMA_InitStruct;
  8. DMA_InitStruct.DMA_PeripheralBaseAddr = AddrA;
  9. DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度,按字节的宽度搬运
  10. DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //启用地址++自增
  11. // 以上是外设站点(数据来源)的起始地址、数据宽度、是否自增。
  12. DMA_InitStruct.DMA_MemoryBaseAddr = AddrB;
  13. DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //数据宽度,按字节的宽度粘贴
  14. DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //启用地址++自增
  15. //以上3条是存储器(目的地)的起始地址、数据宽度、是否自增。
  16. DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
  17. DMA_InitStruct.DMA_BufferSize = Size; //缓存区大小,其实就是传输计数器 取用传进来的参数
  18. DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //传输模式,其实就是是否启用自动重装 不自动重装
  19. DMA_InitStruct.DMA_M2M = DMA_M2M_Enable; //选择是硬件触发还是软件触发 软件触发
  20. DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; // 优先级 选择中等优先级
  21. DMA_Init(DMA1_Channel1, &DMA_InitStruct); //第一个参数选择了是哪个DMA到哪个DMA通道,第二个参数结构体
  22. //第三步:调用DMA_CMD,通道使能(要在对应的外设调用XXX_DMACmd开启一下触发信号的输出,如果需要DMA的中断,就调用DMA_ITConfig,开启中断输出,再在NVIC里,配置中断通道,最后写中断函数就行了)
  23. DMA_Cmd(DMA1_Channel1, ENABLE);
  24. }

下面是MyDMA.h文件:

  1. #ifndef __MYDMA_H
  2. #define __MYDMA_H
  3. void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
  4. #endif

下面是main.c文件:

  1. #include "stm32f10x.h" // Device header
  2. #include "OLED.h"
  3. #include "MyDMA.h"
  4. uint8_t dataA[]={0x01, 0x02, 0x03, 0x04};
  5. uint8_t dataB[]={0,0,0,0};
  6. int main(void)
  7. {
  8. OLED_Init(); //oled 屏幕初始化
  9. OLED_ShowHexNum(1,1, dataA[0], 2);
  10. OLED_ShowHexNum(1,4, dataA[1], 2);
  11. OLED_ShowHexNum(1,7, dataA[2], 2);
  12. OLED_ShowHexNum(1,10, dataA[3], 2);
  13. OLED_ShowHexNum(2,1, dataB[0], 2);
  14. OLED_ShowHexNum(2,4, dataB[1], 2);
  15. OLED_ShowHexNum(2,7, dataB[2], 2);
  16. OLED_ShowHexNum(2,10, dataB[3], 2);
  17. MyDMA_Init((uint32_t)dataA, (uint32_t)dataB, 4);
  18. OLED_ShowHexNum(3,1, dataA[0], 2);
  19. OLED_ShowHexNum(3,4, dataA[1], 2);
  20. OLED_ShowHexNum(3,7, dataA[2], 2);
  21. OLED_ShowHexNum(3,10, dataA[3], 2);
  22. OLED_ShowHexNum(4,1, dataB[0], 2);
  23. OLED_ShowHexNum(4,4, dataB[1], 2);
  24. OLED_ShowHexNum(4,7, dataB[2], 2);
  25. OLED_ShowHexNum(4,10, dataB[3], 2);
  26. while(1)
  27. {
  28. }
  29. }

这样就实现了最简单的DMA数据搬运的小功能了。看似没有什么用,其实这功能是在硬件上自动完成的,可以在不浪费软件资源的前提下,把外设上的数据搬运到内存中来,软件就能直接用了,还是非常的方便的。

今天又更改了一点代码,单独写了一个函数用来启动搬运工作,更改位置为:

源文件为:

  1. #include "stm32f10x.h" // Device header
  2. uint16_t MyDMA_Size;
  3. void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
  4. {
  5. MyDMA_Size = Size;
  6. //1,RCC
  7. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  8. //2,DMA_Init()
  9. DMA_InitTypeDef DMA_InitStruct;
  10. DMA_InitStruct.DMA_PeripheralBaseAddr = AddrA;
  11. DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  12. DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  13. DMA_InitStruct.DMA_MemoryBaseAddr = AddrB;
  14. DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  15. DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
  16. DMA_InitStruct.DMA_BufferSize = Size;
  17. DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
  18. DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;
  19. DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
  20. DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
  21. DMA_Init(DMA1_Channel1, &DMA_InitStruct);
  22. //3:DMA_Cmd
  23. DMA_Cmd(DMA1_Channel1, DISABLE);
  24. }
  25. void DMA_BanYun(void)
  26. {
  27. DMA_Cmd(DMA1_Channel1, DISABLE);
  28. DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
  29. DMA_Cmd(DMA1_Channel1, ENABLE);
  30. while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
  31. DMA_ClearFlag(DMA1_FLAG_TC1);
  32. }

下面就是主main函数的内容了:

这样就会直接显示数组A和B的每一个元素的内容和数组的地址,随后数组A中的每一个元素都自加1,屏幕是可以计时看到结果的,过1秒钟后开始搬运,才能看到搬运的结果,数组B的值发生变化,就这样周而复始的循环。来个图片看看吧,CSDN上放个小视频太麻烦了,也不改进一下???下面图片中的数值是在不断地自增中。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/292301
推荐阅读
相关标签
  

闽ICP备14008679号