学习笔记 --- S3C2440 DMA操作原理

发布者:创新驿站最新更新时间:2022-04-02 来源: eefocus关键字:学习笔记  S3C2440  DMA操作 手机看文章 扫描二维码
随时随地手机看文章

DMA(Direct Memory Access,直接内存访问)是一种不经过CPU而直接从内存存取数据的数据交换模式。在需要进行大量数据交换的场合,用好DMA,可以大大提高系统的性能,因为DMA操作几乎不占用CPU资源。s3c2440提供了4个通道的DMA


每个DMA通道能处理下面四种情况的数据传输:

(1)源器件和目的器件都在系统总线 APB

(2)源器件在系统总线,目的器件在外设总线

(3)源器件在外设总线,目的器件在系统总线

(4)源器件和目的器件都在外设总线 AHB

下面的DMA驱动程序使用的是第四种,内存属于AHB总线上的,我们打算在内存中开辟两个连续空间,分别作为源和目的。我们用两个方法将源中的数据写到目的中,一种方法是让cpu去做,另外一种方法是让DMA去做:


#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define MEM_CPY_NO_DMA  0

#define MEM_CPY_DMA     1

 

#define BUF_SIZE  (512*1024)

 

#define DMA0_BASE_ADDR  0x4B000000

#define DMA1_BASE_ADDR  0x4B000040

#define DMA2_BASE_ADDR  0x4B000080

#define DMA3_BASE_ADDR  0x4B0000C0

 

struct s3c_dma_regs {

 unsigned long disrc;

 unsigned long disrcc;

 unsigned long didst;

 unsigned long didstc;

 unsigned long dcon;

 unsigned long dstat;

 unsigned long dcsrc;

 unsigned long dcdst;

 unsigned long dmasktrig;

};

 

 

static int major = 0;

 

static char *src;

static u32 src_phys;

 

static char *dst;

static u32 dst_phys;

 

static struct class *cls;

 

static volatile struct s3c_dma_regs *dma_regs;

 

static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);

/* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */

static volatile int ev_dma = 0;

 

static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

 int i;

 

 memset(src, 0xAA, BUF_SIZE);

 memset(dst, 0x55, BUF_SIZE);

 

 switch (cmd)

 {

                //这是非DMA模式

  case MEM_CPY_NO_DMA :

  {

   for (i = 0; i < BUF_SIZE; i++)

    dst[i] = src[i];  //CPU直接将源拷贝到目的

   if (memcmp(src, dst, BUF_SIZE) == 0)

   {

    printk("MEM_CPY_NO_DMA OKn");

   }

   else

   {

    printk("MEM_CPY_DMA ERRORn");

   }

   break;

  }

 

                //这是DMA模式

  case MEM_CPY_DMA :

  {

   ev_dma = 0;

   

   /* 把源,目的,长度告诉DMA */

                        /* 关于下面寄存器的具体情况,我们在注释3里面来详细讲一下 */

   dma_regs->disrc      = src_phys;        /* 源的物理地址 */

   dma_regs->disrcc     = (0<<1) | (0<<0); /* 源位于AHB总线, 源地址递增 */

   dma_regs->didst      = dst_phys;        /* 目的的物理地址 */

   dma_regs->didstc     = (0<<2) | (0<<1) | (0<<0); /* 目的位于AHB总线, 目的地址递增 */

   dma_regs->dcon       = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0);  /* AHP总线同步,使能中断,全速传输(BIT27),软件触发(BIT23), */

 

   /* 启动DMA */

   dma_regs->dmasktrig  = (1<<1) | (1<<0);  //打开通道,软件启动

 

   /* 如何知道DMA什么时候完成? */

   /* 休眠 */

   wait_event_interruptible(dma_waitq, ev_dma);

 

   if (memcmp(src, dst, BUF_SIZE) == 0)

   {

    printk("MEM_CPY_DMA OKn");

   }

   else

   {

    printk("MEM_CPY_DMA ERRORn");

   }

   

   break;

  }

 }

 

 return 0;

}

 

static struct file_operations dma_fops = {

 .owner  = THIS_MODULE,

 .ioctl  = s3c_dma_ioctl,

};

 

static irqreturn_t s3c_dma_irq(int irq, void *devid)

{

 /* 唤醒 */

 ev_dma = 1;

    wake_up_interruptible(&dma_waitq);   /* 唤醒休眠的进程 */

 return IRQ_HANDLED;

}

 

static int s3c_dma_init(void)

{

         /* 这里注册一个中断,当DMA数据传输完毕之后会发生此中断 */

 if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1))

 {

  printk("can't request_irq for DMAn");

  return -EBUSY;

 }

 

 /* 分配SRC, DST对应的缓冲区:之前我们知道在内核中开辟空间可以用kmalloc函数,这里却用了dma_alloc_writecombine,这是为什么呢?这是因为kmalloc开辟的空间其逻辑地址虽然是连续的,但是其实际的物理地址可能不是连续的。而DMA传输数据时,要求物理地址是连续的,dma_alloc_writecombine就满足这一点,这个函数的原型是:dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)其中size代表开辟的空间的大小,handle代表开辟的空间的物理地址,返回值是开辟的空间的逻辑地址*/

 src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);//源

 if (NULL == src)

 {

  printk("can't alloc buffer for srcn");

  free_irq(IRQ_DMA3, 1);

  return -ENOMEM;

 }

 

 dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);//目的

 if (NULL == dst)

 {

  free_irq(IRQ_DMA3, 1);

  dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);

  printk("can't alloc buffer for dstn");

  return -ENOMEM;

 }

 

 major = register_chrdev(0, "s3c_dma", &dma_fops);//注册字符设备

 

 /* 为了自动创建设备节点 */

 cls = class_create(THIS_MODULE, "s3c_dma");

 class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */

 

 dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));//这边是将DMA控制寄存器映射到内核空间

  

 return 0;

}

 

static void s3c_dma_exit(void)

{

 iounmap(dma_regs);

 class_device_destroy(cls, MKDEV(major, 0));

 class_destroy(cls);

 unregister_chrdev(major, "s3c_dma");

 dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);

 dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys); 

 free_irq(IRQ_DMA3, 1);

}

 

module_init(s3c_dma_init);

module_exit(s3c_dma_exit);

 

MODULE_LICENSE("GPL");

测试程序:


#include

#include

#include

#include

#include

#include

 

/* ./dma_test nodma

 * ./dma_test dma

 */

#define MEM_CPY_NO_DMA  0

#define MEM_CPY_DMA     1

 

void print_usage(char *name)

{

 printf("Usage:n");

 printf("%s n", name);

}

 

 

int main(int argc, char **argv)

{

 int fd;

 

  if (argc != 2)

 {

  print_usage(argv[0]);

  return -1;

 }

 

 fd = open("/dev/dma", O_RDWR);

 if (fd < 0)

 {

  printf("can't open /dev/dman");

  return -1;

 }

 

 if (strcmp(argv[1], "nodma") == 0)

 {

  while (1)

  {

   ioctl(fd, MEM_CPY_NO_DMA);

  }

 }

 else if (strcmp(argv[1], "dma") == 0)

 {

  while (1)

  {

   ioctl(fd, MEM_CPY_DMA);

  }

 }

 else

 {

  print_usage(argv[0]);

  return -1;

 }

 return 0;  

}

测试方法:

# insmod dma.ko      //加载驱动

# cat /proc/interrupts //查看中断

           CPU0

 30:      52318         s3c  S3C2410 Timer Tick

 33:          0         s3c  s3c-mci

 34:          0         s3c  I2SSDI

 35:          0         s3c  I2SSDO

 36:          0         s3c  s3c_dma

 37:         12         s3c  s3c-mci

 42:          0         s3c  ohci_hcd:usb1

 43:          0         s3c  s3c2440-i2c

 51:       2725     s3c-ext  eth0

 60:          0     s3c-ext  s3c-mci

 70:         97   s3c-uart0  s3c2440-uart

 71:        100   s3c-uart0  s3c2440-uart

 83:          0           -  s3c2410-wdt

Err:          0


# ls /dev/dma     //查看设备

/dev/dma


# ./dmatest       //如此就会打印用法

Usage:

./dmatest


# ./dmatest dma     //以DMA方式拷贝,CPU可以做其他事情

MEM_CPY_DMA OK

MEM_CPY_DMA OK

MEM_CPY_DMA OK

MEM_CPY_DMA OK

MEM_CPY_DMA OK

MEM_CPY_DMA OK

MEM_CPY_DMA OK


# ./dmatest nodma     //CPU拷贝,各种竞争CPU

MEM_CPY_NO_DMA


MEM_CPY_NO_DMA

MEM_CPY_NO_DMA


————————————————————————————————————————————————————————————————————————————


熟悉了DMA与寄存器配置后, 下面来看下裸奔S3C2440的时候如何使用DMA来播放音频:


转自:http://blog.csdn.net/zhaocj/article/details/5583935


下面我们就用DMA的方式来实现音频的播放。由于是用DMA的方式,因此在播放的过程中不占用系统资源,我们可以很容易的实现声音的各种操作而丝毫不影响播放的效果,如音量的提高和降低、静音、暂停等。在这里,还需要强调一点,利用DMA传输数据,一次最多可以传输的字节大小为:DSZ×TSZ×TC,DSZ表示的是数据大小(字节、半字还是字,即是1、2还是4),TSZ表示的是传输大小(单元传输还是突发传输,即1还是4),TC表示传输计数值(即寄存器DCONn的低20位存放的数据),因此如果需要传输的字节大小超出了这三个参数乘积的大小,则还要进一步处理,在我们给出的程序中,我们就考虑了这方面的问题。下面就是具体的程序,其中我们是利用UART来实现音频信号的播出、停止、暂停、静音、音量的提高和降低的。


……   ……

//一段纯音频数据数组

unsigned char music[] = {

0xF9, 0xFF, 0xF5, 0xFF, 0xF8, 0xFF, 0xF8, 0xFF, 0xF6, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xF9, 0xFF,

0xF6, 0xFF, 0xF6, 0xFF, 0xFA, 0xFF, 0xFD, 0xFF, 0xFA, 0xFF, 0xFA, 0xFF, 0xF7, 0xFF, 0xF6, 0xFF,

……   ……

};

 

int result;

int remainder;

char flag;

char cmd;

char play_state;

 

void __irq uartISR(void)

{

       char ch;

       rSUBSRCPND |= 0x1;

       rSRCPND |= 0x1<<28;

       rINTPND |= 0x1<<28;

       ch=rURXH0;

      

       switch(ch)

       {

[1] [2] [3] [4]
关键字:学习笔记  S3C2440  DMA操作 引用地址:学习笔记 --- S3C2440 DMA操作原理

上一篇:学习笔记--- S3C2440 对NANDFLASH操作原理与测试代码分析
下一篇:S3C2440的RAM和启动过程!

推荐阅读最新更新时间:2024-11-23 18:42

STM32学习笔记11——HardFault_Handler处理方法
根据网络资料及自己调试经验总结如下: STM32 出现 HardFault_Handler 故障的原因主要有两个方面: 1、内存溢出或者访问越界。这个需要自己写程序的时候规范代码,遇到了需要慢慢排查。 2、堆栈溢出。增加堆栈的大小。 排查方法: 发生异常之后可首先查看 LR 寄存器中的值,确定进入异常前一刻使用的堆栈为 MSP 或 PSP,然后找到相应堆栈的指针? 注:在 HardFault_Handler(void)中断里第一条语句打断点,进入中断后,查看 LR 寄存器的值,如果是 0XFFFFFFF9,那么中断前使用的是 MSP,如果是 0XFFFFFFFD,那么中断前使用的是 PSP; 根据找到的堆栈指针, 在内存中查
[单片机]
STM32<font color='red'>学习</font><font color='red'>笔记</font>11——HardFault_Handler处理方法
S3C2440交叉编译环境搭建
在韦东山《Linux开发使用手册》光盘中的tool目录下找到交叉编译的工具包arm-linux-gcc-4.4.3.tar.gz,安装步骤: 1、解压交叉编译开发工具包 sudo tar xvzf arm-linux-gcc-4.4.3.tar.gz -C / 解压工具链到根目录,这里的解压目录可以任意指定。系统中会增加目录/opt/FriendlyARM/toolschain/4.4.3/ 2、修改环境变量,把交叉编译器的路径加入到PATH 采用修改/etc/bash.bashrc文件的方法(还可有别的方法) ①用vim打开文件: #sudo vim /etc/bash.b
[单片机]
S3C2440微控制器外部中断实验
实验目的:掌握S3C2440微控制器I/O和外部中断的使用方法 实验内容: 用外部中断的方式,实现: (1)按下K11,LED1闪烁2次; (2)按下K12,LED2闪烁2次; (3)按下K13,LED3闪烁2次; (4)按下K14,LED4闪烁2次; (5)按下K15,LED1与LED3一起闪烁2次; (6)按下K16,LED2与LED4一起闪烁2次; 要求使用下降沿触发外部中断。 #include 2440addr.h #include 2440lib.h #define rGPBCON (*(volatile unsigned *)0x56000010) //Port B control #define rGPBD
[单片机]
STM32学习笔记(7):USART串口的使用
1. 串口的基本概念 在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,也支持LIN(局部互联网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。还可以使用DMA方式,实现高速数据通信。 USART通过3个引脚与其他设备连接在一起,任何USART双向通信至少需要2个引脚:接受数据输入(RX)和发送数据输出(TX)。 RX: 接受数据串行输入
[单片机]
S3C2440内存组成
大家好,我们这回来讨论下S3C2440内存组成。 先来张图吧: 图1 S3C2440是32bit的单片机(请允许我叫它单片,因为我觉得它和普通的单片机真的没有本质的区别),那么按道理它的地址范围就是0~0xFFFFFFFF,也就是4GB的寻址空间。但是,实际上很多是保留的,0~0x3FFFFFFF的地址部分是分配给用户的NandFlash、NorFlash、SDRAM等存储器件,0x40000000开始有一部分是内部寄存器,绝大部分是保留不使用的。 NandFlash:我们可以把它当作PC机的硬盘。 NorFlash:也是FLASH,但是其读的速度很快。 SDRAM:中文全称是同步随机动态存储,类似于SRAM,但是造价啥的比较
[单片机]
STM32学习笔记---SysTick定时器
Q:什么是SYSTick定时器? SysTick 是一个24 位的倒计数定时器,当计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息。 Q:为什么要设置SysTick定时器? (1)产生操作系统的时钟节拍 SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统 心跳 的节律。 (2)便于不同处理器之间程序移植。 Cortex‐M3处
[单片机]
S3C2440 I2C总线控制
多主机IIC总线控制(IICCON): IIC控制总线状态(IICSTAT): IIC总线地址(IICADD): IIC发送,接收总线寄存器(IICDS) IIC总线控制寄存器: 源码如下: void Rd24C080(U32 slvAddr,U32 addr,U8 *data) { _iicMode = SETRDADDR; _iicPt = 0; _iicData = (U8)addr; _iicDataCount = 1; rIICDS = slvAddr; rIICSTAT = 0xf0; //MasTx,Start //Clearing the pendin
[单片机]
<font color='red'>S3C2440</font> I2C总线控制
基于嵌入式S3C2440的船舶导航系统设计
       文章在以ARMS3C2440为核的硬件平台上应用Linux操作系统开发了船舶导航系统,完成了系统的硬件设计、软件设计、交叉编译环境的建立、引导程序的移植、内核的移植、驱动程序的编写和根文件系统的建立等,实现了系统的功能。         1.前言        集计算机技术、通信技术、微电子技术等多种技术为一体的嵌入式技术进入到了一个飞速发展的阶段,嵌入式系统已被广泛应用到了航空、消费电子、信息家电、网络通信等各个领域。ARMS3C2440ARM是嵌入式处理器是性价比较优秀的芯片,在各个领域的开发应用有着广阔的应用前景。        船舶导航技术也应随着科学技术背景的改变不断地向着高性能稳定性的方面发展,潜
[嵌入式]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved