DMA(Direct Memory Access)直接存储器存取
DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
外设:指外设存储器DR(Data Register):ADC数据寄存器,串口数据寄存器
存储器:这指运行内存SRAM和程序存储器Flash,数据转运
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA资源:DMA1(7个通道)
存储器映像
DMA框图
外设是寄存器,寄存器也是存储器 ————从某个地址取内容,再放到另一个地址去
总线矩阵左端是主动单元,拥有存储器的访问权,有DCode和系统总线,可以访问右边的存储器
DCode总线是专门访问Flash的,DMA要转运数据,所以DMA也必须要有访问的主动权
右端是被动单元,存储器只能被主动单元读写
仲裁器:根据通道的优先级,决定谁先用谁后用,防止冲突。总线矩阵的仲裁器可以暂停DMA对CPU的访问,以防止冲突。不过仍然会保证CPU得到一半的总线带宽
DMA作为外设也有相应的寄存器,所以DMA既是总线矩阵的主动单元,可以读写各种存储器,也是AHB上的被动单元
DMA基本结构
起止地址:决定参数从哪来到哪去
数据宽度:指定一次转运要按多大的数据宽度来进行
地址是否自增:指定一次转运完成后,下一次转运是不是要把地址移动到下一个位置去
自动重装器:转运结束后,是否恢复原来的值,决定转运模式,单次还是循环
M2M:存储器到存储器
软件触发适合存储器到存储器的转运,不需要时机,需尽快完成的任务,软件触发和循环模式不能共用
DMA请求
数据宽度与对齐
如果数据宽度不一样,该如何解决?
源端数据比目标宽度大,就在目标数据前多出来的空位补0
反之则把多出来的高位舍弃掉
数据转运+DMA
ADC扫描模式+DMA
DMA初始化步骤
1.RCC开启DMA时钟
2.调用DMA_Init函数,初始化各个参数,结构体,开关控制DMA_Cmd
如果使用硬件触发,还需调用XXX_DMACmd,开启触发信号输出
如果需要DMA中断,调用DMA_ITConfig,开启中断输出
3.传输计数器清零,再给传输计数器赋值,就DMA失能,写传输计数器,DMA使能
代码部分
void DAC_DeInit(void);
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct);
void DAC_StructInit(DAC_InitTypeDef* DAC_InitStruct);
void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState);
#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
void DAC_ITConfig(uint32_t DAC_Channel, uint32_t DAC_IT, FunctionalState NewState);
#endif
void DAC_DMACmd(uint32_t DAC_Channel, FunctionalState NewState);
#define DMA_DIR_PeripheralDST ((uint32_t)0x00000010)
//外设站点作为目的地,传输方向是存储器站点到外设站点
#define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000)
//外设站点作为源头,传输方向是外设站点作为存储器站点
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. */
//其实就是传输计数器,直接赋值给了传输计数器的寄存器