mini2440裸机试炼之——DMA直接存取 实现Uart(串口)通信

这个仅仅能作为自己初步了解MDA的开门篇

实现功能:

        将字符串数据通过DMA0通道传递给UTXH0,然后在终端

        显示。传输数据完后。DMA0产生中断,beep声, LED亮。

DMA基本知识

计算机系统中各种经常使用的数据输入/输出方法有查询方式(包含无条件及条件传送方式)和中断方式,这些方式适用于CPU与慢速及中速外设之间的数据交换。但当高速外设要与系统内存或者要在系统内存的不同区域之间进行大量数据的高速传送时。就在一定程度上限制了数据传送的速率。直接存储器存取(DMA)就是为解决问题提出的,採用DMA方式,在一定时间段内,由DMA控制器代替CPU。获得总线控制权,来实现内存与外设或者内存的不同区域之间大量数据的高速传送。

DMA的概述:

SC2440支持位于系统总线与外围总线之间的四通道DMA控制。每一通道的DMA都能够处理一下四种情况:

1.源和目的器件均能够在系统总线

2.源器件在系统总线而目的器件在外围总线

3.源器件在外围总线而目的器件在系统总线

4.源和目的器件均能够在外围总线

DMA最大的有点就是能够在没有CPU干涉的情况下进行数据的传送。能够通过软件控制DMA启动,或者通过内部请求或者外部请求引脚启动。

DMA控制

DMA使用三态的有限状态机FSM (Finite State Machine)。有限状态机制)对其进行控制,下面用三步进行描写叙述:

状态1:

在初始状态,DMA等待DMA请求。当DMA请求到达时。进入状态2。在这 阶段DMA ACK和INT REQ均为0。

状态2:

在这个阶段,DMA ACK变成1以及计数器CURR_TC从DCON[19:0]寄存器载入数据。

(注意:DMA ACK保持1直到对其清零)

状态3:

在这个阶段,DMA对进行原子操作(atomic operation)的子有限状态机(sub-FSM)进行初始化。

sub-FSM从源地址读取数据以后并写进目的地址。在这个操作前。数据的大小和传输的大小均应给予考虑。在总体模式(Whole service mode)下的计数器(CURR_TC)为0之前。传输数据的操作将会继续。

当sub-FSM完毕原子操作后,主FSM进行倒计。另外,在计数器CRRR_TC为0以及中断设置DCON[29]寄存器被置1时,主FSM发出INT REQ信号。除此之外。同一时候清除DMA ACK。

 

DMA寄存器配置

    rDISRC0=(U32)SendBuffer;                    //数据地址	
    rDISRCC0 |=((0<<1)|(0<<0));                 //[1]系统总线,[0]地址将依据单次和突发模式中每次传输后其数据大小而添加
    rDIDST0=(U32)UTXH0;                         //传输目标地址UTXH0 {2440addr.h文件里定义 #define UTXH0       (0x50000020) }
    rDIDSTC0 |=((0<<2)|(1<<1)|(1<<0));          //[2]在TC到达0时发生中断, [1] APB(外设总线上)  , [0]传输后地址总线不变
    //对上述寄存器说明。rDISRC0、rDIDST0分别配置为数据地址(DMA内部)、传输目标地址(uart是属于外设设备),再对应的配置rDISRCC0、rDIDSTC0
  
    rDCON0 |=((U32)1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(1<<24)|(1<<23)|(1<<22)|(0<<20)|(15);   
   //[31] 握手模式; [30]同步PCLK=50M(APB时钟< 外设 >); [29]当全部传输完毕产生中断请求(即CURR_TC变为0);[28]运行一个单元传输;
   //[27]选择每次原子传输(单次或突发4)后DMA停止和等待其他DMA请求的单服务模式; [24]UART0; [23]选择DMA源触发DMA操作;
   //[22]当传输计数的当前值变为0时DMA通道(DMA  REQ)关闭; [20]字节(每次一个字节传输);[19:0]初始传输计数15,15=SendBuffer字符数组长度;
   
    rDMASKTRIG0=(0<<2)|(1<<1)|(0<<0);  //[1]打开DMA通道而且处理此通道DMA请求


总结

源地址:    字符串SendBuffer的地址

目的地址:  uart0的地址

MDA仅仅是实现传输数据的一种机制,桥梁搭建的作用

这里并没实现MDA字符串SendBuffer赋值,而是CPU完毕的,MDA仅仅是实现直接从内存(字符串已经被CPU存放于内存)传输到uart0, 字符串Hello mini2440!,长15,每次输出一个字节,要输出十五次。tc就是存放输出的次数每输出一次tc减1,为0时, DMA停止和等待其他DMA请求的单服务模式,假设设置中断则产生MDA中断。所以说tc也就是节拍。

代码区

Main.c

#define GLOBAL_CLK      1

#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"  //函数声明
#include "2440slib.h"
#include "mmu.h"
#include "profile.h"

//函数声明处
extern void DMA_UART(void);
            

void Main(void)
{   
    U32 mpll_val = 0,consoleNum;
    Port_Init();
   
    mpll_val = (92<<12)|(1<<4)|(1);
    
    //init FCLK=400M,
    ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3);
    ChangeClockDivider(14, 12);   //the result of rCLKDIVN [0:1:0:1] 3-0 bit
    cal_cpu_bus_clk();            //HCLK=100M   PCLK=50M
   
    consoleNum = 0;   // Uart 1 select for debug.
    Uart_Init( 0,115200 );
    Uart_Select( consoleNum );
    
    MMU_Init();//中断映射地址初始化
    Beep(2000, 100);           

    DMA_UART();                  //DMA方式实现Uart(串口)通信

}

DMA_uart.c

//====================================================================
//  实现功能:
//           DMA直接存取  实现Uart(串口)通信,
//       将字符串数据通过DMA0通道传递给UTXH0,然后在终端
//       显示。传输数据完后,DMA0产生中断,beep声, LED亮。
//                                                   by:梁惠涌
//====================================================================

#include "2440addr.h"
#include "2440lib.h"  //beep函数

char  *SendBuffer = "Hello mini2440!" ;    //source data

/**************************************************************
                          DMA初始化
**************************************************************/
void Dma_init()
{
    rDISRC0=(U32)SendBuffer;                    //数据地址  
    rDISRCC0 |=((0<<1)|(0<<0));                 //[1]系统总线,[0]地址将依据单次和突发模式中每次传输后其数据大小而添加
    rDIDST0=(U32)UTXH0;                         //传输目标地址UTXH0 {2440addr.h文件里定义 #define UTXH0       (0x50000020) }
    rDIDSTC0 |=((0<<2)|(1<<1)|(1<<0));          //[2]在TC到达0时发生中断, [1] APB(外设总线上)  , [0]传输后地址总线不变
    //对上述寄存器说明。rDISRC0、rDIDST0分别配置为数据地址(DMA内部)、传输目标地址(uart是属于外设设备),再对应的配置rDISRCC0、rDIDSTC0
  
    rDCON0 |=((U32)1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(1<<24)|(1<<23)|(1<<22)|(0<<20)|(15);   
   //[31] 握手模式; [30]同步PCLK=50M(APB时钟< 外设 >); [29]当全部传输完毕产生中断请求(即CURR_TC变为0);[28]运行一个单元传输;
   //[27]选择每次原子传输(单次或突发4)后DMA停止和等待其他DMA请求的单服务模式; [24]UART0; [23]选择DMA源触发DMA操作;
   //[22]当传输计数的当前值变为0时DMA通道(DMA  REQ)关闭; [20]字节(每次一个字节传输);[19:0]初始传输计数15,15=SendBuffer字符数组长度;
   
    rDMASKTRIG0=(0<<2)|(1<<1)|(0<<0);  //[1]打开DMA通道而且处理此通道DMA请求
}

/**************************************************************
                          DMA中断
**************************************************************/
void __irq Dma0_isr(){
    rSRCPND|=0x1<<17;   //清除中断挂起状态
    rINTPND|=0x1<<17;
    Uart_Printf("
       ***DMA TO Uart finished***
");
    Beep(2000,100);
    rGPBCON=0x015400;
    rGPBDAT=0x6<<5;
}

/**************************************************************
|                          DMA子函数
|  需设置2440lib.c里   rUCON0  |=((1<<0) | (1<<3) | (2<<10) ); 
**************************************************************/
void DMA_UART(){
    Uart_Printf("
       ***HELLO DMA TO Uart ***
");
    Delay(1000);
    
    Dma_init();//DMA初始化       
    rINTMSK &=~(1<<17);  //开 dma0 中断
    pISR_DMA0=(U32)Dma0_isr;
    
   
    while(1){
    }   
}

截图



完整项目下载地址


原文地址:https://www.cnblogs.com/blfshiye/p/5181581.html