【转】MSP430使用DMA详解

2019-07-20 15:31发布

直接存储器存取(DMADirect Memory Access)方式是用硬件实现存储器与存储器之间或存储器与IO设备之间直接进行高速数据传送,不需要CPU的干预。这种方式通常用来传送数据块。MSP430f16x系列单片机内部含有DMA模块,而且几乎内部所有外设都可以触发DMA开始存取数据。这里实现了这个模块的程序通用的函数库,方便使用。
  • 硬件介绍:MSP430F15X/16X 系列单片机具有DMA 控制器,从而能够为数据高速传输提供保证。例如,通过DMA控制器可以直接将ADC 转换存贮器的内容传到RAM 单元。
    MSP430系列单片机扩展的DMA具有来之所有外设的触发器,不需要CPU的干预即可提供先进的可配置的数据传输能力,从而加速了基于MCU的信号处理进程,DMA传输的触发来源对CPU 来说是完全透明的,DMA控制器可在内存与外部及外部硬件之间进行精确的传输控制。DMA 消除了数据传输延迟时间以及各种开销,从而可以解放16为RISC CPU,以便其将更多的时间用于处理数据,而非执行正在处理的任务。
    MSP430F16x系列单片机的DMA模块有以下特点:数据传送不需要CPU介入,完全由DMA控制器自行管理。在整个地址空间范围内传输数据,块方式传输可达65536字节;能够提高片内外设数据吞吐能力,实现高速传输,每个字或者字节的传输仅需要2个MCLK;减少系统功耗,即使在片内外设进行数据输入或输出时,CPU也可以处于超低功耗模式而不需唤醒;字节和字数据可以混合传送:DMA传输可以是字节到字节、字到字、字节到字或者字到字节。当字到字节传输时,只有字中较低字节能够传输,当从字节到字传输时,传输到字的低字节,高字节被自动清零;四种传输寻址模式:固定地址到固定地址、固定地址到块地址、块地址到固定地址以及块地址到块地址;触发方式灵活:边沿或者电平触发。单个、块或突发块传输模式:每次触发DMA操作,可以根据需要传输不同规模的数据
    DMA的四种寻址模式如下图所示:

    DMA控制器模块:3个独立的传输通道:通道0、通道1和通道2。每个通道都有源地址寄存器、目的地址寄存器、传送数据长度寄存器和控制寄存器。每个通道的触发请求可以分别允许和禁止;可配置的通道优先权:优先权裁决模块,传输通道的优先级可以调整,对同时有触发请求的通道进行优先级裁决,确定哪个通道的优先级最高。MSP430的DMA控制器可以采用固定优先级,还可以采用循环优先级。程序命令控制模块,每个DMA通道开始传输之前,CPU要编程给定相关的命令和模式控制,以决定DMA通道传输的类型;可配置的传送触发器:触发源选择模块,DMAREQ(软件触发)、Timer_ACCR2输出、Timer_BCCR2输出、I2C 数据接收准备好、I2C 数据发送准备好、USART接收发送数据、DAC12模块DAC12IFG、ADC12模块的ADC12IFGx、DMAxIFG、DMAE0 外部触发源。并且还具有触发源扩充能力。
    DMA有六种传输模式:单字或者单字节传输;块传输;突发块传输;重复单字或者单字节传输;重复块传输;重复突发块传输。前三个,传输完成后DMAEN自动复位;再次传输时需要重新置位DMAEN位以使能DMA通道。后三个为重复模式,一次传输完成后,DMAEN不复位;再次出发时,可以再次启动数据传输。六种传输模式通过DMADTx寄存器设置:
    <span style="color: green;">DMADTx      Transfer Mode               Description000         Single transfer             Each transfer requires a trigger. DMAEN is                                        automatically cleared when DMAxSZ transfers have                                        been made.001         Block transfer              A complete block is transferred with one trigger.                                        DMAEN is automatically cleared at the end of the                                        block transfer.010, 011    Burst-block transfer        CPU activity is interleaved with a block transfer.                                        DMAEN is automatically cleared at the end of the                                        burst-block transfer.100         Repeated single transfer    Each transfer requires a trigger. DMAEN remains                                        enabled.101         Repeated block transfer     A complete block is transferred with one trigger.                                        DMAEN remains enabled.110, 111    Repeated burst-block        CPU activity is interleaved with a block transfer.            transfer                    DMAEN remains enabled.

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
3条回答
叫我女王思密达
2019-07-20 17:31
程序实现:

DMA的使用主要是DMA寄存器的初始设置,设置完成后,DMA接到触发信号即可自动传输数据。

设置函数如下:

<span style="color: blue;">void </span>DMAInit(<span style="color: blue;">char </span>channel,<span style="color: blue;">char </span>trigger,<span style="color: blue;">char </span>transMode,<span style="color: blue;">char </span>srcMode,<span style="color: blue;">char </span>dstMode,             <span style="color: blue;">unsigned int </span>src,<span style="color: blue;">unsigned int </span>dst,<span style="color: blue;">unsigned int </span>size){    <span style="color: blue;">unsigned int </span>*DMAxCTL,*DMAxSA,*DMAxDA,*DMAxSZ;        DMACTL0 = trigger << (channel << 2);    DMACTL1 = 0x04;         <span style="color: green;">//DMA收到触发请求时,等待当前指令执行完成后        </span><span style="color: blue;">switch </span>(channel)        <span style="color: green;">//选择当前设置哪个DMA通道    </span>{        <span style="color: blue;">case </span>0:             DMAxCTL = (<span style="color: blue;">unsigned int </span>*)&DMA0CTL;            DMAxSA = (<span style="color: blue;">unsigned int </span>*)&DMA0SA;            DMAxDA = (<span style="color: blue;">unsigned int </span>*)&DMA0DA;            DMAxSZ = (<span style="color: blue;">unsigned int </span>*)&DMA0SZ;            <span style="color: blue;">break</span>;                                      <span style="color: green;">//指针 = 0通道控制        </span><span style="color: blue;">case </span>1:             DMAxCTL = (<span style="color: blue;">unsigned int </span>*)&DMA1CTL;            DMAxSA = (<span style="color: blue;">unsigned int </span>*)&DMA1SA;            DMAxDA = (<span style="color: blue;">unsigned int </span>*)&DMA1DA;            DMAxSZ = (<span style="color: blue;">unsigned int </span>*)&DMA1SZ;            <span style="color: blue;">break</span>;                                      <span style="color: green;">//指针 = 1通道控制        </span><span style="color: blue;">case </span>2:             DMAxCTL = (<span style="color: blue;">unsigned int </span>*)&DMA2CTL;            DMAxSA = (<span style="color: blue;">unsigned int </span>*)&DMA2SA;            DMAxDA = (<span style="color: blue;">unsigned int </span>*)&DMA2DA;            DMAxSZ = (<span style="color: blue;">unsigned int </span>*)&DMA2SZ;            <span style="color: blue;">break</span>;                                      <span style="color: green;">//指针 = 2通道控制    </span>}        <span style="color: blue;">switch </span>(transMode)      <span style="color: green;">//设置DMA通道的传输模式    </span>{        <span style="color: blue;">case </span><span style="color: rgb(163, 21, 21);">'S'</span>: *DMAxCTL = DMADT_0;   <span style="color: blue;">break</span>;          <span style="color: green;">//单次传输        </span><span style="color: blue;">case </span><span style="color: rgb(163, 21, 21);">'s'</span>: *DMAxCTL = DMADT_4;   <span style="color: blue;">break</span>;          <span style="color: green;">//重复单次传输        </span><span style="color: blue;">case </span><span style="color: rgb(163, 21, 21);">'B'</span>: *DMAxCTL = DMADT_1;   <span style="color: blue;">break</span>;          <span style="color: green;">//块传输        </span><span style="color: blue;">case </span><span style="color: rgb(163, 21, 21);">'b'</span>: *DMAxCTL = DMADT_5;   <span style="color: blue;">break</span>;          <span style="color: green;">//重复块传输        </span><span style="color: blue;">case </span><span style="color: rgb(163, 21, 21);">'I'</span>: *DMAxCTL = DMADT_2;   <span style="color: blue;">break</span>;          <span style="color: green;">//突发块传输 交错        </span><span style="color: blue;">case </span><span style="color: rgb(163, 21, 21);">'i'</span>: *DMAxCTL = DMADT_6;   <span style="color: blue;">break</span>;          <span style="color: green;">//重复突发块传输 交错    </span>}        *DMAxCTL |= (srcMode & 0x04) << 2;                  <span style="color: green;">//源 字或字节    </span>*DMAxCTL |= (srcMode & 0x03) << 8;                  <span style="color: green;">//源 地址改变方式        </span>*DMAxCTL |= (dstMode & 0x04) << 3;                  <span style="color: green;">//目的 字或字节    </span>*DMAxCTL |= (dstMode & 0x03) << 10;                 <span style="color: green;">//目的 地址改变方式        </span>*DMAxSA = src;    *DMAxDA = dst;    *DMAxSZ = size;        *DMAxCTL |= DMAEN;                                  <span style="color: green;">//DMA使能</span>}

函数比较麻烦,函数内容按参数设置每个寄存器。DMACTL0 = trigger << (channel << 2); 这个是设置对应channel通道的的参考源,不大明白的可以看下DMACTL0的寄存器内容;switch (channel)语句则根据通道设置对应指针指向的寄存器;然后对应设置参数即可。

当设置成非重复模式时,需要重新置位DMAEN,本程序就函数DMAReEnable实现:

<span style="color: blue;">void </span>DMAReEnable(<span style="color: blue;">char </span>channel){    <span style="color: blue;">switch </span>(channel)        <span style="color: green;">//使能对应通道    </span>{        <span style="color: blue;">case </span>0: DMA0CTL |= DMAEN;   <span style="color: blue;">break</span>;      <span style="color: green;">//0通道        </span><span style="color: blue;">case </span>1: DMA1CTL |= DMAEN;   <span style="color: blue;">break</span>;      <span style="color: green;">//1通道        </span><span style="color: blue;">case </span>2: DMA2CTL |= DMAEN;   <span style="color: blue;">break</span>;      <span style="color: green;">//2通道    </span>}}

这个函数比较简单,只是根据传入参数设置对应通道的DMAEN位。

当设置为软件触发时,需要软件启动DMA程序如下:

<span style="color: blue;">void </span>DMAStart(<span style="color: blue;">char </span>channel){    <span style="color: blue;">switch </span>(channel)        <span style="color: green;">//使能对应通道    </span>{        <span style="color: blue;">case </span>0: DMA0CTL |= DMAREQ;  <span style="color: blue;">break</span>;      <span style="color: green;">//0通道        </span><span style="color: blue;">case </span>1: DMA1CTL |= DMAREQ;  <span style="color: blue;">break</span>;      <span style="color: green;">//1通道        </span><span style="color: blue;">case </span>2: DMA2CTL |= DMAREQ;  <span style="color: blue;">break</span>;      <span style="color: green;">//2通道    </span>}}

这个和上个函数类似:仅仅设置一个控制位,函数很简单,不再解释啦。

程序实现就这么多了,有关详细内容可以下载附件里的程序库,程序的注释很详细。


一周热门 更多>