Linux驱动之一个简单的输入子系统程序编写

发布时间:2024-08-20  

在Linux驱动之输入子系统简析已经分析过了输入子系统的构成,它是由设备层、核心层、事件层共同组成的。其中核心层提供一些设备层与事件层公用的函数,比如说注册函数、反注册函数、事件到来的处理函数等等;事件层其实在Linux内核里面已经帮我们写好了很多有关的事件;而设备层就跟我们新添加到输入系统的具体设备相关了。这里以JZ2440开发板上的4个按键作为输入子系统的按键:它定义的功能分别为:KEY_L、KEY_S、KEY_ENTER、KEY_LEFTSHIFT。这几个值是在includelinuxinput.h中被定义的。接下来就是编写程序:


直接贴出源程序:


#include

#include

#include

#include

#include         //含有iomap函数iounmap函数

#include //含有copy_from_user函数

#include //含有类相关的处理函数

#include //含有S3C2410_GPF0等相关的

#include     //含有IRQ_HANDLEDIRQ_TYPE_EDGE_RISING

#include    //含有IRQT_BOTHEDGE触发类型

#include //含有request_irq、free_irq函数

#include

#include   //含有各种错误返回值

#include                     //含有输入子系统相关的类型

//#include


struct pin_desc 

{

    char * name;          //名称

    unsigned int pin;     //管脚定义

    unsigned int irq;     //中断号

    unsigned int key_val; //按键值

};


static struct pin_desc  pins_desc[4] = //初始化四个按键

{

    {'S2',S3C2410_GPF0,IRQ_EINT0,KEY_L},

    {'S3',S3C2410_GPF2,IRQ_EINT2,KEY_S},

    {'S4',S3C2410_GPG3,IRQ_EINT11,KEY_ENTER},

    {'S5',S3C2410_GPG11,IRQ_EINT19,KEY_LEFTSHIFT}

};


static struct pin_desc *pin_des=NULL;



static struct timer_list inputbuttons_timer;//新建一个定时器


static struct input_dev *buttons_input;     //新建一个输入子系统的设备层结构


/*

 *利用dev_id的值为pins_desc来判断是哪一个按键被按下或松开

 *中断处理程序主要是将发生中断的按键记录下来,然后修改定时器的定时时间为10ms

 */

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

    pin_des = (struct pin_desc *)dev_id;            //取得哪个按键被按下的状态

    mod_timer(&inputbuttons_timer, jiffies+HZ/100);//10ms之后调用定时器处理函数

    

    return IRQ_HANDLED;

}


/*

 *定时器的处理程序,主要根据按下的按键,通过input_event函数上传事件

 */

static void inputbuttons_timer_timeout(unsigned long a)

{

    unsigned int pin_val;


    if(pin_des==NULL)

        return;

    else

    {

        

        pin_val = s3c2410_gpio_getpin(pin_des->pin);


        /*0松开,1按下*/

        if(pin_val) //按键松开

        {

                   input_event(buttons_input,EV_KEY, pin_des->key_val, 0);

                //input_sync(buttons_input);//上传同步事件,似乎没什么作用

        }

        else

        {

                  input_event(buttons_input,EV_KEY, pin_des->key_val, 1);

                //input_sync(buttons_input);//上传同步事件

        }

    }

}


/*

 *模块入口函数

  1、分配buttons_input结构体并初始化

  2、注册buttons_input结构,一旦注册会产生一个/dev/event1设备节点文件

  3、初始化一个定时器

  4、申请4个中断

 */

static int seven_drv_init(void)

{

    unsigned char i;

    int ret;

    /*1、分配一个buttons_input结构体*/

    buttons_input = input_allocate_device();

    if (!buttons_input)

        return -ENOMEM;


    /*2、设置输入事件类型*/

    set_bit(EV_KEY, buttons_input->evbit);

    set_bit(EV_REP, buttons_input->evbit);//重复事件类型

    

    /*3、输入事件类型的哪一种按键*/

    set_bit(KEY_L, buttons_input->keybit);

    set_bit(KEY_S, buttons_input->keybit);

    set_bit(KEY_ENTER, buttons_input->keybit);

    set_bit(KEY_LEFTSHIFT, buttons_input->keybit);

    

    /*4、注册它*/

    input_register_device(buttons_input);//注册设备驱动


    /*5、硬件相关操作*/

    /*增加一个定时器用于处理按键抖动*/

    init_timer(&inputbuttons_timer);

    inputbuttons_timer.expires = 0;

//    buttons_timer->data = (unsigned long) cs;

    inputbuttons_timer.function = inputbuttons_timer_timeout;

    add_timer(&inputbuttons_timer);

    

    /*申请中断*/

    for(i=0;i<4;i++)

    {

        ret = request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, (void * )&pins_desc[i]);

        if(ret)

        {

            printk('open failed %dn',i);

            return -(i+1);

        }

    }    

    

    return 0;

}



/*

 *模块出口函数

  1、反注册buttons_input

  2、释放buttons_input结构所占内存

  3、删除定时器

  4、释放4个中断申请

 */

static void seven_drv_exit(void)

{

    unsigned char i;


    input_unregister_device(buttons_input);

    input_free_device(buttons_input);

    del_timer(&inputbuttons_timer);


    for(i=0;i<4;i++)

    {

        free_irq(pins_desc[i].irq, (void * )&pins_desc[i]);

    }    

}


module_init(seven_drv_init);

module_exit(seven_drv_exit);


MODULE_LICENSE('GPL');


a、先看seven_drv_init函数,因为它负责对设备层进行初始,并且注册它,将它与事件层联系起来。看到这个函数:


1、分配一个buttons_input结构体


/*1、分配一个buttons_input结构体*/

    buttons_input = input_allocate_device();

    if (!buttons_input)

        return -ENOMEM;

2、设置输入事件类型


set_bit(EV_KEY, buttons_input->evbit);

set_bit(EV_REP, buttons_input->evbit);//重复事件类型

事件类型位于includelinuxinput.h中


/*

 * Event types

 */

#define EV_SYN            0x00//同步事件

#define EV_KEY            0x01//按键事件

#define EV_REL            0x02//位移事件

#define EV_ABS            0x03//绝对位移事件

#define EV_MSC            0x04

#define EV_SW            0x05

#define EV_LED            0x11

#define EV_SND            0x12

#define EV_REP            0x14

#define EV_FF            0x15

#define EV_PWR            0x16

#define EV_FF_STATUS        0x17

#define EV_MAX            0x1f


3、设置输入事件类型的哪一种按键


/*3、输入事件类型的哪一种按键*/

    set_bit(KEY_L, buttons_input->keybit);

    set_bit(KEY_S, buttons_input->keybit);

    set_bit(KEY_ENTER, buttons_input->keybit);

    set_bit(KEY_LEFTSHIFT, buttons_input->keybit);

按键码同样定义在includelinuxinput.h中,截取其中一小部分:


#define KEY_ENTER        28//enter的按键码

#define KEY_S            31//S的按键码

#define KEY_L            38//L的按键码

#define KEY_LEFTSHIFT    42//leftshift的按键码

4、注册它buttons_input结构体


注册的功能其实就是将当前的设备与事件层的结构进行匹配,这里会匹配到evdev_handler,它位于driversinputevdev.c,匹配后会产生/dev/event1设备节点文件。


/*4、注册它*/

input_register_device(buttons_input);//注册设备驱动

 

b、接着看到inputbuttons_timer_timeout函数,最终的按键的按键值是通过它上传的。


/*

 *定时器的处理程序,主要根据按下的按键,通过input_event函数上传事件

 */

static void inputbuttons_timer_timeout(unsigned long a)

{

    unsigned int pin_val;


    if(pin_des==NULL)

        return;

    else

    {

        

        pin_val = s3c2410_gpio_getpin(pin_des->pin);


        /*0松开,1按下*/

        if(pin_val) //按键松开

        {

                   input_event(buttons_input,EV_KEY, pin_des->key_val, 0);

                //input_sync(buttons_input);//上传同步事件,似乎没什么作用

        }

        else

        {

                  input_event(buttons_input,EV_KEY, pin_des->key_val, 1);

                //input_sync(buttons_input);//上传同步事件

        }

    }

}


c、接着编写测试程序,源码如下:


#include

#include

#include

#include

#include

#include


static int fd;


int main(int argc, char **argv)

{

    char* filename='/dev/event1';

   int oflags,ret=0;

   unsigned char key_val[16];

    

    fd = open(filename, O_RDWR);//|O_NONBLOCK);//打开dev/firstdrv设备文件,阻塞方式打开

    if (fd < 0)//小于0说明没有成功

    {

        printf('error, can't open %sn', filename);

        return 0;

    }

    

    if(argc !=1)

    {

        printf('Usage : %s ',argv[0]);

     return 0;

    }

    

  while(1)

  {

       ret = read(fd, key_val, 16);//读取的个数必须大于16字节

       printf('ret = %d,code: %02d value:%dn',ret,key_val[10],key_val[12]);

  }

    

   return 0;

}


可以看到这个测试程序是以阻塞方式打开的。read函数读的个数必须大于16个字节,看到driversinputevdev.c下的evdev_read函数,最后是它是将event这个结构发送给应用程序的。


static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

{

    struct evdev_client *client = file->private_data;

    struct evdev *evdev = client->evdev;

    int retval;


    if (count < evdev_event_size())

        return -EINVAL;


    if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))//如果是非阻塞方式打开的文件,并且现在缓存中不存在数据,直接返回

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    进入上面的计时器的时候,可以看出,并不是在那里死循环,只是单纯的加减一下寄存器就退出了,整个过程耗时极其短,看代码不同吧,5us到 20us左右吧,对主程序的执行没有什么影响。 下面看看具体怎么调用 最开......
     DELAY ;调用延时子程序   SETB  P1.0 ;P1.0输出高电平,使LED1熄灭   CLR   P1.1 ;P1.1输出低电平,使LED2点亮   ACALL DELAY ;调用延时子程序......
    开始地址 START:MOV SP,#60H ;设置堆栈起始地址为60H CLR P1.0 ;P1.0输出低电平,使LED1点亮 ACALL DELAY ;调用延时子程序 SETB P1.0 ;P1.0输出......
    将结果打印出来。 其中,__str__在 fire中用来完成自定义序列化。如果不提供这个方法,在链式调用完成后将会打印帮助内容。 比如,我们可以这么调用: $ python......
    高电平 LCALL DELAY;这一行是调用延时子程序 ;目的是让P1.0保持高电平 ;多停留一段时间 CLR P1.0;让P1.0输出低电平 LCALL DELAY;这一行也是调用延时子程序 ;和刚才一样,让......
    三菱PLC伺服定位编程案例;如图:螺距5mm.电机分辨率1000.减速比为5. 要求定位100mm,然后返回原点。程序怎么写? 分析:定位100mm.需要发多少脉冲? 定位距离=脉冲数*移动......
    三菱plc 伺服定位编程实例;如图:螺距5mm.电机分辨率1000.减速比为5. 要求定位100mm,然后返回原点。程序怎么写? 分析:定位100mm.需要发多少脉冲? 定位距离=脉冲数*移动......
    参。 一个是函数指针注册函数hal_KeyScanCBSRegister。 如果我们需要给别人用的话,不要学我啥注释没有,我们是在视频教程里有讲每个函数的作用。 一定要有点注释,让别人知道怎么调用,有什......
    == '__main__': fire.Fire() 然后我们就可以在命令行中这么调用: $ python example.py add 10 20 30 $ python......
    针 P 标记的步跳转 FEND结束主程序 SRET子程序返回 执行标记的子程序子程序执行完毕后,执行SERT指令,返回到CALL调用指令的下一步。 如果不执行CALL指令,在工作中是不接通的。即使待用程序......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>