直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU 任何干预,通过DMA 数据可以快速地移动。这就节省了CPU 的资源来做其他操作。
DMA 控制器有7 个通道,每个通道专门管理多个外设请求。

DMA配置:
下面是配置DMA 通道x 的过程(x 代表通道号):
1. 在DMA_CPARx 寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地
址将是数据传输的源或目标。
2. 在DMA_CMARx 寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的
数据将从这个地址读出或写入这个地址。
3. 在DMA_CNDTRx 寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
4. 在DMA_CCRx 寄存器的PL[1:0] 位中设置通道的优先级。
5. 在DMA_CCRx 寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、
外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。
6. 设置DMA_CCRx 寄存器的ENABLE 位,启动该通道。一旦启动了DMA 通道,它既
可响应联到该通道上的外设的DMA 请求。
当传输一半的数据后,半传输标志(HTIF) 被置1,当设置了允许半传输中断位(HTIE) 时,
将产生一个中断请求。在数据传输结束后,传输完成标志(TCIF) 被置1,当设置了允许传
输完成中断位(TCIE) 时,将产生一个中断请求。
DMA中断:
每个DMA 通道都可以在DMA 传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。


代码如下:
#include "dma.h"
#include "delay.h"
u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMATX_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC->AHBENR|=RCC_AHBENR_DMAEN; //开启DMA1时钟
delay_ms(5); //等待DMA时钟稳定
DMA_CHx->CPAR=cpar; //DMA1 外设地址
DMA_CHx->CMAR=(u32)cmar; //DMA1,存储器地址
DMA1_MEM_LEN=cndtr; //保存DMA传输数据量
DMA_CHx->CNDTR=cndtr; //DMA1,传输数据量
DMA_CHx->CCR=0X00000000; //复位
DMA_CHx->CCR|=DMA_CCR1_DIR; //从存储器读
DMA_CHx->CCR&=~DMA_CCR1_CIRC; //普通模式只触发法一次//配置成为循环接受
//DMA_CHx->CCR|=DMA_CCR1_CIRC;
DMA_CHx->CCR&=~DMA_CCR1_PINC; //外设地址非增量模式
DMA_CHx->CCR|=DMA_CCR1_MINC; //存储器增量模式
DMA_CHx->CCR&=~DMA_CCR1_PSIZE_0; //外设数据宽度为8位
DMA_CHx->CCR&=~DMA_CCR1_MSIZE_0; //存储器数据宽度8位
DMA_CHx->CCR|=DMA_CCR1_PL_0; //中等优先级
DMA_CHx->CCR&=~DMA_CCR1_MEM2MEM; //非存储器到存储器模式
}
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_CHx->CCR&=~(DMA_CCR1_EN); //关闭DMA传输
DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,传输数据量
DMA_CHx->CCR|=DMA_CCR1_EN; //开启DMA传输
}
void MYDMA_RX_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC->AHBENR|=RCC_AHBENR_DMAEN; //开启DMA1时钟
delay_ms(5); //等待DMA时钟稳定
DMA_CHx->CPAR=cpar; //DMA1 外设地址
DMA_CHx->CMAR=(u32)cmar; //DMA1,存储器地址
DMA1_MEM_LEN=cndtr; //保存DMA传输数据量
DMA_CHx->CNDTR=cndtr; //DMA1,传输数据量
DMA_CHx->CCR=0X00000000; //复位
DMA_CHx->CCR&=~DMA_CCR1_DIR; //从存储器读
//DMA_CHx->CCR&=~DMA_CCR1_CIRC; //普通模式
DMA_CHx->CCR|=DMA_CCR1_CIRC; //普通模式
DMA_CHx->CCR&=~DMA_CCR1_PINC; //外设地址非增量模式
DMA_CHx->CCR|=DMA_CCR1_MINC; //存储器增量模式
DMA_CHx->CCR&=~DMA_CCR1_PSIZE_0; //外设数据宽度为8位
DMA_CHx->CCR&=~DMA_CCR1_MSIZE_0; //存储器数据宽度8位
DMA_CHx->CCR|=DMA_CCR1_PL_0; //中等优先级
DMA_CHx->CCR&=~DMA_CCR1_MEM2MEM; //非存储器到存储器模式
MY_NVIC_Init(3,3,DMA1_Channel5_IRQn,1);//组2,最低优先级
DMA_CHx->CCR|=0x2;//允许传输完成中断
}
#ifndef __DMA_H
#define __DMA_H
#include "sys.h"
void MYDMATX_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);//配置DMA1_CHx
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);//使能DMA1_CHx
void MYDMA_RX_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);
#endif
#include "sys.h"
#include "uart_nvic.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "WWDG.h"
#include "DMA.h"
/*** * _ooOoo_ *
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* . ' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
*
* .............................................
* 佛祖保佑 永无BUG
*/
u8 len=0;
u16 i;
u8 t;
u8 TestStatus=0;//DMA传输完成标志位
u8 DST_Buffer[10]= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};//UART DMA接受到数据存放数组
u32 TEXT_TO_SEND[] = {'1','2','3','4','5','6','7','8','9'};
#define TEXT_LENTH 8//此处为buffer的大小,不过要在后面添加一个0D0A,此处是在后面添加的
u8 SendBuff[(TEXT_LENTH+2)*100];
char deta[20]={
0x00,0x11,0x99,0x33,0x00,0x44,0x88,0x88,0x66,0x44,
0x00,0x11,0x99,0x33,0x00,0x44,0x88,0x88,0x66,0x44
};
void Usart1SendByte(char *data)//发送一个字节 硬件连接选用的为USART1
{
len=20;
for(t=0;t<len;t++)
{
while((UART1->CSR&0x1)==0);//循环发送,直到发送完毕 while((UART1->SR&0X40)==0);//等待发送结束
UART1->TDR=deta[t];
}
UART_RX_STA=0;
}
void mydmasend()
{
// printf("\r\nDMA DATA:\r\n");
for(i=0;i<(TEXT_LENTH+2)*100;i++)//填充ASCII字符集数据
{
if(t>=TEXT_LENTH)//加入换行符
{
SendBuff[i++]=0x0d;
SendBuff[i]=0x0a;
t=0;
}else SendBuff[i]=UART_RX_BUF[t++];//复制TEXT_TO_SEND语句
}
UART1->GCR|=1<<1; //选择串口1的DMA方式发送
UART1->GCR|=1<<4; //使能串口1的发送
MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!
while(1)
{
if(DMA1->ISR&(1<<13))//等待通道4传输完成
{
DMA1->IFCR|=1<<13;//清除通道4传输完成标志
break;
}
}
// printf("\r\nUART DMA TEST OK!\r\n ");
}
void DMA1_Channel5_IRQHandler(void)
{
if(DMA1->ISR&0x00020000)
{
DMA1->IFCR|=0x00010000;
TestStatus = 1;
LED2=!LED2;
}
}
void MDArecive()
{
UART1->GCR|=1<<1; //选择串口1的DMA方式发送
UART1->GCR|=1<<3; //使能串口1的 接收
MYDMA_Enable(DMA1_Channel5);//开始一次DMA传输!
}
int main(void)
{
delay_init(); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
uart_nvic_init(SystemCoreClock/1000000,115200); //串口初始化为115200
LED1=1; //初始化与LED连接的硬件接口
wwdg_init(0X7F,0X5F,3);//计数器值为7f,窗口寄存器为5f,分频数为8
MYDMATX_Config(DMA1_Channel4,(u32)&UART1->TDR,(u32)SendBuff,(TEXT_LENTH+2)*1);//DMA1通道4,外设为串口1,存储器为SendBuff,长(TEXT_LENTH+2)*100.
MYDMA_RX_Config(DMA1_Channel5,(u32)&UART1->RDR,(u32)DST_Buffer,10);//DMA1通道4,外设为串口1,存储器为SendBuff,长(TEXT_LENTH+2)*100.
//显示提示信息 此处的*1为串口发送一次
//*x就为打印x次
MDArecive();
while(1)//死循环让程序停在此处
{
// MDArecive();
if(TestStatus == 1)
{
TestStatus=0;
mydmasend();
printf("\r\n");
}
}
}
版权声明:本文为badaoshaonian原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。