MSP430F1单片机的学习1——认识和使用DMA
MSP430F1单片机的学习1——认识和使用DMA

使用编译工具:IAR For MSP430 6.4
使用单片机:MSP430F1611

单片机的DAM是个啥?

说起DAM,玩单片机的人应该大多都听说过,但也有一些初学者不太了解,这到底是个啥,有人说用DAM的速度快,有人说用DMA不占用CPU内存,但是有没有比较好理解的解释呢?哎嘿,那绝对是当然的了,请看我的表演,其实DMA可以看做是一家专门的搬运公司,没错,DMA就是MCU中的一家搬运公司,它可以帮你把货物从货源地A搬运到目的地B地址,只要你告诉他什么时候开始搬,要怎样搬,你就不用管了,他就会给你搬过去,而且速度贼快。

在MSP430F1611中,搬运公司共有3个司机,分别是DMA0、DMA1、DMA2,
DMA总寄存器,DMACTL0、DMACTL1是公司的统一控制器,分别控制着三个司机的工作方式
DMACTL0 :选择DAM的工作模式,选择触发源,运输的条件
DMACTL1 : 传输条件、中断,需要运输时,司机响应的情况

DMA0寄存器,主要给DMA0分配具体工作
DMA0SA :设置DAM的货源,必须为16位地址,(u16) DMA0DA :设置DAM的目的地,必须为16位地址,(u16)*
DMA0SZ :设置送货的数量,字节为单位
DMA0CTL :设置运输方式,、启动,停止、中断、等控制

DMA1寄存器,主要给DMA1分配具体工作
DMA1SA :设置DAM的货源,必须为16位地址,(u16) DMA1DA :设置DAM的目的地,必须为16位地址,(u16)*
DMA1SZ :设置送货的数量,字节为单位
DMA1CTL :设置运输方式、启动,停止、中断、等控制

DMA2寄存器,主要给DMA2分配具体工作
DMA2SA :设置DAM的原始货源地,必须为16位地址,(u16)* DMA2DA :设置DAM的原始目的地,必须为16位地址,(u16)*
DMA2SZ :设置送货的数量,字节为单位
DMA2CTL :设置运输方式、启动,停止、中断、等控制

重点要控制的是,
DMACTL0 ——选择触发源,
DMAxCTL(以DMA0位代表DMA0CTL)——设置运输方式、启动,停止、中断、等控制
DMAxSA(DMA0SA)——原始货源地,可以设置单片机内的所有地址,包括UART的发送BUF,Flash,ROM等,
DMAxDA(DMA0DA )——原始货源地,可以设置单片机内的所有地址,包括UART的发送BUF,Flash,ROM等,
DMAxSZ (DMAxSZ )——货物总量,所要传输的数据大小,以字节为单位,
DMACTL1

具体程序测试,使用DMA0进行测试,DMA1、DMA2的用法一样,不赘述。
测试内容:
在DMACTL1、DMA0SA、DMA0DA、DMA0SZ、DMA0CTL寄存器不变的情况下,改变DMACTL0的值,从而改变触发传输的方式
DMACTL0、DMA0SA、DMA0DA、DMA0SZ、DMA0CTL不变的情况下,改变DMACTL1
DMACTL1、DMACTL0、DMA0SA、DMA0SZ、DMA0CTL不变的情况下,改变DMA0DA

在DMACTL1、DMA0SA、DMA0DA、DMA0SZ、DMA0CTL寄存器不变的情况下,改变DMACTL0的值,从而改变触发传输的方式;DMACTL0中低四位控制DMA0的触发方式,总共有2^4=16种,如下图所示,


测试开始:DMACTL0 = DMA0TSEL_0,DMA_REQ触发模式,DMA0CTL寄存器的DMAREQ位 置位 了,就触发1次传输,该位软件设置触发,DMA0CTL |= DMAREQ;该位自动清零;

打开Memory,在左上角的Go to中输入我们要观察的地址,

继续运行,可以看到 0x0220——0x025f 共0x40个字节被重新赋值,而0x0280——0x02bf 的0x40个字节的值还是没变,比较乱;

继续运行,盯着这个值,运行当前一句代码,

如下,DAM0把0x0220中的货物(值)运送到了目的地0x0280中,

以后每次触发REQ,发现DMA0都会去以原始货源地地址为基准的下一个地址,然后把那里的值运送到以原始目的地为基准的下一地址,最终把所有货物 DMA0SZ = 0x040;运输完成

DMA完成传输,之后再触发REQ,后面地址的值也不会变,DAM0已经干完活,去休息了。


测试代码:
#include <msp430.h>
__no_init u8 SDEventBuf[0x10] @0x0220;
//u8 SDEventBuf[0x10] ={0};
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗
for(u16 temp=0;temp<0x40;temp++) //给数组赋值
{
*(SDEventBuf+temp) = temp;
}
DMACTL0 = DMA0TSEL_0; // 选择DMA0触发方式 ,REQ
DMACTL1 = 0; //触发时的响应,中断等,无
DMA0SA = (u16)SDEventBuf; // DMA0货源地,16bit
DMA0DA = 0x0280; // DMA0目的地,16bit
DMA0SZ = 0x040; // 货物总量,字节
// 字节to字节,每次触发运输一个字节,货源地地址自加,目的地地址自加,启动DMA0
DMA0CTL = DMASBDB + DMADT_4 + DMASRCINCR_3 + DMADSTINCR_3 + DMAEN;
for (;;)
{
DMA0CTL |= DMAREQ; // 手动触发,DMA0REQ
}
}

改变DMACTL0,DMACTL0 = DMA0TSEL_0,触发方式为Timer_A(TACCR2.IFG), TACCTL2的CCIFG位 置位 了,就触发1次传输,如果设置了Timer_A定时器的CCR2,并使能了标志位,那么每次定时时间到,就触发1次传输;本次测试使用软件设置该位,TACCTL2 |= CCIFG;标志位自动清零;这就是DMA和定时器Timer的结合使用

直接来到这一步,触发之前,可以看到 0x0220——0x025f 共0x40个字节被重新赋值,而0x0280——0x02bf 的0x40个字节的值还是没变,比较乱;

运行当前一句代码,如下,同样的,DAM0把0x0220中的货物(值)运送到了目的地0x0280中,

跟上面是一样的,除了触发方式不一样了,其他都一样,

测试代码:
#include <msp430.h>
#include "typeredefine.h"
__no_init u8 SDEventBuf[0x10] @0x0220;
//u8 SDEventBuf[0x10] ={0};
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗
for(u16 temp=0;temp<0x40;temp++) //给数组赋值
{
*(SDEventBuf+temp) = temp;
}
DMACTL0 = DMA0TSEL_1; // 选择DMA0触发方式 ,REQ
DMACTL1 = 0; //触发时的响应,中断等,无
DMA0SA = (u16)SDEventBuf; // DMA0货源地,16bit
DMA0DA = 0x0280; // DMA0目的地,16bit
DMA0SZ = 0x040; // 货物总量,字节
// 字节to字节,每次触发运输一个字节,货源地地址自加,目的地地址自加,启动DMA0
DMA0CTL = DMASBDB + DMADT_4 + DMASRCINCR_3 + DMADSTINCR_3 + DMAEN;
for (;;)
{
TACCTL2 |= CCIFG; // 手动触发,TACCTL2 |= CCIFG
}
}

总结, DMACTL0控制着DMA0——DMA2的触发方式,每个DMA通道有16种触发方式,以DMA为例(其他的为相应的DMAxTSEL_x)进行简单描述:
DMA0TSEL_0 软件设置DMA0CTL |= DMAREQ;触发, DMA0TSEL_1 定时器Timer_A的CCR2标志位 置位,即TACCTL2 &CCIFG==CCIFG触发 DMA0TSEL_2 定时器Timer_B的CCR2标志位 置位,即TACCTL2 &CCIFG==CCIFG;触发 DMA0TSEL_3 UART0/I2C的接收标志位 置位,UART/SPI模式: IFG1 &URXIFG0 == URXIFG0; 触发 。I2C模式,未测试
DMA0TSEL_4 UART0/I2C的发送标志位 置位, UART/SPI模式 IFG1 &UTXIFG0 == UTXIFG0; 触发;但是需要注意的是,IFG1的UTXIFG0位需要在U0CTL的 SWRST要置零后才能触发,即U0CTL &= ~SWRST; 做个标记,当设置U0CTL &= ~SWRST之后触发了一次中断。后面有时间测试一下UART功能。I2C模式,未测试
DMA0TSEL_5 DAC12的中断标志位 置位,即DAC12_0CTL &DAC12IFG== DAC12IFG;触发
DMA0TSEL_6 ,标记,测试失败
DMA0TSEL_7 定时器Timer_A的CCR0标志位 置位,即TACCTL0 &CCIFG==CCIFG触发
DMA0TSEL_8 定时器Timer_B的CCR0标志位 置位,即TACCTL0 &CCIFG==CCIFG;触发
DMA0TSEL_9 UART1的接收标志位 置位,即 IFG2 &URXIFG1 == URXIFG0; 触发 DMA0TSEL_10 UART1的发送标志位 置位,即 IFG2 &URXIFG1 == URXIFG0; 触发;但是需要注意的是,IFG2的UTXIFG2位需要在U1CTL的 SWRST要置零后才能触发,即U1CTL &= ~SWRST; 做个标记,当设置U1CTL &= ~SWRST之后触发了一次中断。后面有时间测试一下UART功能。
DMA0TSEL_11
DMA0TSEL_14 DMA其他中断标志位置位触发,其中DAM2→DMA0,DMA0→ DMA1, DMA1 → DMA2,即,DMA2CTL &DMAIFG == DMAIFG; 触发DMA0传输,以此类推。
DMA0TSEL_15 暂未找到如何测试

DMACTL0、DMA0SA、DMA0DA、DMA0SZ、DMA0CTL不变的情况下,改变DMACTL1, DAMCTL1寄存器的各个位的控制如下

14-12位,控制每次传输的方式,DMADT_0——DMADT_7共8种模式;
11-10位,控制目的地地址的变化方式,共DMADSTINCR_0——DMADSTINCR_3四种,其实是三种;
9-8位,控制货源地地址的变化方式,共DMASRCINCR_0——DMASRCINCR_3四种,其实是三种;
7-6位,控制货源地→目的地的地址分配方式,共四种,DMASWDW:字→字, DMASBDW:字节→字,DMASWDB :字→字节,DMASBDB:字节→字节;上面的测试使用的都是:字节→字节;
第5位,控制传输触发是上升沿触发,还是高电平触发,DMALEVEL=0为上升沿触发;
第4位,DMAEN控制启动DMA的开关,DMAEN=1,开;
第3位,DMAIFG,中断触发标志位,当开启中断后,传输的总货量完成(DMA0SZ=0)之后,触发中断,DMAIFG =1;
第2位,DMAIE,DMA中断开关,DMAIE=1,开启DMA中断;
第1位,DMAABORT,DMA停止传输开关,当DMAABORT=1,传完当前的一次传输后,停止传输;
第0位,DMAREQ,软件触发DAM传输,当触发方式为DMA0TSEL_0——DMAREQ触发时,DMAREQ = 1,触发传输,并且自动清零。

为了方便测试,DMACTL0 = DMA0TSEL_0;使用软件触发方式进行测试。

先改变传输方式:
DMA0CTL = DMASBDB + DMAIE + DMASRCINCR_3 + DMADSTINCR_3 + DMAEN;
不变,
改变:DMA0CTL |=DMADT_0;从DMADT_0——DMADT_78种模式

程序测试:未完待续。。。
飞花两岸照船红
百里榆堤半日风
卧看满天云不动
不知云与我俱东
——————2020年6月24日09:02:15