zoukankan      html  css  js  c++  java
  • 第一个Linux驱动-流水灯【转】

    转自:http://www.xuebuyuan.com/1856562.html

    水平有限,描述不当之处请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7711695            

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

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

        

    驱动程序:

    #include<linux/module.h>
    #include<linux/init.h>
    #include<linux/types.h>
    #include<linux/fs.h>
    #include<linux/mm.h>
    #include<linux/cdev.h>
    #include<linux/slab.h>
    #include<linux/timer.h>
    #include<linux/jiffies.h>
    #include<asm/io.h>
    #include<asm/uaccess.h>
    #include<mach/regs-gpio.h>
    
    
    #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
    ",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
    ",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口可以使用内核中已经提供的操作函数,在archarmplat-s3c24xxGpio.c中,需要包含头文件<asm/io.h>相关的IO口定义在archarms3c2410includemachRegs-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<stdio.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<string.h>
    #include<stdlib.h>
    
    
    
    
    int main()
    {
    	int fd;
    
    	fd = open("/dev/led_timer",O_RDWR);
    
    	if(fd != -1)
    	{
    		printf("open /dev/led_timer
    ");
    		while(1);
    	}
    	else
    	{
    		printf("cannot open /dev/led_timer!");
    	}
    }

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

  • 相关阅读:
    基于centos的freeradius高可用lvs(UDP)
    sql server 2012的AlwaysOn高可用
    python基础题型一
    用户访一个APP或者网页流程示意图
    DNS解析流程
    crontab的定时任务实例
    Xcode设置
    Nvidia Nsight + .NET
    C++ Pointer-to-Member Selector
    C++11
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5198911.html
Copyright © 2011-2022 走看看