3.字符设备驱动——led驱动和按键驱动和定时器

时间:2024-04-09 15:55:17

3.字符设备驱动——led驱动和按键驱动和定时器

 

APP通过一个函数打开文件进行驱动程序,它的属性就是:属于字符设备,有主设备号。应用程序进去c库进入内核,内核最后调用驱动,驱动里有led_open,led_read,write......。VFS系统怎么通过APP的函数找到驱动呢。它是字符设备,那就是它在字符设备的数组里面chrdev找到一项,结构,这个结构是驱动程序我们自己来实现,来写的。步骤1:先写程序,2:定义结构体。这个结构体包含驱动程序3:在入口函数里面用register_chrdev注册驱动,挂载到设备号上,从而被访问3.字符设备驱动——led驱动和按键驱动和定时器

有入口函数,也就有出口函数。注册驱动后,要卸载就要出口函数。

 

一。点灯驱动

   参考文件first_drv.c和firstdrvtest.c。

    个人理解:先在main函数里写fd = open("/dev/xyz", O_RDWR);  先构造一个file_operations结构体

static struct file_operations first_drv_fops = {

    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open   =   first_drv_open,     

    .write    =    first_drv_write,       

};

自动跳到first_drv_open函数执行灯配置

    之后main函数中执行write(fd, &val, 4);,跳到first_drv_write函数执行点灯操作。

之前,代码已写好注册驱动及卸载驱动

    int major;

static int first_drv_init(void)

{

    major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

    firstdrv_class = class_create(THIS_MODULE, "firstdrv");

    firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);        //ioremap函数用来设置虚拟地址

    gpfdat = gpfcon + 1;

    return 0;

}

static void first_drv_exit(void)

{

    unregister_chrdev(major, "first_drv"); // 卸载

    class_device_unregister(firstdrv_class_dev);

    class_destroy(firstdrv_class);

    iounmap(gpfcon);

}

module_init(first_drv_init);

module_exit(first_drv_exit);

MODULE_LICENSE("GPL");

然后,写Makefile文件把.c文件生成.o.文件,在串口软件中执行,观察现象 

代码:

在我的git上https://github.com/China-wzs/Linux-driver/blob/master/1.led%E9%A9%B1%E5%8A%A8.rar

二.按键驱动

用中断,linux的中断异常处理和单片机的异常处理差不多,都是

 

3.字符设备驱动——led驱动和按键驱动和定时器

    图二中中断框架中的1-5部系统都帮我们做好,我们要用什么中断操作,就是在执行语句action->handler中修改我们自己需要的功能,就在函数request_irq中定义

3.字符设备驱动——led驱动和按键驱动和定时器

request_irq函数会从中断链表放入新成员(自定义中断)配置中断号,设置引脚,使能中断

free_irq函数会出链(把中断out掉),禁止中断

例如:request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);

然后通过判断是否有按键按下唤醒或者进入休眠,减少cpu占据比

    ev_press = 1;                  /* 表示中断发生了 */

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

 

    /* 如果没有按键动作, 休眠 */

    wait_event_interruptible(button_waitq, ev_press);

github代码https://github.com/China-wzs/Linux-driver/blob/master/1.led%E9%A9%B1%E5%8A%A8.rar

三.按键驱动+定时器实现按键防抖

今天学习了定时器实现按键的防抖,因为硬件的关系,按键在按下的时候,会有抖动的现象,可能会导致多次触发上升沿或者下降沿中断。采用定时器,规定每方10ms后才可触发中断,这就防止了抖动触发中断。

 

代码实现如下:

        init_timer(&buttons_timer);

        buttons_timer.function = buttons_timer_function;

        add_timer(&buttons_timer);

        

  由add_timer()推出前面两个函数,知道了定时器到后执行buttons_timer_function函数,之前在buttons_irq函数实现的放在这个函数用

  static void buttons_timer_function(unsigned long data)

    {

    struct pin_desc * pindesc = irq_pd;

    unsigned int pinval;

    if (!pindesc)

        return;

    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)

    {

        /* 松开 */

        key_val = 0x80 | pindesc->key_val;

    }

    else

    {

        /* 按下 */

        key_val = pindesc->key_val;

    }

    ev_press = 1;                  /* 表示中断发生了 */

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

    kill_fasync (&button_async, SIGIO, POLL_IN);

}

 

反过来,buttons_irq函数里配置和启动定时器

 static irqreturn_t buttons_irq(int irq, void *dev_id)

{

    /* 10ms后启动定时器 */

    irq_pd = (struct pin_desc *)dev_id;

    mod_timer(&buttons_timer, jiffies+HZ/100);

    return IRQ_RETVAL(IRQ_HANDLED);

}