博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第一个Linux驱动-流水灯【转】
阅读量:7137 次
发布时间:2019-06-28

本文共 6390 字,大约阅读时间需要 21 分钟。

转自:

水平有限,描述不当之处请指出,转载请注明出处            

       本节介绍如何利用板载的Led和Linux的内核定时器实现一个简单的流水灯的驱动,所使用的开发板是TQ2440,内核版本2.6.30.4。

        比较简单,也没涉及到什么机制,直接上了!关于定时器的使用模板可以参考<<Linux设备驱动开发详解>>.

    

驱动程序:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LED_MAJOR 244#define LED_ON 0#define LED_OFF 1#define LED1_PIN S3C2410_GPB5#define LED2_PIN S3C2410_GPB6#define LED3_PIN S3C2410_GPB7#define LED4_PIN S3C2410_GPB8 static unsigned long led_major = LED_MAJOR;struct led_dev { struct cdev cdev; struct timer_list s_timer; atomic_t led_no; //LED编号 atomic_t sec_counter; //秒计时数};struct led_dev *led_devp;void led_control(int led_no){ switch(led_no) { case 1:s3c2410_gpio_setpin(LED1_PIN,LED_ON); s3c2410_gpio_setpin(LED2_PIN,LED_OFF); s3c2410_gpio_setpin(LED3_PIN,LED_OFF); s3c2410_gpio_setpin(LED4_PIN,LED_OFF); break; case 2:s3c2410_gpio_setpin(LED1_PIN,LED_OFF); s3c2410_gpio_setpin(LED2_PIN,LED_ON); s3c2410_gpio_setpin(LED3_PIN,LED_OFF); s3c2410_gpio_setpin(LED4_PIN,LED_OFF); break; case 3:s3c2410_gpio_setpin(LED1_PIN,LED_OFF); s3c2410_gpio_setpin(LED2_PIN,LED_OFF); s3c2410_gpio_setpin(LED3_PIN,LED_ON); s3c2410_gpio_setpin(LED4_PIN,LED_OFF); break; case 4:s3c2410_gpio_setpin(LED1_PIN,LED_OFF); s3c2410_gpio_setpin(LED2_PIN,LED_OFF); s3c2410_gpio_setpin(LED3_PIN,LED_OFF); s3c2410_gpio_setpin(LED4_PIN,LED_ON); break; default:break; }}//定时器处理函数static void sec_timer_handler(unsigned long arg){ int num; mod_timer(&led_devp->s_timer,jiffies+HZ); num = atomic_read(&led_devp->led_no); if(num == 4) { atomic_set(&led_devp->led_no,1); } else { atomic_inc(&led_devp->led_no); } num = atomic_read(&led_devp->led_no); led_control(num); atomic_inc(&led_devp->sec_counter); num = atomic_read(&led_devp->sec_counter); printk(KERN_INFO "sec_count:%d\n",num); }static int led_open(struct inode *inode,struct file *filp){ struct timer_list *timer; timer = &led_devp->s_timer; init_timer(timer); timer->function = sec_timer_handler; timer->expires = jiffies+HZ; //计时频率为HZ add_timer(timer); atomic_set(&led_devp->sec_counter,0); atomic_set(&led_devp->led_no,0); return 0;}static int led_release(struct inode *inode, struct file *filp){ del_timer(&led_devp->s_timer); return 0; }static ssize_t led_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){ int count,led_no; int result; count = atomic_read(&led_devp->sec_counter); led_no = atomic_read(&led_devp->led_no); result = (count<<3)+led_no; if(put_user(result,(int*)buf)) { return -EFAULT; } else { return sizeof(int); }}static const struct file_operations led_fops ={ .owner = THIS_MODULE, .read = led_read, .open = led_open, .release = led_release,};static void led_setup_cdev(struct led_dev *dev, int index){ int err,devno = MKDEV(led_major,index); cdev_init(&dev->cdev,&led_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev,devno,1); if(err) { printk(KERN_NOTICE "Error %d adding %d\n",err,index); }}static int led_init(void){ int result; dev_t devno = MKDEV(led_major,0); //获取设备号 /*注册设备*/ if(led_major) result = register_chrdev_region(devno,1,"led"); else { result = alloc_chrdev_region(&devno,0,1,"led"); led_major = MAJOR(devno); } if(result<0) { printk("register failed!"); return result; } led_devp =(struct led_dev*)kmalloc(sizeof(struct led_dev),GFP_KERNEL); if(!led_devp) { result = -ENOMEM; unregister_chrdev_region(devno,1); } memset(led_devp, 0 ,sizeof(struct led_dev)); led_setup_cdev(led_devp,0); /*配置IO口*/ s3c2410_gpio_cfgpin(LED1_PIN,S3C2410_GPIO_OUTPUT); s3c2410_gpio_cfgpin(LED2_PIN,S3C2410_GPIO_OUTPUT); s3c2410_gpio_cfgpin(LED3_PIN,S3C2410_GPIO_OUTPUT); s3c2410_gpio_cfgpin(LED4_PIN,S3C2410_GPIO_OUTPUT); /*初始化IO电平*/ s3c2410_gpio_setpin(LED1_PIN,LED_OFF); s3c2410_gpio_setpin(LED2_PIN,LED_OFF); s3c2410_gpio_setpin(LED3_PIN,LED_OFF); s3c2410_gpio_setpin(LED4_PIN,LED_OFF); return 0;}static void led_exit(void){ cdev_del(&led_devp->cdev); kfree(led_devp); unregister_chrdev_region(MKDEV(led_major,0),1);}MODULE_LICENSE("GPL");MODULE_AUTHOR("Vanbreaker");module_init(led_init);module_exit(led_exit);

在该例程中,由于控制LED亮灭的部分放在了定时器处理函数中,因此led_read函数没有实际作用,这样的话应用程序就简单一些;另一种选择是将该控制部分放在应用程序中完成,不过还得添加一个iocntl函数,这是我之前的做法。

 

控制s3c2440的IO口可以使用内核中已经提供的操作函数,在arch\arm\plat-s3c24xx\Gpio.c中,需要包含头文件<asm/io.h>相关的IO口定义在arch\arm\s3c2410\include\mach\Regs-gpio.h中,需要包含头文件<mach/regs-gpio.h>

可以分析下s3c2410_gpio_setpin()这个函数

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to){	void __iomem *base = S3C24XX_GPIO_BASE(pin);//获取该pin所在的gpio组的虚拟地址	unsigned long offs = S3C2410_GPIO_OFFSET(pin);//计算该pin在gpio组内的偏移	unsigned long flags;	unsigned long dat;	local_irq_save(flags);	dat = __raw_readl(base + 0x04);//通过加上0x04定位到数据寄存器,将该组gpio的数据读取出来	dat &= ~(1 << offs);	   //相应位清零	dat |= to << offs;              //相应位置位	__raw_writel(dat, base + 0x04); //写回数据	local_irq_restore(flags);}

 

#define S3C24XX_GPIO_BASE(x)  S3C2410_GPIO_BASE(x)

 

#define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)

S3C2410_GPIO_BASE(pin)用来计算pin所在的GPIO组的虚拟地址,S3C24XX_VA_GPIO是IO的虚拟基址,我们分析一下 ((pin)&~31)>>1是什么意思。首先我们得了解传入的参数究竟是什么形式的,以S3C2410_GPA0的定义为例:

#define S3C2410_GPA0         S3C2410_GPIONO(S3C2410_GPIO_BANKA, 0)
#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))#define S3C2410_GPIO_BANKA   (32*0)#define S3C2410_GPIO_BANKB   (32*1)#define S3C2410_GPIO_BANKC   (32*2)#define S3C2410_GPIO_BANKD   (32*3)#define S3C2410_GPIO_BANKE   (32*4)#define S3C2410_GPIO_BANKF   (32*5)#define S3C2410_GPIO_BANKG   (32*6)#define S3C2410_GPIO_BANKH   (32*7)

可以看到bank都有32位,因此传入的pin参数实际就是离起始bank的位偏移,S3C2410_GPA0为0,S3C2410_GPB0为32.而每个GPIO组都有4个寄存器,一个GPIO组所占的内存大小就有4*32/8=32/2=32>>1=16个字节大小,由此可见 (pin)&~31是先将低位屏蔽,计算出pin所在的gpio组,再将结果右移一位就是计算字节的偏移,如GPA的偏移为0,GPB的偏移为0X10,GPC的偏移为0X20……最后将偏移加上IO的虚拟基址就得到了该组IO口的虚拟地址了。

 

计算组内偏移很简单:

#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)

 

应用测试程序:

#include
#include
#include
#include
#include
int main(){ int fd; fd = open("/dev/led_timer",O_RDWR); if(fd != -1) { printf("open /dev/led_timer\n"); while(1); } else { printf("cannot open /dev/led_timer!"); }}

将编译好的模块和应用程序移到开发板上进行加载和执行,即可以看到流水灯的效果

【作者】
【出处】
【博客园】
【新浪博客】
【知乎】
【我的作品---旋转倒立摆】
【我的作品---自平衡自动循迹车】
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
你可能感兴趣的文章
大数据波分传输工程方案设计主要细节
查看>>
Z字形扫描(201412-2)
查看>>
如何确定Windows Server 2012中虚拟机的动态内存可用大小
查看>>
P2327 [SCOI2005]扫雷
查看>>
Hibernate基础实例
查看>>
索引设计规范
查看>>
python笔记4:计算输入时间为当年的第几天
查看>>
Linux 常用命令集合
查看>>
linux rpm安装webmin
查看>>
DDOS***原理与防护
查看>>
懒加载 与 富文本编辑器 的 相爱相杀
查看>>
我的友情链接
查看>>
java转换是json需要的jar包导致的问题,
查看>>
mac安装brew和nginx
查看>>
技术博客
查看>>
关于ssh远程登录太慢的解决方法
查看>>
子类化QMainWindows
查看>>
Windows完成端口 IOCP模型(一)
查看>>
修改roo的密码 虚拟机vmware8.04 Centos 6.3
查看>>
大数据真的好找工作么?
查看>>