DMA的解释
DMA(Direct Memory Access,直接存储器访问)。 DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。
MSP430系列单片机扩展的DMA具有来之所有外设的触发器,不需要CPU的干预即可提供先进的可配置的数据传输能力,从而加速了基于MCU的信号处理进程,DMA传输的触发来源对CPU 来说是完全透明的,DMA控制器可在内存与外部及外部硬件之间进行精确的传输控制。DMA 消除了数据传输延迟时间以及各种开销,从而可以解放16位RISC CPU,以便其将更多的时间用于处理数据,而非执行正在处理的任务。
MSP430系列单片机的DMA模块大多有以下特点:
- 无需CPU介入,完全由DMA控制器自行管理。
- 在整个地址空间范围内有效,块方式传输可达65536字节;
- 每次传输仅需要2个MCLK;
- 可在CPU进入超低功耗模式时运行;
- 字节和字混合传输;
- 四种传输寻址模式:固定地址到固定地址、固定地址到块地址、块地址到固定地址以及块地址到块地址;
- 触发方式灵活:边沿或者电平触发。
- 多种触发源可配置。
- 多通道优先级可配置。
DMA的应用
最常见的场景,当你想吃饭时,你需要中止你的工作,走出去,到某家店,点餐,等餐,用餐,再回到你的工作地点继续你的工作。如果工作比较多的时候呢?你就没有时间外出,那你一般就是点个外卖,然后继续你的工作,等外卖员把餐送到了,再用餐,用餐后再继续你的工作。现在把这个场景全部类比到单片机的工作中来,吃饭是一个支线任务,但无法直接开始,需要等待,点餐是一个机器周期可以完成的简单操作,你的工作是CPU的主线任务。对于CPU而言,如果主线任务比较繁重,而支线任务又不得不做,那做了支线任务就必然要占用做主线的时间,这个结果就可能导致主线任务完成得不好,所以在这个时候我们就需要一个外卖员,他就是DMA!
好多人是按章节学习,到了DMA这一章就到处询问怎么使用DMA,要程序要代码,但是我在上面也举过例子了,你只有在CPU主线特别忙得时候才需要用到DMA,又或者是某种低功耗需求而IO处理有特别多得时候。没有这种需求你完全没必要去搞清楚DMA是怎么用的,你只要知道有这么个东西就行了,将来有需求了再回来研究。因为这里面还是有很多坑的,如果你急这在项目里上马DMA,那不可预料的BUG真的是会耽误你很多时间。所以根据你的需求,谨慎选择是否继续读下去。
注释已翻译成中文了
#include <msp430.h>
#include <stdint.h>
int main(void)
{
WDTCTL = WDTPW+WDTHOLD; // 关闭看门狗
P1DIR |= 0x01; // P1.0 设为输出
__data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) 0x1C00);
// 设置源地址
__data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) 0x1C20);
// 设置目标地址
DMA0SZ = 16; // 设置传输快尺寸
DMA0CTL = DMADT_5+DMASRCINCR_3+DMADSTINCR_3;
// 通道0:重复块传输、源地址和目标地址自动增计数,默认为软件触发
DMA0CTL |= DMAEN; // 使能DMA通道0
while(1)
{
P1OUT |= 0x01; // 置位P1.0
DMA0CTL |= DMAREQ; // 触发块传输
P1OUT &= ~0x01; // 复位P1.0
}
}
一些说明
- 官方历程由来:点我下载msp430ware,具体请自行搜索。
- 关于程序执行过程,手册说DMA块传输需要2×MCLK×DMAxSZ(记tD)的时间,所以在执行了触发语句DMA0CTL |= DMAREQ; 后CPU将处于挂起状态,即在本例中,在执行*P1OUT &= ~ 0x01;*之前,系统会待机tD ,一定要引起注意。
- 在老版的CCS中用MSP430wave导入的例程中,代码长这样:
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW+WDTHOLD; // 关闭看门狗
P1DIR |= 0x01; // P1.0 设为输出
__data16_write_long((unsigned short) &DMA0SA,(unsigned long) 0x1C00);
// 设置源地址
__data20_write_long((unsigned short) &DMA0DA,(unsigned long) 0x1C20);
// 设置目标地址
DMA0SZ = 16; // 设置传输快尺寸
DMA0CTL = DMADT_5+DMASRCINCR_3+DMADSTINCR_3;
// 通道0:重复块传输、源地址和目标地址自动增计数,默认为软件触发
DMA0CTL |= DMAEN; // 使能DMA通道0
while(1)
{
P1OUT |= 0x01; // 置位P1.0
DMA0CTL |= DMAREQ; // 触发块传输
P1OUT &= ~0x01; // 复位P1.0
}
}
你在编译的时候会发现在设置地址的地方有警告 #770-D conversion from pointer to smaller integer 你可以自行比较一下新旧连个例程就应该知道问题在哪了1。
待续
之所以提到这个,是因为在我写总结的时候才下载了最新的CCS9.10,而之前我的工程都是用的CCS6.10,而我的工程电脑也不联网,就不知道有新版本。这个警告起初困惑了我很久,因为我确实习惯消除掉程序里的所有警告,而为了解决这个问题我还真是测试了很久才get到20位地址空间这个点,算是个不大不小的坑。通过这个例子,我建议大家还是时不时关注下工具软件的更新,兴许一些老问题就迎刃而解了。 ↩︎