下面的代码是单片机串口发送数据的程序.
char a=0xAA;//定义变量a,值为0xAA;
TXREG = a;//把数据由内存转移到串口外设;
那我们定义的变量a的值存储在哪里了呢?可以看下单片机的逻辑框图。
变量其实都是存在一个叫SRAM的存储器中,它是单片机的内存。存储变量的这个步骤,是由单片机的内核通过总线来操作完成。
第二行代码:它是把内存里的数据发送到串口的数据寄存器当中。当串口的数据寄存器接收到数据之后,它就会由串口外设自动的把数据发送出去,它也是需要由内核操作的。
但如果是发送一个字节的数据,一瞬间就能完成;如果是发送1万个0xAA,大约需要10s,对于单片机而言,将导致CPU极其宝贵的资源全都消耗再在数据转移上。
为了解决转移数据占用CPU资源这个痛点,工程师们设计了一种叫DMA的模块(Direct Memory Acess 直接内存访问),它的主要作用就是转移数据,比如由内存向串口外设转移数据,或者外设向内存转移数据,且不需要内核的参与,这样内核就可以腾出手来操作其它的事情,比如控制I/O口或者响应中断(如下图)。(即CPU负责和内存交互,而DMA负责内存向低速设备的复制)
以下面的常规程序而言,它需要先发送一万个串口数据,发送数据就得需要10s,然后才可以让LED闪烁。发送串口数据和LED灯闪烁没有办法同时进行。
for (i=0;i<10000;i++)
{
TXREG = 0xAA;//让串口发送0xAA;
}
LED = 1; //点亮LED;
Delay(5); //延时5s
LED = 0;
Delay(5); //延时5s
但如果我们应用DMA后,它就能一边发送串口数据,一边让LED灯闪烁。
//串口发送 //LED闪烁
for (i=0;i<10000;i++) LED = 1; //点亮LED;
{ Delay(5); //延时5s
TXREG = 0xAA;//让串口发送0xAA; LED = 0;
} Delay(5); //延时5s
那DMA是如何实现这个效果的呢?可以看下采用了DMA之后的程序
for(i=0;i<10000;i++)
{
SendData[i] = 0xAA;
}
DMA_PeriAddr = 0xFFFF; //设置DMA要传输的目标寄存器的地址,此例程为SRAM数据转移到串口外设,例程中串口外设数据寄存器的地址为0xFFFF;
DMA_SramAddr = 0x0000;//设置SRAM内存源地址,也就是我们要转移的第一个数据的内存地址
DMA_Direction = DMA-Peri;//指明数据转移的方向,及从DMA到串口外设
DMA_DataSize = 10000;//设置传输数据的大小
DMA_Sram+ = ON;//设置内存地址自增,如果为OFF,则会一直发送第一个数据
DMA_Peri+ = OFF;//让串口外设地址不增;因为这些数据都是转移到串口,所以这些数据都发往同一个地址
LED_shanruo(10);
上述程序中数组里的数据就是我们要发送的串口数据,并被存储在SRAM里,而且它们被存储的地址是连续的(如下图)
该程序在最开始阶段先配置好DMA初始化程序,然后就开始通过DMA发送数据,只有配置DMA时需要内核参与,配置完之后就不需要内核再参与了。这时候,DMA负责数据转移的操作,与此同时,内核就可以腾出手来控制LED闪烁。
除了串口发送数据,还有很多数据转移的应用。比如串口接收数据,它是把串口外设里面的数据转移到内存;以及ADC模数转换,它是把ADC寄存器里面的数据转移到内存里面。