1.DMA传输概念
DMA--直接存储器存取
直接内存访问(DMA,Direct Memory Access)是一些计算机总线架构提供的功能,它能使数据从附加设备(如磁盘驱动器)直接发送到计算机主板上的内存,不需要CPU干预。
DMA主要是用于帮助CPU传输数据,DMA可以加快数据传输的速率,提高CPU工作效率。
在所有电子设备,主要有处理器的,我们大家关注的最重要的一方面就是设备运行的流畅度,也就是数据的处理能力。
应用场景:
1.串口(重要)--使用DMA,可以帮助我们快速的传输数据,保证数据的完整性。DMA中有缓存区的概念,而且DMA可以加快数据传输的速度,避免因为阻塞造成的数据丢失问题。
2.ADC--在有多个通道需要传输的时候,比如 检测电阻 检测光照,检测电压,使用多通道,但是多个通道的数据会汇总到一个DR寄存器中,DMA就可以解决多通道传输的问题,DMA中可以开辟缓存区,用于保存数据。
2.STM32F1系列的单片机中--DMA
直接存储器存取(DMA)作用:
提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。两个DMA控制器有12个通道(DMA1有7个通道, DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
DMA控制器数量:2个
DMA1通道:7个
DMA2通道:5个
优先级:很高、高、中等和低
数据宽度:字节、半字(16bit)、全字(32Bit)
ADC数据宽度--12位,数据寄存器--16位。选择半字
串口--8bit,选择字节
3个事件标志:DMA半传输(传输一半)、 DMA传输完成(数据传输结束)和DMA传输出错。
传输方向:存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输
外设到外设---错误(CPU不需要干预数据传输,但是数据传输的开始和结束都是需要CPU把控的)。
外设和存储器分类:闪存、 SRAM、外设的SRAM(存储器)、 APB1、 APB2和AHB外设(片上外设)均可作为访问的源和目标。
数据传输数量:可编程的数据传输数目:最大为65535
外设资源:
DMA框架:
使用串口的接收功能+DMA:
传输方向:外设---》存储器(内存)
源(外设):串口--数据寄存器(DR)
目标(内存):内存--我们工程师自己定义的存储空间,可以是一个char类型变量,也可以是一个数组
使用ADC采集数据+DMA功能:
传输方向:外设---》存储器(内存)
源(外设):ADC--数据寄存器(DR)
目标(内存):内存--我们工程师自己定义的存储空间,可以是一个short/float/int类型变量,也可以是一个数组。
DMA处理:
需要三个步骤:
DMA_CPARx:外设地址寄存器
DMA_CMARx:存储器地址寄存器
可编程的数据量
接收和发送双方的数据宽度必须保持一致
CCR寄存器中:
指针增量
以串口为例:
接受DMA
源--数据寄存器 USART_DR(我们的数据是8Bit)
目标--缓存区(内存),char rx_buff[100];
当数据接受的时候,rx_buff如何偏移?此时就需要用到指针增量,每次rx_buff需要偏移数据宽度个字节,在我们串口中一次偏移1个字节。
DMA的配置流程:
循环模式:一旦有数据传输,就不停的接收或者发送
单次模式:当设置的传输的数据量减为0,即停止。
DMA中断:
DMA中断触发事件:传输过半、传输完成、传输错误。
3.串口接收DMA配置
DMA1_CH5--USART1_RX
1.打开时钟
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
2.配置DMA的工作模式
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
DMA_InitTypeDef结构体内容:
typedef struct
{
u32 DMA_PeripheralBaseAddr;
u32 DMA_MemoryBaseAddr;
u32 DMA_DIR;
u32 DMA_BufferSize;
u32 DMA_PeripheralInc;
u32 DMA_MemoryInc;
u32 DMA_PeripheralDataSize;
u32 DMA_MemoryDataSize;
u32 DMA_Mode;
u32 DMA_Priority;
u32 DMA_M2M;
} DMA_InitTypeDef;
3.使能DMA控制器
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
4.接收数据
什么时候接收数据??
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
void DMA_ClearFlag(uint32_t DMAy_FLAG);
假如需要使用中断:
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
编写串口的接收DMA代码,必须有串口驱动
#include "my_dma.h"
#include "stdio.h"
char rx_data=0;//接收缓存区(一个字节)
void DMA1_Config(void)
{
DMA_InitTypeDef DMA_InitSources;
//打开时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
/*
typedef struct
{
u32 DMA_PeripheralBaseAddr;
u32 DMA_MemoryBaseAddr;
u32 DMA_DIR;
u32 DMA_BufferSize;
u32 DMA_PeripheralInc;
u32 DMA_MemoryInc;
u32 DMA_PeripheralDataSize;
u32 DMA_MemoryDataSize;
u32 DMA_Mode;
u32 DMA_Priority;
u32 DMA_M2M;
} DMA_InitTypeDef;
*/
DMA_InitSources.DMA_PeripheralBaseAddr=(u32)&(USART1->DR);//外设地址
DMA_InitSources.DMA_MemoryBaseAddr=(u32)&rx_data;//内存地址
DMA_InitSources.DMA_DIR=DMA_DIR_PeripheralSRC;//外设作为源,内存目标
DMA_InitSources.DMA_BufferSize=1;//缓存区大小
DMA_InitSources.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitSources.DMA_MemoryInc=DMA_MemoryInc_Disable;
DMA_InitSources.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//外设数据宽度
DMA_InitSources.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//内存数据宽度
DMA_InitSources.DMA_Mode=DMA_Mode_Circular;//循环模式
DMA_InitSources.DMA_Priority=DMA_Priority_High;//优先级
DMA_InitSources.DMA_M2M=DMA_M2M_Disable;//不适用内存到内存。
//配置DMA的工作模式
DMA_Init(DMA1_Channel5,&DMA_InitSources);
//使能
DMA_Cmd(DMA1_Channel5,ENABLE);
//串口接收DMA使能
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
}
void Get_Data(void)
{
//判断DMA是否接收完成
if(DMA_GetFlagStatus(DMA1_FLAG_TC5))
{
DMA_ClearFlag(DMA1_FLAG_TC5);
printf("USART1_RX_DMA--->%x\r\n",rx_data);
}
}
.h文件
#ifndef _MY_DMA_H_
#define _MY_DMA_H_
#include "stm32f10x.h" //stm32f1头文件
void DMA1_Config(void);
void Get_Data(void);
#endif