日期:2014-05-16  浏览次数:20657 次

自娱自乐10之Linux DMA使用2(DMA使用实例,用timer作DMA请求源实现流水灯)

我想大家听过流水灯的实现,有很多方法,有一种是用定时器实现的。

通常是利用定时器中断,今天我要用timer作为DMA请求源,当timer时间到启动DMA传输,这样把一个一个数送的gpio口。实现流水灯

下面是代码,我的流水灯只流一次,平台是s3c2440

/***********************************
 Copyright(C), 2013 LDP
 FileName:  tiemr.c
 Author:    wwxxxxll
 Date:          
 Description: linux-3.2-36
 History:       
 Author       Date            Desc
************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/interrupt.h>

#include <asm/io.h>
#include <asm/mach/time.h>

#include <mach/regs-gpio.h>
#include <mach/regs-gpioj.h>
#include <mach/hardware.h>
#include <mach/dma.h>

#include <plat/regs-timer.h>
#include <plat/gpio-fns.h>
#include <plat/dma-ops.h>



#define DEVICE_NAME			"timer1" 

#define DMA_TEST
//如果不定义DMA_TEST,就是一个timer1中断驱动

#ifdef DMA_TEST

#define SIZE 4

struct timer_data
{
    struct samsung_dma_ops * timer_dma_ops;
    struct samsung_dma_info info;
	struct samsung_dma_prep_info pre_info;
	dma_addr_t dma_addr;
    unsigned ch;
	void * buf;
};

static struct timer_data timer1_data;

static struct s3c2410_dma_client timer1_dma_client = 
{
	.name = "samsung-timer-dma",
};
#else

static irqreturn_t maichong_interrupt_1(int irq, void *dev_id)
{
    printk("interrupt\n");

    disable_irq_nosync(IRQ_TIMER1);

	return IRQ_RETVAL(IRQ_HANDLED);
}

#endif

static int timer1_open(struct inode *inode, struct file *file);
static int timer1_close(struct inode *inode, struct file *file);

static struct file_operations dev_fops = {
	.owner	=	THIS_MODULE,
	.open    =   timer1_open,
    .release =   timer1_close, 
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

#ifdef DMA_TEST
//dma中断时会调用这个函数,对这个函数来说就是数据传输完成
static inline void timer_fp(void *name)
{
	unsigned long tcon;
	printk(KERN_INFO "complete\n"); 

	tcon = __raw_readl(S3C2410_TCON);
    __raw_writel((tcon & (~(0xf << 8))) | (0xa << 8) ,S3C2410_TCON);//关timer,就流一次
}
#endif

static int timer1_open(struct inode *inode, struct file *file)
{
	unsigned long tcon;
	unsigned long tcfg1;
	unsigned long tcfg0;

	int ret = 0;

	printk(KERN_INFO "%s\n", __func__);

#ifdef DMA_TEST
	//设置为输出,根据自己的平台设置
	s3c2410_gpio_cfgpin(S3C2410_GPB(5), S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(S3C2410_GPB(6), S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(S3C2410_GPB(7), S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(S3C2410_GPB(8), S3C2410_GPIO_OUTPUT);
#endif

	tcon = __raw_readl(S3C2410_TCON);
	tcfg1 = __raw_readl(S3C2410_TCFG1);
	tcfg0 = __raw_readl(S3C2410_TCFG0);

	__raw_writel(tcfg0 | 0xff ,S3C2410_TCFG0 ); 

	//下面对照数据手册看吧
#ifdef DMA_TEST
	__raw_writel((tcfg1 & (~(0xf << 4)) ) | (3 << 4) | (2 << 20), S3C2410_TCFG1 );
#else
	__raw_writel((tcfg1 & (~(0xf << 4)) ) | (3 << 4), S3C2410_TCFG1 );
#endif
	__raw_writel((tcon & (~(0xf << 8)) ) | (0xa << 8) ,S3C2410_TCON );
	
	__raw_writel(8000, S3C2410_TCNTB(1));//我没有去计算多长时间,就是可以看到小灯一个一个量
	__raw_writel(1, S3C2410_TCMPB(1));

	__raw_writel((tcon & (~(0xf << 8)) ) | (0xa << 8) ,S3C2410_TCON ); 
	
#ifndef DMA_TEST
	if (request_irq(IRQ_TIMER1 , maichong_interrupt_1, IRQF_DISABLED, DEVICE_NAME, NULL))
	{
        printk(KERN_ERR "can't request_irq\n");
	}

#else
    timer1_data.buf = (void *)kzalloc(SIZE * sizeof(unsigned long), GFP_KERNEL);
	if (timer1_data.buf == NULL)
	{
		ret = -ENOMEM;
		goto mem_err;
	}

	//流水灯数据
	*((unsigned long *)timer1_data.buf) = 0xffffffdf;
	*((unsigned long *)((unsigned long *)timer1_data.buf + 1)) = 0xffffffbf;
	*((unsigned long *)((unsigned long *)timer1_data.buf + 2)) = 0xffffff7f;
	*((