我想大家听过流水灯的实现,有很多方法,有一种是用定时器实现的。
通常是利用定时器中断,今天我要用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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
*((unsigned long *)((unsigned long *)timer1_data.buf + 3)) = 0xfffffeff;
timer1_data.pre_info.buf = dma_map_single(misc.this_device, timer1_data.buf, SIZE * sizeof(unsigned long), DMA_TO_DEVICE);
if (dma_mapping_error(misc.this_device, DMA_TO_DEVICE))
{
ret = -ENOMEM;
goto map_err;
}
timer1_data.pre_info.cap = DMA_SLAVE;
timer1_data.pre_info.direction = DMA_TO_DEVICE;
timer1_data.pre_info.fp = timer_fp;
timer1_data.pre_info.len = SIZE * timer1_data.info.width; //这个长度是字节数
timer1_data.timer_dma_ops->prepare(timer1_data.ch, &timer1_data.pre_info);
timer1_data.timer_dma_ops->trigger(timer1_data.ch);
#endif
__raw_writel( ( tcon & (~(0xf << 8)) ) | (0x9 << 8) ,S3C2410_TCON );
#ifdef DMA_TEST
return ret;
mem_err:
dma_unmap_single(misc.this_device, timer1_data.pre_info.buf, SIZE * sizeof(unsigned long), DMA_TO_DEVICE);
kfree(timer1_data.buf);
map_err:
#endif
return ret;
}
static int timer1_close(struct inode *inode, struct file *file)
{
printk(KERN_INFO "%s\n", __func__);
#ifndef DMA_TEST
free_irq(IRQ_TIMER1, NULL);
#else
timer1_data.timer_dma_ops->stop(timer1_data.ch);
dma_unmap_single(misc.this_device, timer1_data.pre_info.buf, SIZE * sizeof(unsigned long), DMA_TO_DEVICE);
kfree(timer1_data.buf);
#endif
return 0;
}
static int __init dev_init(void)
{
int ret;
#ifdef DMA_TEST
timer1_data.timer_dma_ops = samsung_dma_get_ops();
timer1_data.info.cap = DMA_SLAVE;
timer1_data.info.direction = DMA_TO_DEVICE;
timer1_data.info.client = &timer1_dma_client;
timer1_data.info.fifo = 0x56000014;//这是GPIO B口数据寄存器地址
timer1_data.info.width = 4;//数据宽度
timer1_data.ch = timer1_data.timer_dma_ops->request(DMACH_TIMER, &timer1_data.info);
if (timer1_data.ch == 0)
{
ret = -1;
goto req_err;
}
timer1_data.timer_dma_ops->stop(timer1_data.ch);
#endif
ret = misc_register(&misc);
if (ret < 0)
{
goto reg_err;
}
return ret;
reg_err:
#ifdef DMA_TEST
timer1_data.timer_dma_ops->release(DMACH_TIMER, timer1_data.info.client);
req_err:
#endif
return ret;
}
static void __exit dev_exit(void)
{
#ifdef DMA_TEST
timer1_data.timer_dma_ops->release(DMACH_TIMER, timer1_data.info.client);
#endif
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wwxxxxll");
测试程序
#include
#include
int main(void)
{
int fd;
fd = open("/dev/timer1", O_RDWR);
if (fd < 0)
{
return -1;
}
getchar();
close(fd);
return 0;
}
不好意思我不能把小灯的现象呈现给大家
后记:
如果你看过前面的udc驱动,你会看到有cache的应用,cache也是我要搞的
不过接下来我会先考虑在udc中加上DMA。这可能是我下期的内容。再见!