2416开发记录十一:按键驱动(platform/中断)

发布者:心灵的旅程最新更新时间:2019-01-17 来源: eefocus关键字:按键驱动  platform  中断 手机看文章 扫描二维码
随时随地手机看文章

在前面几章的基础上编写了一个按键中断的驱动,并验证成功。 


这里用到了字符设备驱动,platform驱动,并有资源的获取,算是比较全面的platform驱动了。


首先是设备模块代码


//my2416PlatformKeyDev.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

/* 参考arch/arm/plat-s3c24xx/devs.c */



/*1. 根据芯片手册来获取资源*/

static struct resource key_resource[] = {

 /*EINT0*/

    [0]=

    {

        .flags = IORESOURCE_IRQ,//flags可以为IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_DMA等如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。

        .start = IRQ_EINT0,//资源的起始地址,结束地址

        .end = IRQ_EINT0,

        .name = "S3C24XX_EINT0",

    },

    /*EINT1*/

    [1]=

    {

        .flags = IORESOURCE_IRQ,

        .start = IRQ_EINT1,

        .end = IRQ_EINT1,

        .name = "S3C24xx_EINT1",

    },

    /*EINT2*/

    [2]=

    {

        .flags = IORESOURCE_IRQ,

        .start = IRQ_EINT2,

        .end = IRQ_EINT2,

        .name = "S3C24xx_EINT2",

    },

    /*EINT3*/

    [3]=

    {

        .flags = IORESOURCE_IRQ,

        .start = IRQ_EINT3,

        .end = IRQ_EINT3,

        .name = "S3C24xx_EINT3",

    },

    [4]=

    {

        .flags = IORESOURCE_IRQ,

        .start = IRQ_EINT4,

        .end = IRQ_EINT4,

        .name = "S3C24xx_EINT4",

    },

};


void key_release(struct device *dev)


}


/*1.构建平台设备结构体,将平台资源加入进来,需要注意的是platform_device 实质上是经过处理过的设备,在platform_device结构体中存在一个设备结构体,与之前的设备存在差别的是引入了设备资源。这些设备资源就能实现对设备寄存器,中断等资源的访问。*/

struct platform_device key_device = {

 .name    = "myplatformkey", /* 设备名,使用名为"myplatformkey"的平台驱动 ,注册后,会在/sys/device/platform目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。*/

 .id    = -1,/*设备id,一般为-1,如果是-1,表示同样名字的设备只有一个举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1  如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。 */

 .dev = {//结构体中内嵌的device结构体。

  .release = key_release,

 },

 .num_resources   = ARRAY_SIZE(key_resource),/* 设备所使用各类资源数量 */

 .resource   = key_resource,//定义平台设备的资源

};


/*2。把我们的设备资源挂在到虚拟总线的设备连表中去,

如果没有定义上面的struct platform_device led_device,那么需要下面的init函数*/

int key_dev_init(void)

{

 platform_device_register(&key_device); //platform设备的初注册  

 return 0;

}

/*如果没有定义上面的struct platform_device led_device,那么需要使用platform_device_alloc()函数分配一个platform_device结构体,然后使用platform_device_add_resources函数添加资源,最后使用platform_device_add函数



struct platform_device *my_buttons_dev;  


static int __init platform_dev_init(void)  

{  

    int ret;  


    my_buttons_dev = platform_device_alloc("my_buttons", -1);     

    platform_device_add_resources(my_buttons_dev,key_resource,6);//添加资源  

    ret = platform_device_add(my_buttons_dev); //platform设备的注册  

    if(ret)  

        platform_device_put(my_buttons_dev);  

    return ret;  

}   

*/


void key_dev_exit(void)

{

 platform_device_unregister(&key_device);

}


module_init(key_dev_init);

module_exit(key_dev_exit);


MODULE_AUTHOR("Zhao Yidong

MODULE_LICENSE("Dual BSD/GPL");

MODULE_DESCRIPTION("A sample key module dev int");

MODULE_ALIAS("key module dev int");


对应的makefile和以前的差不多,就不写出来了。


驱动模块代码

//my2416PlatformKeyDriver.c

#include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

#include

#include

 #include

 #include

 #include

#include


#define DEVICE_NAME "mykeys"

#define DRIVER_NAME "my2416keys"//加载驱动之后会在/dev/目录下发现my2416keys,应用程序可以使用


#define MYKEY_SIZE 0x1000//全局内存最大4K

#define MEM_CLEAR 0x1//清零全局内存

#define MYKEY_MAJOR 251 //预设的mykeys的主设备号


#define NUM_RESOURCE    5//资源数


static int mykey_major = MYKEY_MAJOR;


struct class *mykey_class;

static struct device *mykeyDevice=NULL;


 /*中断结构体定义*/

struct irqs

{

    int pirqs[NUM_RESOURCE];

    char *names[NUM_RESOURCE];

}irqs;

struct mykey_dev

{

   struct cdev cdev;//cdev结构体

   //unsigned char mem[MYKEY_SIZE];//全局内存

};

struct mykey_dev *mykey_devp;//设备结构体指针


//定义并初始化等待队列头

static DECLARE_WAIT_QUEUE_HEAD(key_waitq);  



static volatile int ev_press = 0;   

static volatile int key_values;  



 //定义GPIO管脚

 static unsigned long key_table [] =

  {

      S3C2410_GPF(0),  

      S3C2410_GPF(1),

      S3C2410_GPF(2),

      S3C2410_GPF(3),

      S3C2410_GPF(4),

 };

 //设置管脚模式

 static unsigned int key_cfg_table [] =

  {

      S3C2410_GPF0_EINT0, //随内核版本中定义类型的变化,在arch/arm/mach-sc2410/include/mach/Regs-gpio.h文件中定义

      S3C2410_GPF1_EINT1,

      S3C2410_GPF2_EINT2,

      S3C2410_GPF3_EINT3,

      S3C2410_GPF4_EINT4,

 };

/*

 static int my2416_keys_ioctl(struct file* filp, unsigned int cmd,unsigned long arg)

 {

   switch(cmd)

   {

      case KEY_ON:

         s3c2410_gpio_setpin(S3C2410_GPB(1), KEY_ON);

         break;

      case KEY_OFF:

         //s3c2410_gpio_setpin(key_table[arg], !cmd);

         s3c2410_gpio_setpin(S3C2410_GPB(1), KEY_OFF);

         break;

      default:

         printk("KEY control:no cmd\n");

         printk("KEY control are KEY_ON or KEY_OFF\n");

         return(-EINVAL);

   }

   return 0;

 }*/


static irqreturn_t keys_interrupt(int irq, void *dev_id)  

{  

    int i;  

    for(i=0; i

        if(irq == irqs.pirqs[i]){  

            key_values = i;  

            ev_press = 1;  

            wake_up_interruptible(&key_waitq);     

        }  

    }  


    return IRQ_RETVAL(IRQ_HANDLED);  

}   

//打开函数

static int my2416_key_open(struct inode *inode, struct file *file)  

{   

   int i=0;

   int err = 0;  


   ////这里只定义了一个io口GPB1配置GPIO

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

   {

       s3c2410_gpio_cfgpin(key_table[i], key_cfg_table[i]);

       //s3c2410_gpio_pullup(key_table[i], 1);//拉高引脚 根据原理图 按下为低电平

   }

   //申请中断

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

   {  //                                                 |上升沿下降沿触发

      err = request_irq(irqs.pirqs[i], keys_interrupt, IRQ_TYPE_EDGE_BOTH ,   

                          irqs.names[i], NULL);  

     if (err)  

        break;  

   }  


   if (err) 

   {  

      i--;  

      for (; i >= 0; i--) 

      {  

         if (irqs.pirqs[i] < 0) 

         {  

            continue;  

         }  

      disable_irq(irqs.pirqs[i]);  

            free_irq(irqs.pirqs[i], NULL);  

      }  

      return -EBUSY;  

   }  


   return 0;    

static int my2416_key_close(struct inode *inode, struct file *file)  

{  

   int i;  


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

   {  

      free_irq(irqs.pirqs[i], NULL);  

   }  


    return 0;  

}   

static int my2416_key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)  

{  

    unsigned long err;  


    if (!ev_press) {  

    if (filp->f_flags & O_NONBLOCK)  

        return -EAGAIN;  

    else  

        //添加等待队列,条件是ev_press

        wait_event_interruptible(key_waitq, ev_press);  

    }  


    ev_press = 0;  


    err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count));  


    return err ? -EFAULT : min(sizeof(key_values), count);  

}   

static unsigned int my2416_key_poll( struct file *file, struct poll_table_struct *wait)  

{  

    unsigned int mask = 0;  

    poll_wait(file, &key_waitq, wait);  

    if (ev_press)  

        mask |= POLLIN | POLLRDNORM;//POLLIN|POLLRDNORM表示有数据可读

    return mask;  

}  


 //dev_fops操作指令集

 static struct file_operations my2416Led_fops =

 {

   .owner =THIS_MODULE, 

   .open    =   my2416_key_open,  

   .release =   my2416_key_close,  

   .read    =   my2416_key_read,  

   .poll    =   my2416_key_poll,  

   //.unlocked_ioctl = my2416_keys_ioctl,//这里必须是unlocked_ioctl而不是ioctl。

 };

 /*//第三步:混杂设备定义

 static struct miscdevice my2416Ledmisc =

  {

      .minor = MISC_DYNAMIC_MINOR,

      .name = DEVICE_NAME,//加载驱动之后会在/dev/目录下发现mykeys,应用程序可以使用

      .fops = &my2416Led_fops,

 };

*/


static void Key_setup_cdev(struct mykey_dev *dev,int index)

{

    int err, devno = MKDEV(mykey_major,index);

    /*初始化cdev,并将相关的文件操作添加进来*/

    cdev_init(&dev->cdev, &my2416Led_fops);

    dev->cdev.owner = THIS_MODULE;

    //dev->cdev.ops   = &my2416Led_fops;

    /*注册字符设备*/

    err = cdev_add(&dev->cdev, devno, 1);


    if (err)

        printk("Error %d\n", err);

    else

        printk("have finish add\n");

}

/*3。实现probe函数*/

static int key_probe(struct platform_device *pdev)

{

   int result;

   unsigned char i=0;

   struct resource * irq_resource;


   printk("key_probe\n");

   /*创建一个设备号*/

   dev_t devno=MKDEV(mykey_major,0);


   /*注册一个设备号*/

   /*如果定义了主设备号采用静态申请的方式*/

   if(mykey_major)

   {

      result=register_chrdev_region(devno,1,DEVICE_NAME);

   }

   else//动态申请设备号

   {

      result= alloc_chrdev_region(&devno,0,1,DEVICE_NAME);

      mykey_major=MAJOR(devno);

   }

   if(result<0)

   {

      printk (DEVICE_NAME " can't register\n");  

      return result;

   }


   printk("key devno\n");



   //动态申请设备结构体内存

   mykey_devp=kmalloc(sizeof(struct mykey_dev), GFP_KERNEL);

   if(!mykey_devp)//申请失败

   {

      printk("kmalloc faile\n");

      result=-ENOMEM;

      goto fail_malloc;

   }

   printk("kmalloc succeed\n");

  /*清除空间*/

   memset(mykey_devp,0,sizeof(struct mykey_dev));


   /*创建一个设备*/

   Key_setup_cdev(mykey_devp,0);


   //class_create和device_create函数是为了自动在/dev下创建DRIVER_NAME设备文件。

   //创建一个类,这个类存放于sysfs下面

   mykey_class=class_create(THIS_MODULE,DRIVER_NAME);

   if(IS_ERR(mykey_class))

   {

    result = PTR_ERR(mykey_class);

    printk("class create faikey\n");

    goto class_create_fail;

   }

   //在/dev目录下创建相应的设备节点

   mykeyDevice = device_create(mykey_class,NULL,devno,NULL,DRIVER_NAME);


   if(IS_ERR(mykeyDevice))

   {

    result = PTR_ERR(mykeyDevice);

    printk("device_create faile\n");

    goto device_create_faile;

   }


   //获得资源

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

   {

      /*获得设备的资源*/

      irq_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);


      if(NULL == irq_resource)

      {

          return -ENOENT;

      }

      irqs.pirqs[i] = irq_resource->start;

      /*实现名字的复制操作*/

      //strcpy(tq2440_irqs.name[i],irq_resource->name);

      /*这一句是将指针的地址指向一个具体的地址*/

      irqs.names[i] = irq_resource->name;


      printk("irqs name are %s\n",irq_resource->name);

   }

/*

   cdev_init(&(mykey_dev.cdev),&my2416Led_fops);

   mykey_dev.cdev.owner = THIS_MODULE;

   ret = cdev_add(&(mykey_dev.cdev),devno,1);    

   if(ret)

   {

       printk("Add device error\n");

       return ret;

   }

   printk (DEVICE_NAME " Initialized \n");

   return 0;

*/

fail_malloc:

   unregister_chrdev_region(devno,1);//释放设备号 

class_create_fail:

   unregister_chrdev_region(MKDEV(mykey_major, 0), 1);//释放设备号  

device_create_faile:

   class_destroy(mykey_class);/*注销创建的设备类*/

   return result;


}


int key_remove(struct platform_device *dev)

{

   /*注销设备*/

   device_destroy(mykey_class,MKDEV(mykey_major, 0));

    /*注销创建的设备类*/

   class_destroy(mykey_class);

    /*字符设备注销*/

   cdev_del(&mykey_devp->cdev);//注销cdev

   kfree(mykey_devp);/*释放设备结构体内存*/

   unregister_chrdev_region(MKDEV(mykey_major, 0), 1);//释放设备号

   printk(DEVICE_NAME " exit\n");


   return 0;

}


/*1。平台驱动定义*/ 

static struct platform_driver key_driver = {

 .probe  = key_probe,     /* 平台总线下增加一个平台设备时,调用枚举函数 */

 .remove  = key_remove,    /* 平台总线下去掉一个平台设备时,调用remove函数 */

 .driver  = {

  .name = "myplatformkey",       /* 能支持名为"myplatformkey"的平台设备 */

  .owner = THIS_MODULE,

 },

};


/*2。注册,把我们的驱动加入到平台设备驱动连表中去*/

static int key_drv_init(void)

{

   int ret;

   /*平台驱动注册*/ 

   ret=platform_driver_register(&key_driver);

   return ret;

}


static void __exit key_drv_exit(void)

 {

    /*平台驱动注销*/ 

    platform_driver_unregister(&key_driver);

 }


module_init(key_drv_init);

module_exit(key_drv_exit);


MODULE_LICENSE("GPL"); 


测试的应用程序

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include



#define DEVICE_NAME "/dev/my2416keys"


#define LED_ON 0

#define LED_OFF 1



int main()

{

    int buttons_fd;

    int key_value = 0;


    /*open函数测试*/

    printf("My2416 keys dev test!\n");


    buttons_fd=open(DEVICE_NAME,O_RDWR);

    printf("fd=%d\n",buttons_fd);


    if(buttons_fd < 0)

    {

        perror("open device buttons\n");

        exit(1);

    }


    while(1)

    {

        fd_set rds;

        int ret;


        FD_ZERO(&rds);

        FD_SET(buttons_fd,&rds);

        /*poll函数测试*/

        ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);

        if(ret < 0)

        {

            perror("select");

            exit(1);

        }

        if(ret == 0)

        {

            printf("Timeout.\n");

        }

        else if(FD_ISSET(buttons_fd,&rds))

        {

    /*read函数测试*/

            int ret = read(buttons_fd,&key_value,sizeof key_value);

            if(ret != sizeof key_value)

            {

                if(errno != EAGAIN)

                    perror("read buttons\n");

                continue;

            }

            else

            {

                printf("buttons_value:%d\n",key_value+1);

            }

        }

    }

    /*release函数测试*/

    close(buttons_fd);

    return 0;

}


关键字:按键驱动  platform  中断 引用地址:2416开发记录十一:按键驱动(platform/中断)

上一篇:2416开发记录十:platform的相关函数详解
下一篇:2416开发记录十二:ioremap

推荐阅读最新更新时间:2024-03-16 16:22

STM32 中断初识
前段时间经常用stm32f4 discovery,但是因为对NVIC , EXTI不是很了解,所以使用的过程中一直都在避免使用中断,这两天没什么事决定来学习一下stm32 的中断,写一下自己的心得,如有谬误之处,欢迎指正。 我把用到的几份文档寄存器的文档(RM0090)、《Cortex-M技术参考手册》、《Cortex™-M4 Devices Generic User Guide》、《ARMv7-M Architecture Reference Manual》放在百度云,需要的自取http://pan.baidu.com/s/1hq4L328 密码:4g91 关于与NVIC和EXTI有关的寄存器 先说EXTI吧, EX
[单片机]
单片机定时器中断时间误差的解决方案
1 前言 单片机内部一般有若干个定时器。如8051单片机内部有定时器0和定时器1。在定时器计数溢出时,便向CPU发出中断请求。当CPU正在执行某指令或某中断服务程序时,它响应定时器溢出中断往往延迟一段时间。这种延时虽对单片机低频控制系统影响甚微,但对单片机高频控制系统的实时控制精度却有较大的影响,有时还可能造成控制事故。为扩大单片机的应用范围,本文介绍它的定时器溢出中断与CPU响应中断的时间误差、补偿误差的方法和实例。 2 误差原因、大小及特点 产生单片机定时器溢出中断与CPU响应中断的时间误差有两个原因。一是定时器溢出中断信号时,CPU正在执行某指令;二是定时器溢出中断信号时,CPU正在执行某中断服务程序。 2.1. CP
[单片机]
STM32CubeMX-1.串口收发(中断
一,使用CubeMX生成工程文件 1,配置串口引脚 2,时钟配置 3,打开中断 (此图还不太懂,先记下来,待研究) 4,生成代码 备注: 此处选为自己所用软件 记得勾选此处,让代码结构更容易看懂。 二,完成主函数:直接上代码,数字标注处为代码添加部分。 /** ****************************************************************************** * File Name : main.c * Date : 11/07/2018 19:53:00 * Description : Mai
[单片机]
STM32CubeMX-1.串口收发(<font color='red'>中断</font>)
STM32_EXIT中断
今天的软件工程下载地址(360云盘): https://yunpan.cn/cPhvyer3vIwXh 访问密码 57e1 STM32F10x的资料可以在我360云盘下载: https://yunpan.cn/crBUdUGdYKam2 访问密码 ca90 工程概要说明:定义一个按键(可自己定义),每按键一次,响应中断一次,在中断函数中LED提示灯变化一次,用户可更加实际情况在中断函数做出相应操作,这里只是一个模板。 关于“STM32F103 EXIT中断” 我把重要的几点在下面分别讲述,若不明白,请关注微信公众号“EmbeddDeveloper”查阅或留言。 一、RCC时钟配置 该函数位于在bsp.c
[单片机]
STM32_EXIT<font color='red'>中断</font>
51单片机中断定时器浅谈
中断处理函数原型 void timer1() interrupt 0 using 0 timer1() 函数名 interrupt 表示是中断处理函数 0 表示是第几个中断源的处理函数 using 0 表示是使用第几组工作寄存器一般在C语言里面编译器屏蔽了会自动分配程序员一般不用关心。 注意:如果在中断处理函数中进行处理数据不能处理的数据量太大,因为定时器时间已到它就会又进入下一个中断处理函数,也就是这个中断处理函数还没有处理完下一个中断又来了。这样会造成结果错误或异常。 定时器: 定时器又2个寄存器控制,一个是TMOD 用于选择定时器、计数器 T0,T1的工作模式和工作方式
[单片机]
STM32CubeMX串口空闲中断加DMA实现不定长度收发数据
这里教程是选择的STM32F407。STM32F103也测试过。 1.首先选择串口,选择异步通信。 2.添加DMA 3.打开中断 4.生成代码,生成代码选择了每个外设单独使用.c/.h 5.我使用的是Keil5。打开工程,首先注释掉dma.c里的DMA接收中断,因为不需要DMA接收中断,DMA发送中断是需要的。(dma.c) void MX_DMA_Init(void) { __HAL_RCC_DMA2_CLK_ENABLE(); HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 2, 0); HAL_NVIC_En
[单片机]
STM32外部中断步骤
tm32外部中断就是某种外部事件发生时,单片机的中断系统将迫使CPU暂停正在执行的程序,转而去进行中断事件的处理的一个过程,此博客的目的就是我在学习外部中断的程序步骤: 1.首先进行分配时钟: //以按键的外部中断为例 RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);//给按键的GPIO进行分配时钟; typedef struct { uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; //设置GPIOD的速度,有10hz、20hz、50hz GPIOMode_TypeDef GPI
[单片机]
关于ARM核异常与中断处理机制研究
  一.ARM处理器异常及其对应的模式   当一个异常发生时,ARM处理器总是切换到ARM状态(即非Thumb状态)。Thumb指令集没有包含进行异常处理时需要的一些指令,因此在异常中断时,还是要使用ARM指令。      每种异常都导致内核进入一种特定的模式。此外,可以通过修改cpsr,进入任何ARM处理器模式。用户和系统模式是仅有的可不通过相应异常进入的2中模式。   当一个异常导致模式的改变时,内核自动的:   把cpsr保存到相应异常模式下的spsr   把pc保存到相应模式下的lr   设置cpsr为相应异常模式   设置pc为相应异常处理程序的入口地址   二.向量表   异常发生时,ARM
[单片机]
关于ARM核异常与<font color='red'>中断</font>处理机制研究
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved