您的位置:首页 > 运维架构 > Linux

linux驱动分析之LED

2012-12-15 20:01 323 查看
本次LED驱动的编写是基于linux-2.6.32.6内核

驱动程序led.c

//相关头文件

#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/gpio.h>

#include <linux/errno.h>

#include <linux/ioctl.h>

#include <linux/cdev.h>

#include <linux/string.h>

#include <linux/list.h>

#include <linux/device.h>

#include <asm/uaccess.h>

#include <asm/atomic.h>

#include <asm/unistd.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

static int my_gpio_major =0;

/*这里的主要目的是:当需要打印调试信息时使用#define DUG_printk printk

 当不需要打印调试信息时使用#define DUG_printk(x...),可是在程序完成的时候方便地屏蔽打印信息

*/

//#define DUG_printk printk

#define DUG_printk(x...)

/*

 先定义一个类,在这个类下定义一个设备,这样可以动态的创建设备节点,

避免手动输入的麻烦

*/

static struct class *my_gpio_class;

static void my_gpio_cedv(void);//函数的声明

/*

定义一个cdev结构,这是2.6内核相对2.4的一个新变化

*/ 

struct my_gpio_dev_t{

  struct cdev mycdev;

  

} my_gpio_cdev;

//应用程序执行open时,该函数将被调用

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

{   

   unsigned int i; 

DUG_printk("my_gpio_open  \n");

  for(i=5;i<9;i++)

{

         /*

         注意从linux2.6.30.4到linux-2.6.32.60,GPIO物理地址定义时的方式发发生变化

例如  linux2.6.30.4 中

#define S3C2410_GPH6        S3C2410_GPIONO(S3C2410_GPIO_BANKH, 6)定义GPH6

但是linux-2.6.32.60中这样定义S3C2410_GPH(1) 宏定义为

#define S3C2410_GPH(_nr) (S3C2410_GPIO_H_START + (_nr))

在2.6.32内核中,s3c2410_gpio_cfgpin是在include/linux/gpio.h中定义的,要添加这个文件

在sound/soc/s3c24xx/s3c24xx-i2s.c中,第24行添加:

#include <linux/gpio.h>

        */

 s3c2410_gpio_cfgpin(S3C2410_GPB(i),S3C2410_GPIO_OUTPUT);//配置端口为输出

 s3c2410_gpio_setpin(S3C2410_GPB(i), 0);//刚打开时输出0,四个LED点亮

 DUG_printk("LEDS ALL OFF %d \n",i);

}

return 0;

}

//应用程序执行IOCTL时内核该函数被调用

static int my_gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

  s3c2410_gpio_setpin(S3C2410_GPB(5+arg), cmd);//根据应用程序IOCTL传来的命令控制相应的LED

  return 0;

  

}

//这是一个结构体,也是字符设备的核心,

static struct file_operations my_gpio_fops ={

   .owner = THIS_MODULE,

   .open = my_gpio_open,

   .ioctl= my_gpio_ioctl,

} ;

//初始化部分,执行insmod xxxx.ko时该函数被调用

static int __init my_gpio_init(void)

{ u8 result,minor;

 

  dev_t devno ;//定义一个设备号

  

 

 if(my_gpio_major)

  {

   devno= MKDEV(my_gpio_major,0);

/*2.6内核中,当主设备号已知时执行这部分语句,1代表此设备号只有一个,而devno= MKDEV(my_gpio_major,0);里次设备号为0,所以注册的这个设备 次设备号就是0

*/

   result = register_chrdev_region(devno, 0, "my_gpio0");

     DUG_printk("register_chrdev_region  \n");

    }

 

 else

  {

    //当主设备号未知时,执行该语句,下面有相详细分析

    result = alloc_chrdev_region(&devno, 0, 1, "my_gpio");

      my_gpio_major = MAJOR(devno);//获得主设备号

  minor = MINOR(devno);//获得次设备号

  DUG_printk("minor =%d \n",minor);

  DUG_printk("alloc_chrdev_region my_gpio_major =%d \n",my_gpio_major);

  }

 if(result)//成功申请设备号 返回0

  DUG_printk("register faill \n");

   my_gpio_cedv();//调用函数完成cdevd的相关设置

//动态分配设备节点

  my_gpio_class = class_create(THIS_MODULE, "my_gpio2");

  device_create(my_gpio_class, NULL, MKDEV(my_gpio_major,0),NULL, "my_gpio");

 

  return 0;

}

//完成cdevd的相关设置

 static void my_gpio_cedv(void)

 {

  u8 ret;

  dev_t devno ;  //定义一个dev_t结构体变量,存储设备号

  devno = MKDEV(my_gpio_major,0);//可以通过主次设备号生成dev_t

  cdev_init(&my_gpio_cdev.mycdev,&my_gpio_fops);//初始化cedv并建立cdev与file_operations的连接

  my_gpio_cdev.mycdev.owner = THIS_MODULE;

  ret = cdev_add(&my_gpio_cdev.mycdev,devno,1);;//注册一个cdev

                 

  if(ret<0)

   DUG_printk("cdev_add faill \n");

 }

//模块卸载函数,在执行rmmod时执行,

static void __exit my_gpio_exit(void)

{

 cdev_del(&my_gpio_cdev.mycdev);

device_destroy(my_gpio_class, MKDEV(my_gpio_major, 0));

class_destroy(my_gpio_class);

 unregister_chrdev_region(MKDEV(my_gpio_major,0), 1);

 DUG_printk("my_gpio_exit  \n");

}

module_init(my_gpio_init);

module_exit(my_gpio_exit);

MODULE_LICENSE("GPL");

有关cdev的一些操作函数

void cdev_init(struct cdev *, struct file_operations *); 

struct cdev *cdev_alloc(void); 

void cdev_put(struct cdev *p); 

int cdev_add(struct cdev *, dev_t, unsigned); 

void cdev_del(struct cdev *); 

在 调用 cdev_add() 函 数 向系统注册 字符 设备 之前 , 应首先调用

regist er_chrdev_regi on()或 alloc_chrdev_region()函数向系统申请设备号, 这两个函数

的型如下:

int  register_chrdev_region(dev_t  from,  unsigned  count,  const  char 

*name); 

int  alloc_chrdev_region(dev_t  *dev,  unsigned  baseminor,  unsigned  count, const char *name);

当设备号已知情况下用

int  register_chrdev_region(dev_t  from,  unsigned  count,  const  char *name )

中from,是dev_t结构,用MKDEV(int major, int minor) 可以通过主次设备号生成dev_t,

其中的count是定义的次设备号的个数,MKDEV(int major, int minor)中的minor是起始次设备号,当minor=0,如果count=5,那么申请的次设备号就是 0,12,3,4,const  char *name是设备名称,当使用cat  /proc/devicess命令时会在相应的主设备号处看到该名字

当设备号未知的情况下

int  alloc_chrdev_region(dev_t  *dev,  unsigned  baseminor,  unsigned  count, const char *name);

被执行,将动态分配设备号,并保存在dev_t  *dev,里,unsigned  baseminor是次设备号的起始值,其他参数与int  register_chrdev_region(dev_t  from,  unsigned  count,  const  char *name )意义相同,

2.4内核的注册方法

register_chrdev()仅支持0到255的主设备号,已经不满足

现在多主机的系统,所以cdev出现,2.4内核以前用register_chrdev,

2.6以后用cdev

register_chrdev()注册时,一个主设备号对应0~255个次设备号,

但使用cedv的方法,一个主设备号对应的次设备号的起始值和个数已经确定

自动创建设备节点 

头文件#include <linux/device.h>

第一步;创建一个类static struct class *forthdrv_class;

类下创建设备static struct  device *forthdrv_class_dev;

第二步

forthdrv_class = class_create(THIS_MODULE, "forth_drv");

forthdrv_class_dev = device_create(forthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

注意:如果使用cedv,则MKDEV(major, 0)的次设备号需要与注册时对应 

 释放:device_unregister(my_gpio_device);

  class_destroy(my_gpio_class);

我发现也释放时可以使用

 class_destroy(led_class);

 unregister_chrdev(major, "led");也可以

这个驱动只是一个简单的例子,完整的驱动还需要添加各个函数返回值的判断部分,

编写测试程序,

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#define dug_printf printf

int main(int argc,char **argv)

 {

   int fd;

   int i;

   

   fd = open("/dev/my_gpio",O_RDWR);//打开

  while(1)

   {

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

     {

     ioctl(fd,1,i);

  sleep(1);//睡眠1S是LED凉1S实现流水灯效果

     }

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

     {

     ioctl(fd,0,i);

  sleep(1);

     }

   }

  

   return 0;

}

如果想实现开发板启动后自动运行流水灯的话,

第一

  把编好的驱动程序放在目录/drivers/char下并修改这个目录下的Kconfig文件添加

config MY_LED

      tristate"MY_LED FALSH"

      depends on ARCH_S3C2440

      ---help---

           LEDS_flash

并修改这个目录下的Makefile  添加obj-$(CONFIG_MY_LED)            += led.o

配置内核,选择

-> Device Drivers 

   -> Character devices

<*> MY_LED FALSH

*代表编译进内核

编译测试程序生成led_flash,

 上传led测试程序led_flash到/xxx目录(

在etc/init.d/rcS 文件添加/xxx/led_test &   //&代表后台启动LED测试程序

重启开发板即可

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: