mini2440 按键和led灯的驱动

      很久没有继续写博客了,主要是最近一段时间在友善之臂开发板mini2440上调试驱动程序。按照配套的教材我分别看完按键驱动和led灯的驱动,突然想将两者结合起来,即写一个新的驱动,在该驱动中实现按键控制led灯的功能,简而言之,是将两者驱动融合,在内核空间实现此功能,然后在用户空间编写应用程序来测试所写的驱动。熟悉一下驱动的编写和移植流程。经过一段时间的努力,终于实现了此功能。写驱动的基本流程

 

(1)看原理图,熟悉硬件的IO端口接线,查看相关芯片说明手册。列出相关寄存器的表格

 (2)驱动的编写尽量在已有驱动的基础上改动,切忌从头编写

(3)编写相应的Makefile ,根据要求修改该驱动程序所在目录的Makefile和Konfig文件,然后在源代码的根目录下执行make menuconfig

新编写的驱动会在其中显示。保存退去后,执行make  modules 即可生成相应驱动的文件,后缀为.ko(有错误需修改驱动或Makefile,直到没有错误为止)下载到开发板上即可  详细的步骤见下面的介绍。

 

驱动源代码如下:

 

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/sched.h>

#define  DEVICE_NAME    "buttons_leds_zhao"
#define  IOCTL_LED_ON   1
#define  IOCTL_LED_OFF  0

struct button_irq_desc {
    int irq;
    int pin;
    int pin_setting;
    int number;
    char *name;
};

static struct button_irq_desc button_irqs [] = {
    {IRQ_EINT8 , S3C2410_GPG(0) ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG(3) ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG(5) ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG(6) ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG(7) ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},
};
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};

static unsigned long led_table [] = {
        S3C2410_GPB(5),
        S3C2410_GPB(6),
        S3C2410_GPB(7),
        S3C2410_GPB(8),
};

static unsigned int led_cfg_table [] = {
        S3C2410_GPIO_OUTPUT,
        S3C2410_GPIO_OUTPUT,
        S3C2410_GPIO_OUTPUT,
        S3C2410_GPIO_OUTPUT,
};

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;//the flag of the interupt event ,the value that is 1 shows that the interupt has happend

 

static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
    int down;

 

 

其中函数s3c2410_gpio_getpin等在linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach/hardware.h找得到。它的功能是得到相应的管脚值,一般一个管脚有四种功能有关叫32位寄存器中2位来控制。驱动写好之后,接下来我们要做的工作是修改Kconfig和Makefile。

 

 

因为我所写的这个驱动属于字符驱动,因此将它放在/linux-2.6.32.2/drivers/char目录下

 

在该目录下修改Kconfig文件

 

config MINI2440_HELLO_MODULE
        tristate "Mini2440 module sample"
        depends on MACH_MINI2440
        default m if MACH_MINI2440
        help
          Mini2440 module sample.

 


config BUTTONS_LEDS_ZHAO
        tristate "Mini2440 button  and leds sample"
        depends on MACH_MINI2440
        default m if MACH_MINI2440
        help
          Mini2440  button and leds  module sample.
添加上面BUTTONS_LEDS_ZHAO一段,这一段的作用是我们在源代码目录下输入命令

 

make menuconfig 后的菜单显示的一个条目。保存退出后,在 /linux-2.6.32.2/drivers/char下修改

Makefile文件,上面修改的Kconfig文件只是在菜单配置的界面出现该条目,如果不修改该目录下Makefile,仍然不能编译该驱动。Makefile的修改如下:

obj-$(CONFIG_MINI2440_BUZZER)   += mini2440_pwm.o
obj-$(CONFIG_MINI2440_ADC)      += mini2440_adc.o
obj-$(CONFIG_BUTTONS_LEDS_ZHAO)      += button_leds_zhao.o

添加了最下面一行。将该驱动选择为模块编译方式

但是在源代码目录下执行make modules 依然不能生成button_leds_zhao.ko,原来在我的源代码目录下[root@localhost linux-2.6.32.2]# ls
arch                 config_mini2440_vga1024x768  firmware  kernel         Module.symvers  security
block                COPYING                      fs        lib            net             sound
config_mini2440_a70  CREDITS                      include   MAINTAINERS    README          tools
config_mini2440_l80  crypto                       init      Makefile       REPORTING-BUGS  usr
config_mini2440_n35  Documentation                ipc       mm             samples         virt
config_mini2440_t35  drivers                      Kbuild    modules.order  scripts

 

 

 

 由于新解压的包,当中配置文件根据显示屏的不同有好几种,我的显示屏是NEC的 因此执行命令 cp  config_mini2440_n35   .config 后在执行该命令就可以生成了。具体原因会在下一篇博客中讨论.config,  Kconfig, Makefile 讨论

 

 

下载到开发板上去后,执行modprobe  button_leds_zhao 就可以了,注意不要带后缀.ko

 

 

编写的测试程序如下;

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#include<errno.h>

int  main()
{
        int i,button_fd;
        char  key_values[6]={'0','0','0','0','0','0'};
         char temp='0';

 button_fd=open("/dev/buttons_leds_zhao",0);
 if(button_fd<0)
 {
    perror("cannot open device buttons");
    exit(1);
  }
        while(1)
 {
  fd_set rds;
  int  ret,io;
  
  FD_ZERO(&rds);
  FD_SET(button_fd,&rds);
  
  ret=select(button_fd+1,&rds,NULL,NULL,NULL);

  if(ret<0)
  {
   perror("select");
   exit(1);
  }
        if(0==ret)
       {
   printf("time out/n");
       }
         else if (FD_ISSET(button_fd,&rds))
       {
                         int ret=read(button_fd,key_values,sizeof(key_values));
   if(ret!=sizeof(key_values))
   {
   if(errno!=EAGAIN)
   perror("read button/n");
   continue;
   }
   else
            {
   for(i=0;i<6;i++)
   if(key_values[i]!='0')
   {
    printf("K %d  is  %s/n",i+1,
         key_values[i]=='0'?"released":"pressed down ");
   
  if(temp==key_values[i])
               {
   if(i>=0&&i<4)
                       ioctl(button_fd,0,i);//light off
            printf("light  off/n");
           temp='0';
  }
//i means the number of the six buttons,but we only control four lights here
  else
  {
    if(i>=0&&i<4)
       ioctl(button_fd,1,i);//light on
   printf("light  on/n");
    temp=key_values[i];

   
  }


   printf("temp=%c /n",temp);
   }
  }
 }
}
  close(button_fd);
  return  0;
}

 

测试程序在编译以后,一直达不到理想的要求,按下一个键后,灯亮,但是再按其他的键没有反应。多次修改后。终于实现功能。即按下一个键,相应的灯亮,再按下该键,灯灭。按其他的键有同样的效果。

        经过这次的调试,掌握了最简单驱动的编写和移植。

 


 

    // udelay(0);
    down = !s3c2410_gpio_getpin(button_irqs->pin);

    if (down != (key_values[button_irqs->number] & 1)) { // Changed

        key_values[button_irqs->number] = '0' + down;

        ev_press = 1;
        wake_up_interruptible(&button_waitq);
    }

    return IRQ_RETVAL(IRQ_HANDLED);
}

 


static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
    int i;
    int err = 0;

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
    {
        if (button_irqs[i].irq < 0)
        {
                continue;
        }
        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
                          button_irqs[i].name, (void *)&button_irqs[i]);
        if (err)
            break;
    }

    if (err)
    {
       // i--;
        for (; i >= 0; i--)
         {
            if (button_irqs[i].irq < 0)
            {
                continue;
            }
            disable_irq(button_irqs[i].irq);
            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
        return -EBUSY;
    }


 for(i=0;i<sizeof(led_cfg_table)/sizeof(led_cfg_table[0]);i++)
 {
  s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
 }


   // ev_press = 1;

    return 0;
}

 

static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
    int i;

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
        if (button_irqs[i].irq < 0) {
            continue;
        }
        free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    }

    return 0;
}

 

static int s3c24xx_buttons_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
            wait_event_interruptible(button_waitq, ev_press);
    }

    ev_press = 0;//clear the flag of the interupt every time after read a time

    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 s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait);
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;
    return mask;
}

static int    s3c24xx_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
 if(arg>4)
    return -EINVAL;
        switch(cmd)
       {
        case IOCTL_LED_ON:
      s3c2410_gpio_setpin(led_table[arg],0);
 // printk("light  on/n");
   break;

        case IOCTL_LED_OFF:
              
                s3c2410_gpio_setpin(led_table[arg], 1);
   // printk("light off/n");
          break;
         default:
                return -EINVAL;
        }
 return 0;
}

static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   s3c24xx_buttons_open,
    .release =   s3c24xx_buttons_close,
    .read    =   s3c24xx_buttons_read,
    .poll    =   s3c24xx_buttons_poll,
    .ioctl  =   s3c24xx_leds_ioctl,
};

static struct miscdevice misc = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEVICE_NAME,
        .fops = &dev_fops,
};
static int __init dev_init(void)
{
        int ret;

        ret = misc_register(&misc);

        printk (DEVICE_NAME"/tinitialized/n");

        return ret;
}

static void __exit dev_exit(void)
{
        misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

这个驱动主要是将两个单独的驱动结合起来。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值