led灯定时防抖

void add_timer(struct timer_list * timer)
#include <linux/timer.h>

Installs the timer structures in the list timer in the timer list.

The timer_list structure is defined by:

struct timer_list {
        struct timer_list *next;
        struct timer_list *prev;
        unsigned long expires;
        unsigned long data;
        void (*function)(unsigned long);
};

In order to call add_timer(), you need to allocate a timer_list structure, and then call init_timer(), passing it a pointer to your timer_list. It will nullify the next and prevelements, which is the correct initialization. If necessary, you can allocate multiple timer_list structures, and link them into a list. Do make sure that you properly initialize all the unused pointers to NULL, or the timer code may get very confused.

For each struct in your list, you set three variables:

expires
The number of jiffies (100ths of a second in Linux/86; thousandths or so in Linux/Alpha) after which to time out.
function
Kernel-space function to run after timeout has occured.
data
Passed as the argument to  function when  function is called.

Having created this list, you give a pointer to the first (usually the only) element of the list as the argument to add_timer(). Having passed that pointer, keep a copy of the pointer handy, because you will need to use it to modify the elements of the list (to set a new timeout when you need a function called again, to change the function to be called, or to change the data that is passed to the function) and to delete the timer, if necessary.

I'm writing a kernel (3.4.5) led module that the code as follows:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/gpio.h> 
#include <asm/irq.h>
#include <linux/sched.h>   
#include <linux/irq.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/timer.h>

#define DRVNAME "s5pv210_gpio"
struct led_device_ops{
	struct cdev led_cdev;
	struct platform_device *pdev;
	dev_t devno;
	struct class *leddrv_class;
	struct device	*leddrv_class_dev;
	wait_queue_head_t my_waitqueue;
	struct timer_list timer;	/* statistic timer */
	int irq;
	unsigned long virt;
};
static struct led_device_ops led_info;
static int major = 0;		/* default to dynamic major */
static uint32_t last_timer; 

static volatile int press = 0; 
struct pin_desc{
//	unsigned int pin;
	unsigned char key_val;
};
static unsigned char witch_key = 0;
static struct pin_desc key_descs[8] ={ 
    [0] = { 
     //   .pin = S5PV210_GPH0(0), 
        .key_val = 0x01, 
    }, 
    [1] = { 
      //  .pin = S5PV210_GPH0(1), 
        .key_val = 0x02, 
    }, 
 
    [2] = { 
      //  .pin = S5PV210_GPH0(2), 
        .key_val = 0x03, 
    }, 
 
    [3] = { 
       // .pin = S5PV210_GPH0(3), 
        .key_val = 0x04, 
    }, 
 
    [4] = { 
      //  .pin = S5PV210_GPH0(4), 
        .key_val = 0x05, 
    }, 
 
    [5] = { 
      //  .pin = S5PV210_GPH0(5), 
        .key_val = 0x06, 
    }, 
 
    [6] = { 
       // .pin = S5PV210_GPH2(6), 
        .key_val = 0x07, 
    }, 
 
    [7] = { 
      //  .pin = S5PV210_GPH2(7), 
        .key_val = 0x08, 
    }, 
 };

volatile unsigned long *GPC0CON, *GPC0DAT;//用与存放两个个寄存器的地址
volatile unsigned long *GPH0CON, *GPH0DAT;//按键
static unsigned char KeyFlag;

static void all_leds_off(void);
static void led_config(void)
{
	volatile unsigned long  phys;//用于存放虚拟地址和物理地址
	phys = 0xE0200060; 
	led_info.virt =(unsigned long)ioremap(phys, 0xf00);
	GPC0CON = (unsigned long *)(led_info.virt + 0x00);//指定需要操作的三个寄存器的地址
	GPC0DAT = (unsigned long *)(led_info.virt + 0x04);
	GPH0CON = (unsigned long *)(led_info.virt + 0xc00-0x60); //keY1配置为输入
	GPH0DAT = (unsigned long *)(led_info.virt + 0xc00-0x60+0x04);
	*GPC0CON &= ~(0xFF << 12);
	*GPC0CON |= 0x11 << 12;			// 配置GPC0_3和GPC0_4为输出
	*GPH0CON &= ~0x0F; //配置为输入
	all_leds_off();
}

static void led1_on(void)
{
	*GPC0DAT |= 1 << 3;
	*GPC0DAT &= ~(0x01 << 4);
//	printk("led1 light\n");
}

static void led2_on(void)
{
	*GPC0DAT |= 1 << 4;
	*GPC0DAT &= ~(0x01 << 3);
//	printk("led2 light\n");
}

static void all_leds_on(void)
{
		*GPC0DAT |= 1 << 3;		// 点亮LED1
		*GPC0DAT |= 1 << 4;		// 点亮LED2
		printk("all leds light\n");
}
static void all_leds_off(void)
{

	*GPC0DAT &= ~(0x3 << 3);		// 熄灭LED1和LED2
	printk("all leds off\n");
}

static void led_timer_event(unsigned long arg)
{
	press = 1;
	wake_up_interruptible(&led_info.my_waitqueue); 
	KeyFlag = !KeyFlag;
	
	printk("Timer event time = %ld\n",jiffies-last_timer);
	
	
}


static irqreturn_t led_handler(int irq, void *devid)
{
	struct pin_desc * pindesc = (struct pin_desc *)devid;
	mod_timer(&led_info.timer,jiffies+HZ);  //这里设置定时中断程序多久之后执行,可适当变短些,
	last_timer=jiffies; 									 //这里为了使程序实验效果更加明显,直接用1s
	witch_key = pindesc->key_val;
	printk("KERNEL:irq == %d  witch_key= %d\n",irq,witch_key);
	return IRQ_RETVAL(IRQ_HANDLED); 
	
}

static ssize_t led_device_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	char val = 0;
	int ret = -1;
	ret = copy_from_user(&val, buf, count);
	if(ret)
	{
		printk("write ret= %x\n",ret);	
	}

	return count;
}

static ssize_t led_device_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int ret = -1;
	unsigned char from[1]={0};
	wait_event_interruptible(led_info.my_waitqueue,press);
	press = 0;
	if(KeyFlag)
	{
	   	led1_on();		
	}
	else
	{
		led2_on();
	}
	from[0] = witch_key;
	if(witch_key>0)
	{
		ret = copy_to_user(buf,from,sizeof(from));
		if(ret)
		{
			 printk("copy to user failed\n");	
		}
		witch_key = 0;
	}


	return 0;
}

static int led_device_open(struct inode *inode, struct file *file)
{
	printk("led_drv_open\n");
	init_waitqueue_head(&led_info.my_waitqueue);
	led_config();
	all_leds_off();
	return 0;
}


static int led_device_release(struct inode *inode, struct file *file)
{
	return 0;
}

static const struct file_operations led_device_fileops = {
	.owner   = THIS_MODULE,
	.write   = led_device_write,
	.read    = led_device_read,
	.open    = led_device_open,
	.release = led_device_release,

};

static int led_device_init(void)
{
	  int rc,i;
		//dev_t devid;
		
		led_info.pdev = platform_device_alloc(DRVNAME, 0);
		if (!led_info.pdev)
		    return -ENOMEM;
		
		rc = platform_device_add(led_info.pdev);
		if (rc)
		    goto undo_malloc;
		
		if (major) {
		    led_info.devno = MKDEV(major, 0);
		    rc = register_chrdev_region(led_info.devno, 1, "led_drv");
	  } else {
		    rc = alloc_chrdev_region(&led_info.devno, 0, 1, "led_drv");
		    major = MAJOR(led_info.devno);
		} 
		if (rc < 0) {
		    dev_err(&led_info.pdev->dev, "led_drv  chrdev_region err: %d\n", rc);
		    goto undo_platform_device_add;
		}   
		led_info.led_cdev.owner = THIS_MODULE;
		
		cdev_init(&led_info.led_cdev, &led_device_fileops);
		rc =cdev_add(&led_info.led_cdev, led_info.devno, 1);
  	if(rc){ 
      printk(KERN_ERR "add char device faild!\n"); 
      goto cdev_add_error; 
    } 
   	led_info.leddrv_class = class_create(THIS_MODULE, "led_class_drv");
		if(IS_ERR(led_info.leddrv_class)){ 
        printk(KERN_ERR "function class_create excute error!\n"); 
        goto led_class_error; 
    } 
		led_info.leddrv_class_dev = device_create(led_info.leddrv_class, NULL, led_info.devno, NULL, "led_drv"); /* /dev/xyz */
		if(IS_ERR(led_info.leddrv_class_dev)){ 
        printk(KERN_ERR "function device_create excute error!\n"); 
        goto led_device_error; 
    } 
  	for(i=0;i<5;i++)
  	{
		    rc = request_irq(IRQ_EINT(i), &led_handler, IRQ_TYPE_EDGE_FALLING,"led_irq",  &key_descs[i]);
		    if(rc)
		    {
				    printk(KERN_ERR"function request_irq(%d) excute error\n",i);
				    goto led_request_irq_error;	
		    }
		}
		init_timer(&led_info.timer);
		//led_info.timer.expires = jiffies + HZ/100;
		
		led_info.timer.function = led_timer_event;
		add_timer(&led_info.timer);
		return 0; /* succeed */  
	
	
		
led_request_irq_error:
	device_unregister(led_info.leddrv_class_dev);
led_device_error:
	class_destroy(led_info.leddrv_class); 
led_class_error:
	cdev_del(&led_info.led_cdev);	 
cdev_add_error:
	unregister_chrdev_region(led_info.devno,1); 
undo_platform_device_add:
	platform_device_del(led_info.pdev);
undo_malloc:
	platform_device_put(led_info.pdev);

	return rc;

	
}

static void led_device_exit(void)
{
	free_irq(IRQ_EINT(0), &key_descs[0]);
	free_irq(IRQ_EINT(1), &key_descs[1]);
	free_irq(IRQ_EINT(2), &key_descs[2]);
	free_irq(IRQ_EINT(3), &key_descs[3]);
	free_irq(IRQ_EINT(4), &key_descs[4]);
	printk("led_drv_exit ....excute ok\n");
	//all_leds_off();
	printk("led_drv_exit ....excute ok0\n");
	device_unregister(led_info.leddrv_class_dev);
	printk("led_drv_exit ....excute ok1\n");
	class_destroy(led_info.leddrv_class);
	printk("led_drv_exit ....excute ok2\n");
	cdev_del(&led_info.led_cdev); 
	printk("led_drv_exit ....excute ok3\n");
	unregister_chrdev_region(led_info.devno, 1); 
	printk("led_drv_exit ....excute ok4\n");
	platform_device_unregister(led_info.pdev);
	iounmap((void *)led_info.virt); //撤销映射关系
}

module_init(led_device_init);
module_exit(led_device_exit);

MODULE_AUTHOR("Ethyn blog:http://blog.csdn.net/humanspider1");
MODULE_DESCRIPTION("cortex a8 S5pv210 led test Driver");
MODULE_LICENSE("GPL");
测试程序和之前的一样,在此不附加


猜你喜欢

转载自blog.csdn.net/humanspider1/article/details/41011725