1.直接存储访问DMA(Direct Memory Access):DMA传输不需要CPU的参与,直接在内存和I/O设备间开辟了一条新的数据传输通道,不仅提高数据传输的速率,还因为不需要CPU的干预,从而提高了CPU的利用率。(注:文中的资料参考于正点原子)
STM32最多有2个DMA通道(只有大容量的STM32才有DMA2)。DMA1有7个通道,DMA2有5个通道,每个通道用来管理一个或者多个外设对存储器的访问请求。还有一个通道用来仲裁协调各个DMA请求的优先级。STM32的DMA特性如下:
STM32F103RCT6的DMA1通道表如下:
每个通道同一时刻只能有一个外设使用DMA进行数据传输。比如DMA1的通道1中有三个外设(ADC1、TIM2_CH3、TIM4_CH1),同一时刻只能使用其中的一个外设进行DMA数据传输。本文是利用USART1进行数据传输,由表可知,需要使用的DMA1的通道4。
DMA配置的基本步骤:
(1)使能DMA的时钟,并配置DMA的初始结构体。
(2)开启DMA。
(3)开启对应外设的DMA数据传输。
2.DMA相关的寄存器:
(1)DMA中断状态寄存器(DMA_ISR):当开启DMA_ISR的这些中断后,产生中断触发条件时会跳转到相应的中断服务函数。即使没有开启这些中断,也可以通过这些位来判断当前DMA的传输状态。比如可以用TCIFx来判断DMA是否传输完成。此寄存器为只读寄存器,所以当被置位之后,需要通过其他的操作来清除。
(2)DMA中断标志清除寄存器(DMA_IFCR):DMA_IFCR 的各位就是用来清除 DMA_ISR 的对应位的,通过写 0 清除。在 DMA_ISR 被置位后,必须通过向该位寄存器对应的位写入 0 来清除。
(3)DMA的其他寄存器:
3.DMA的初始化配置:
DMA的初始化函数为:void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)。参数1是DMA的通道号,参数2是一个结构体,其定义如下:
如下为DMA的一个配置实列:
初始化DMA后,然后需要使能外设的DMA功能:
最后,再使能DMA传输通道:
当需要查下DMA的状态时,可使用如下的函数:
4.代码:本文只展示DMA和main部分的代码,如果需要完整的代码,可以结合前面的文章来获取。
(1)dma.h:
#ifndef __DMA_H
#define __DMA_H
#include "stm32f10x.h"
//DMA_CHx:DMAͨµÀCHx
//cpar:ÍâÉèµØÖ·
//cmar:´æ´¢Æ÷µØÖ·
//cndtr:Êý¾Ý´«ÊäÁ¿
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr );
#endif
(2)dma.c:
#include "dma.h"
DMA_InitTypeDef DMA_InitStructure;
u16 DMA1_LEN; //DMA´«ÊäµÄÊý¾Ý³¤¶È
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr )
{
//1.ʹÄÜʱÖÓ£º
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//2.ÅäÖÃDMAµÄÏà¹Ø¼Ä´æÆ÷£º
DMA_DeInit(DMA_CHx); //½«DMAµÄͨµÀ1¼Ä´æÆ÷ÖØÉèΪȱʡֵ
DMA1_LEN = cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //ÍâÉè»ùµØÖ·
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //´æ·ÅDMAÊý¾ÝµÄµØÖ·
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//DMA´«Êä·½Ïò
DMA_InitStructure.DMA_BufferSize = cndtr; //Ò»´Î´«ÊäµÄÊý¾ÝÁ¿
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¹¤×÷ģʽ
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMAÓÅÏȼ¶
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //ÉèÖÃÊÇ·ñÊÇ´æ´¢Æ÷µ½´æ´¢Æ÷µÄ´«Êäģʽ
DMA_Init(DMA_CHx,&DMA_InitStructure); //½«ÉÏÊöÅäÖÃÐÅϢдÈëDMAµÄ¼Ä´æÆ÷ÖÐ
//¿ªÆôDMA£º
DMA_Cmd(DMA_CHx,DISABLE); //Ïȸ´Î»Ò»ÏÂ
DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_LEN); //ÉèÖÃDMAͨµÀµÄDMA»º´æ´óС
DMA_Cmd(DMA_CHx,ENABLE);
}
(3) main.c:
#include "led.h"
#include "usart.h"
#include "delay.h"
#include "lcd.h"
#include "dma.h"
#include "key.h"
const u8 TEXT_TO_SEND[]={"hello world,there are many good things,so we should hold on,hold on"};
#define TEXT_LENGTH sizeof(TEXT_TO_SEND) - 1 //-1ÊDz»°üº¬½áÊø·û
u8 SendBuff[(TEXT_LENGTH+2)*100];
int main(void)
{
float pro = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
LCD_Init();
usart_init(9600);
KEY_Init();
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //¿ªÆô´®¿ÚµÄDMA
//DMA1ͨµÀ4,ÍâÉèΪ´®¿Ú1,´æ´¢Æ÷ΪSendBuff,³¤(TEXT_LENTH+2)*100
MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,(TEXT_LENGTH+2)*100);
GPIO_SetBits(GPIOA,GPIO_Pin_8);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
while(1)
{
//printf("test\r\n");
POINT_COLOR=RED;
if(KEY_2)
{
LCD_ShowString(60,150,200,16,16,"Start Transimit....");
LCD_ShowString(30,40,200,24,24,"hello world");
LCD_ShowString(60,170,200,16,16," %");//ÏÔʾ°Ù·ÖºÅ
while(1)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)//µÈ´ýͨµÀ4´«ÊäÍê³É
{
DMA_ClearFlag(DMA1_FLAG_TC4);//Çå³ýͨµÀ4´«ÊäÍê³É±êÖ¾
break;
}
pro=DMA_GetCurrDataCounter(DMA1_Channel4);//µÃµ½µ±Ç°»¹Ê£Óà¶àÉÙ¸öÊý¾Ý
pro=1-pro/((TEXT_LENGTH+2)*100);//µÃµ½°Ù·Ö±È
pro*=100; //À©´ó100±¶
LCD_ShowNum(60,170,pro,3,16);
}
LCD_ShowNum(60,170,100,3,16);//ÏÔʾ100%
LCD_ShowString(60,150,200,16,16,"Transimit Finished!");//Ìáʾ´«ËÍÍê³É
delay_ms(1000);
}
}
}
5.运行结果:
6.总结:
本文介绍了DMA使用方法,并实验了串口的DMA功能。使用DMA的基本步骤是:
(1)开启时钟,并初始化DMA
(2)配置初始化的结构体信息,调用函数进行初始化
(3)开启DMA数据传输,并使能外设的MDA功能。
本文中理论部分介绍较多,在后面的章节中会利用DNM和ADC来进行实验。