目录
1.DMA的介绍
1.DMA就是直接存储器访问它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。只在开始与结束时通知一下CPU。中间的传输不用去管。传输方向可以是外设到存储器、存储器到外设、存储器到存储器。
2.DMA的通道
STM32最多有2个DMA控制器(DMA2仅存在大容量产品中),DMA1有7个通道。DMA2有5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个DMA请求的优先权。
DMA1 7个通道对应的外设图表 每个通道同时只能有一个外设请求
DMA2 5个通道对应的外设框图 同一时刻只能选择一个外设。s
每个通道可以设置优先权 如:最高、高、中、低。如果有相同的软件优先级,那么由硬件来决定。DMA数据传输可以是8位、16位、32位宽度。
3.DMA的特性
- 每个通道都直接连接专用的硬件DMA请求,都支持软件触发,这些通过软件来配置。
- 在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推) 。
- 独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源 和目标地址必须按数据传输宽度对齐。
- 支持循环的缓冲器管理
- 每个通道都有3个事件标志(DMA 半传输,DMA传输完成和DMA传输出错), 这3个事件标志逻辑或成为一个单独的中断请求。
- 外设和存储器,存储器和外设的传输 ,存储器和存储器间的传输
- 闪存、SRAM、外设的SRAM、APB1 APB2和AHB外设均可作为访问的源和目标。
- 可编程的数据传输数目:最大为65536
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //外设的地址
uint32_t DMA_MemoryBaseAddr; //存储器的地址
uint32_t DMA_DIR; //传输方向 外设作为数据传输的目的地(因为是存储器传到外设)
uint32_t DMA_BufferSize; //指定DMA通道DMA缓存的大小,即需要开辟几个内存空间
uint32_t DMA_PeripheralInc; //外设地址是否递增
uint32_t DMA_MemoryInc; //存储器地址是否递增
uint32_t DMA_PeripheralDataSize; //外设的数据宽度
uint32_t DMA_MemoryDataSize; //存储器的数据宽度
uint32_t DMA_Mode; //是否开启循环模式
uint32_t DMA_Priority; //dma1的优先级
uint32_t DMA_M2M; //设置存储器传到存储器
}DMA_InitTypeDef;
4.DMA1 通道4(串口接收)实例
1.DMA1的初始化
u16 temp;
void myDMA_init(DMA_Channel_TypeDef *channl,u32 target,u32 src,u16 dataSize)
{
DMA_InitTypeDef dma1;
//1.开启DMA1的时钟 串口时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //这里不用开启因为在uart_init(115200)有使能串口
temp=dataSize; //保存DMA缓存的大小
//2.DMA初始化
dma1.DMA_BufferSize=dataSize; //指定DMA通道DMA缓存的大小,即需要开辟几个内存空间
dma1.DMA_DIR=DMA_DIR_PeripheralDST; //传输方向 外设作为数据传输的目的地(因为是存储器传到外设)
dma1.DMA_M2M=DMA_M2M_Disable; //要不要存储器传到存储器 (不能跟循环传输同时用)
dma1.DMA_MemoryBaseAddr=src; //存储器的地址
dma1.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte ; //存储器的数据传输宽度
dma1.DMA_MemoryInc=DMA_MemoryInc_Enable; //内存地址寄存器递增
dma1.DMA_Mode=DMA_Mode_Normal; //工作在正常缓存模式
dma1.DMA_PeripheralBaseAddr=target; //外设的地址
dma1.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte ; //外设的数据传输宽度
dma1.DMA_PeripheralInc=DMA_PeripheralInc_Disable ; //外设的地址寄存器不递增
dma1.DMA_Priority=DMA_Priority_Medium; //dma1的优先级
DMA_Init(channl,&dma1);
}
2.DMA的使能
void myDMA_enble(DMA_Channel_TypeDef *channl)
{
DMA_Cmd(channl, DISABLE); //失能DMA 这时DMA通道DMA缓存的大小读会清零
DMA_SetCurrDataCounter(channl, temp);
DMA_Cmd(channl, ENABLE); //使能DMA
}
3.主函数
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "dma.h"
#define SEND_BUF_SIZE 8200 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK WarShip STM32F1 DMA 串口实验"};
int main(void)
{
u16 i;
u8 t=0;
u8 j,mask=0;
float pro=0;//进度
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init(); //初始化LCD
KEY_Init(); //按键初始化
//DMA1通道4(对应串口1接收外设),外设为串口1,存储器为SendBuff,长度SEND_BUF_SIZE.
MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);
myDMA_enble(DMA1_Channel4);//开始一次DMA传输!
//把TEXT_TO_SEND的值给 SendBuff
j=sizeof(TEXT_TO_SEND);
for(i=0;i<SEND_BUF_SIZE;i++)//填充数据到SendBuff
{
if(t>=j)//加入换行符
{
if(mask)
{
SendBuff[i]=0x0a; //\n
t=0;
}else
{
SendBuff[i]=0x0d; //\r
mask++;
}
}else//复制TEXT_TO_SEND语句
{
mask=0;
SendBuff[i]=TEXT_TO_SEND[t];
t++;
}
}
while(1)
{
t=KEY_Scan(0);
if(t==KEY0_PRES)//KEY0按下
{
//LCD显示
LCD_ShowString(30,150,200,16,16,"Start Transimit....");
LCD_ShowString(30,170,200,16,16," %");//显示百分号
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送
MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!
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/SEND_BUF_SIZE;//得到百分比
pro*=100; //扩大100倍
LCD_ShowNum(30,170,pro,3,16);
}
LCD_ShowNum(30,170,100,3,16);//显示100%
LCD_ShowString(30,150,200,16,16,"Transimit Finished!");//提示传送完成
}
}
}
}
总结:还可以是存储器到存储器比如用一个数组的数据复制到另一个数组。在DMA结构体中设置允许存储器传到存储器。