DMA简介:
直接内存访问,是一种不经过CPU而直接从内存存取数据的数据交换模式。在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给CPU,这样就很大程度上减轻了CPU资源占有率,可以大大节省系统资源;数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是SRAM 或者是FLASH 。DMA 控制器包含了DMA1 和DMA2,其中DMA1 有7 个通道, DMA2 有5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是DMA2 只存在于大容量的单片机中。
DMA模式数据传输过程:
- 如果外设要想通过DMA 来传输数据,必须先给DMA 控制器发送DMA 请求,DMA收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且DMA 控制器收到应答信号之后,就会启动DMA 的传输,直到传输完毕。
- DMA 具有12 个独立可编程的通道,其中DMA1 有7 个通道,DMA2 有5 个通道,每 个通道对应不同的外设的DMA 请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。
- 当发生多个DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器来管理。仲裁器管理DMA通道请求分为两个阶段。第一阶段属于软件阶段,可以在 DMA_CCRx 寄存器中设置,有4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的DMA通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道1高于通道2。在大容量产品和互联型产品中, DMA1 控制器拥有高于DMA2 控制器的优先级。
DMA数据配置:
DMA传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储 器到存储器。具体的方向DMA_CCR 位4 DIR 配置:0 表示从外设到存储器,1表示从存储器到外设。这里面涉及到的外设地址由DMA_CPAR 配置,存储器地址由DMA_CMAR 配置。
- 外设到存储器 :当我们从外设到存储器传输时,以ADC采集为例。DMA外设寄存器的地址对应的就是ADC数据寄存器的地址,DMA存储器的地址就是我们自定义的变量(用来接收存储AD 采集的数据)的地址。设置外设为源地址。
- 存储器到外设 :当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。DMA 外设寄存器的地址对应的就是串口数据寄存器的地址,DMA 存储器的地址就是我们自定义的变量 (相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。设置外设为目标地址。
- 存储器到存储器 :当我们使用从存储器到存储器传输时,以内部FLASH 向内部SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部FLASH (我们这里把内部FALSH 当作一个外设来看)的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部FLASH 的数据)的地址。设置外设(即内部FLASH )为源地址。跟上面两个不一样的是,这里需要把DMA_CCR 位 14:MEM2MEM:存储器到存储器模式配置为1,启动M2M 模式。
bsp_dma_usart.h文件:
#ifndef __DMA_USART1_ #define __DMA_USART1_ #include "stm32f10x.h" #include "stdio.h" #define USARTx USART1//暂时还不清楚其他的几个串口应用在什么场景 #define USART_CLK RCC_APB2Periph_USART1 #define USART_BAUDRATE 115200 #define USART_GPIO_CLK RCC_APB2Periph_GPIOA #define USART_TX_GPIO_PORT GPIOA #define USART_TX_GPIO_PIN GPIO_Pin_9 #define USART_RX_GPIO_PORT GPIOA #define USART_RX_GPIO_PIN GPIO_Pin_10 #define USART_TX_DMA_CHANNEL DMA1_Channel4//不同外设对应不同通道,一个通道对应多个外设,这个是串口发送通道 #define USART_DR_ADDRESS (USART1_BASE+0X04)//串口1的数据寄存器的地址 #define SENDBUFF_SIZE 5000//缓冲区的自定义字节数 void USART_Config(void); void USARTx_DMA_Config(void); #endif
bsp_dma_usart.c文件:
#include "bsp_dma_usart1.h" uint8_t SendBuff[SENDBUFF_SIZE]; void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStrcture; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(USART_GPIO_CLK,ENABLE);//使能GPIOA的时钟 RCC_APB2PeriphClockCmd(USART_CLK,ENABLE);//使能串口的时钟 GPIO_InitStrcture.GPIO_Pin=USART_TX_GPIO_PIN;//传输引脚 GPIO_InitStrcture.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出 GPIO_InitStrcture.GPIO_Speed=GPIO_Speed_50MHz;//速度 GPIO_Init(USART_TX_GPIO_PORT,&GPIO_InitStrcture);//初始化配置 GPIO_InitStrcture.GPIO_Pin=USART_RX_GPIO_PIN;//接受引脚 GPIO_InitStrcture.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(USART_RX_GPIO_PORT,&GPIO_InitStrcture);//初始化配置 USART_InitStructure.USART_BaudRate=USART_BAUDRATE;//配置波特率 USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长 USART_InitStructure.USART_StopBits=USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity=USART_Parity_No;//无检验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件控制位 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发一起 USART_Init(USARTx, &USART_InitStructure);//初始化配置 USART_Cmd(USARTx,ENABLE);//使能串口 } void USARTx_DMA_Config() { DMA_InitTypeDef DMA_InitStructure;//声明结构体变量 RCC_APB2PeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA1时钟 DMA_InitStructure.DMA_PeripheralBaseAddr=USART_DR_ADDRESS;//外设地址为串口的数据寄存器的位置 DMA_InitStructure.DMA_MemoryBaseAddr=(u32)SendBuff;//存储器地址位数组首元素的地址 DMA_InitStructure.DMA_BufferSize=SENDBUFF_SIZE;//缓冲区大小也即数组大小 DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//存储器向外设传输数据 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//失能外设地址自增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//使能存储器地址自增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设的数据单位为字节 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器的数据单位为字节 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//一次 //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循环 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //禁止存储器向存储器传输 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure); //初始化配置 DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);//使能DMA传输通道 }
main.c文件:
#include"stm32f10x.h" #include"bsp_led.h" #include"bsp_dma_usart1.h" extern uint8_t SendBuff[SENDBUFF_SIZE]; static void Delay(__IO u32 nCount); int main(void) { uint16_t i; USART_Config(); USARTx_DMA_Config(); LED_GPIO_Config(); //uint16_t i;放在这里会报错 for(i=0;i<SENDBUFF_SIZE;i++) { SendBuff[i] = 'L'; } //串口向DMA发送TX请求 USART_DMACmd(USARTx, USART_DMAReq_Tx, ENABLE); while(1) { red(ON); Delay(0xFFFFF); red(OFF); green(ON); Delay(0xFFFFF); green(OFF); } } static void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); }
以上即为有效程序的主体代码。