初步了解DMA——直接存储器访问
杜洋工作室 www.DoYoung.net
洋桃电子 www.DoYoung.net/YT
- 在此声明一下所有代码均为杜洋工作室的不允许复制,转发以及在商业上的行为等,本人只是在此程序上进行理解和注释。
上一次的笔记是在洋桃开发板上进行Flash闪存的使用,主要配合其他外设。对Flash的基本了解有兴趣可以去看看:
https://blog.csdn.net/qq_40546576/article/details/99695256
本次主要讲的是了解DMA。由于本次特殊,需要截取部分图片进行讲解,可能造成了观看不适,请大家谅解!谢谢。
DMA主要是可以直接访问数据,不需要通过CPU。可以外设和内存之间相互访问,也可以内存与内存之间访问。
很不幸STM32不是只有一个DMA,居然有2个DMA,分别为DMA1与DMA2。
接下来我们介绍STM32F10xxx参考手册
了解《STM32F10xxx参考手册》
麻烦大家多看看《STM32F10xxx参考手册》,这个样子会直接查看手册,大家就会对不同的芯片都会游刃有余。
我用的是洋桃开发板,里面是STM32F103C8T6的Flash闪存为32K字节,属于小容量的。
官方介绍
主要特征
STM32的DMA1有7个通道,DMA2有5个通道,相信大家看了上面的介绍因该了解这个通道个数,呢么每个通道也有各自的用处的,下面展示DMA1每个通道可以干什么(接收哪些请求)
呢么DMA2的每个通道又有什么用处呢,下面展示DMA2每个通道接收哪些请求
DMA的固件库使用
接下来这个才是我们真正要用的必须掌握,前面均可以了解。
我们需要选择的DMA配置
官方固件库内定义的(stm32f10x_dmah)
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. */
uint32_t DMA_MemoryBaseAddr; /*!< Specifies the memory base address for DMAy Channelx. */
uint32_t DMA_DIR; /*!< Specifies if the peripheral is the source or destination.
This parameter can be a value of @ref DMA_data_transfer_direction */
uint32_t DMA_BufferSize; /*!< Specifies the buffer size, in data unit, of the specified Channel.
The data unit is equal to the configuration set in DMA_PeripheralDataSize
or DMA_MemoryDataSize members depending in the transfer direction. */
uint32_t DMA_PeripheralInc; /*!< Specifies whether the Peripheral address register is incremented or not.
This parameter can be a value of @ref DMA_peripheral_incremented_mode */
uint32_t DMA_MemoryInc; /*!< Specifies whether the memory address register is incremented or not.
This parameter can be a value of @ref DMA_memory_incremented_mode */
uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.
This parameter can be a value of @ref DMA_peripheral_data_size */
uint32_t DMA_MemoryDataSize; /*!< Specifies the Memory data width.
This parameter can be a value of @ref DMA_memory_data_size */
uint32_t DMA_Mode; /*!< Specifies the operation mode of the DMAy Channelx.
This parameter can be a value of @ref DMA_circular_normal_mode.
@note: The circular buffer mode cannot be used if the memory-to-memory
data transfer is configured on the selected Channel */
uint32_t DMA_Priority; /*!< Specifies the software priority for the DMAy Channelx.
This parameter can be a value of @ref DMA_priority_level */
uint32_t DMA_M2M; /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer.
This parameter can be a value of @ref DMA_memory_to_memory */
}DMA_InitTypeDef;
/***********************************************************************************************************************/
//上面为官方的英文翻译一下(个人理解):
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; /*外设地址*/
uint32_t DMA_MemoryBaseAddr; /*存储器地址 */
uint32_t DMA_DIR; /*数据传输方向*/
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_M2M; /*是否为存储器传输到存储器*/
}DMA_InitTypeDef;
//注:M2M的表达和P2P表达方法一样P是点,2的英语发音为to,也就是点对点,我们这里M2M则为内存对内存
接下来我们单独拿出洋桃家的代码进行分析,他家入门100步视频没有专门介绍DMA部分,但是提及了和解释ADC中的DMA相关的代码。我们截取部分在ADC中的DMA初始化程序,大家可以了解一下。可能不适合大家的代码需求,不能直接复制使用!!
void ADC_DMA_Init(void){ //DMA初始化设置
DMA_InitTypeDef DMA_InitStructure; //定义DMA初始化结构体
DMA_DeInit(DMA1_Channel1); //复位DMA通道1
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义DMA通道外设基地址=ADC1_DR_Address
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址
DMA_InitStructure.DMA_BufferSize = 2;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 当前存储器地址:Disable不变,Enable递增(用于多通道采集)
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}
上面程序还需要一个开启时钟程序,程序可以放在初始化程序中也可以放在其他程序,但是一定是在整个初始化过程中开启。
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟
洋桃家放在GPIO初始化中,而秉火家放在DMA初始化中,其实一样的,但不可以缺少!!!
下面我们了解stm32的ADC的使用,还有DAC的。
最后用ADC和DMA做一些小东西
参考来源:
- Google搜寻引擎等等
- 杜洋工作室 www.DoYoung.net
- 洋桃电子 www.DoYoung.net/YT
- STM32库开发实战指南 基于STM32F103(第二版)
- 《stm32f1xx 参考手册》