历史上的今天
返回首页

历史上的今天

今天是:2025年01月17日(星期五)

正在发生

2019年01月17日 | 2416开发记录十一:按键驱动(platform/中断)

2019-01-17 来源:eefocus

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


这里用到了字符设备驱动,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;

}


推荐阅读

史海拾趣

Apx-Crystal公司的发展小趣事

在快速发展的同时,Apx-Crystal公司始终坚持创新驱动的发展理念。公司投入大量资金用于研发创新和技术升级,不断推出具有市场竞争力的新产品。同时,公司还注重品牌建设和市场推广,通过参加行业展会、举办技术研讨会、开展线上线下营销活动等方式,提升品牌知名度和影响力。这些举措使得Apx-Crystal在行业内树立了良好的品牌形象,成为电子元件领域的佼佼者。

Advanced Power Solutions公司的发展小趣事

面对日益激烈的国际竞争,Advanced Power Solutions决定实施全球化战略。公司先后在多个国家和地区设立了研发中心和生产基地,以便更好地满足当地市场的需求。同时,公司还加强了与国际同行的交流与合作,共同推动电源管理技术的发展。这些举措使公司的竞争力得到了显著提升,也为公司的长远发展奠定了坚实基础。

Alpha 3 Manufacturing公司的发展小趣事

Alpha 3 Manufacturing公司成立于XXXX年,初期面临着资金短缺、市场竞争激烈以及技术瓶颈等多重挑战。然而,创始人凭借对电子行业的深刻理解和敏锐的市场洞察力,看到了电子产品制造领域的巨大潜力。他带领团队攻克技术难关,开发出了一款具有创新性的电子产品,迅速获得了市场的认可,为公司的起步奠定了坚实的基础。

台湾双羽公司的发展小趣事

富士通的故事始于1935年,当时它作为一家电信设备制造公司在日本成立。在那个通信技术刚刚起步的时代,富士通凭借其创新精神和卓越的技术实力,迅速在电信设备领域崭露头角。公司最初专注于电话交换机的生产,随着技术的不断进步,富士通逐渐扩大了业务范围,为日本的电信基础设施建设做出了重要贡献。这一阶段的成功,为富士通后续在电子行业的蓬勃发展奠定了坚实的基础。

EPT公司的发展小趣事

EPT深知产品质量是企业发展的基石。因此,公司不仅通过了ISO9001:2015和BSCI等国际质量管理体系认证,还获得了CE、UL、IEC62133、KC等多项证书。此外,EPT还为所有电池购买了产品责任险,为客户提供双重保障。这一举措不仅提升了客户对EPT产品的信任度,也为其在电子行业中树立了良好的品牌形象。

比亚迪(BYD)公司的发展小趣事

比亚迪的发展可以追溯到1995年,当时王传福创立了这家公司,最初专注于生产小型电池。在初创期,比亚迪通过模仿日本三洋和索尼的产品快速积累了生产经验。然而,王传福深知模仿不是长久之计,因此他果断地决定转向自主研发和创新。这一决策在日后证明是极其明智的。随着技术的不断积累,比亚迪于1997年开始量产锂离子电池,并成功实现了年销售额突破1亿元。这一里程碑标志着比亚迪在电池技术领域取得了重大突破,为其日后在电子行业的发展奠定了坚实基础。

问答坊 | AI 解惑

无线发射功率以及接收灵敏度

发射功率与增益 无线电发射机输出的射频信号,通过馈线(电缆)输送到天线,由天线以电磁波形式辐射出去。电磁波到达接收地点后,由天线接收下来(仅仅接收很小很小一部分功率),并通过馈线送到无线电接收机。因此在无线网络的工程中,计算发 ...…

查看全部问答>

请教:如何从硬盘引导进入vxworks

各位,我先从优盘引导进入dos,盘符为C: 然后把生成的bootrom.sys,vxworks以及vxload.com拷贝到硬盘D:(已用format d:/s格式化了) 结果启动到verifying DMI pool data......就不动了,我的硬盘是4G的电子盘,我不知道这个做法对否?希望大家能帮 ...…

查看全部问答>

又是可怕的DEMO: InterruptInitialize failed!!! 请各位前辈帮帮忙。

InterruptInitialize(SysInt,Interruptevent,NULL,0)      我的这个函数总是返回为FALSE,      SysInt在oalintr.h上已经定义,而且在cfw.s   和armint.c也加入相应的中断处理,使能,代码Interru ...…

查看全部问答>

你知道预处理器标识#error的目的吗?

   这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。…

查看全部问答>

成立一个电子技术论坛,大家去看看!!www.520ic.cn

关于单片机、开关电源、信号源与频率计的专业论坛,大家有问题或者想下相关资料的去看看。另外本站首页提供200多万的ic芯片pdf资料搜索下载。www.520ic.cn…

查看全部问答>

网络驱动无法调用我的中断服务程序?/(帮忙)

通过PCI 总线配置寄存器,并安装了中断服务程序,查看中断寄存器也显示发送 和接收包正常,但就是发完和接完就是没有调用中断程序…

查看全部问答>

各位用过的兄弟:STM32生产时怎么设置保密性?

                                 如题,怎样设置以防止软件被读出??…

查看全部问答>

不好意思顶了老帖子,我对UART超时的用法。

我在CC2430上实现了MODBUS,在CC1110上实现了UART透传。 UART应用程序中超时,串口中断的时候数据往环形队列里面放,队列就是一个组数,有两个标记表示头和尾,这个方法我也是模仿一些国外开发系统中集成的程序。 主程序中用getch函数,读队 ...…

查看全部问答>

请教关于STM8S105的问题

                                 请问什么原因会导致STM8S105的可用RAM空间小于128字节呢?文档上不是说有2K的RAM吗。程序出了一点小问题,感觉是ra ...…

查看全部问答>