学习控制发光二极管代码心得

LED的完整驱动代码

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/pci.h>

#include <asm/uaccess.h>

#include <mach/map.h>

#include <mach/regs-gpio.h>

#include <mach/gpio-bank-m.h>

 

#define DEVICE_NAME "s3c6410_leds"

#define DEVICE_COUNT 1               //  设备数量

#define S3C6410_LEDS_MAJOR 0   // 默认主设备号

#define S3C6410_LEDS_MINOR 234  // 默认次设备号

#define PARAM_SIZE  3 //定义数组长度

static unsigned char mem[4]; // 保存4Leds的设置状态

static int major = S3C6410_LEDS_MAJOR;

static int minor = S3C6410_LEDS_MINOR;

static dev_t dev_number; //  设备号

static int leds_state = 1;

static char *params[] = {"string1", "string2","string3"};

static int param_size = PARAM_SIZE;

 

static struct class *leds_class = NULL;

/*********5.控制LED**********/

// s3c6410_leds_ioctl函数用于接收向LED驱动传递的命令和参数,通过I/O命令控制LED

static long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd,

        unsigned long arg)

{

  //命令只能是01

  switch (cmd)

  {

    unsigned tmp;

    case 0:

    case 1:

    if (arg > 4)

    {

      return -EINVAL;

    }

  //读取GPMDAT寄存器的当前值

  tmp = ioread32(S3C64XX_GPMDAT);

  if (cmd == 1)

  {

    tmp &= (~(1 << arg));

  }

  else

  {

    tmp |= (1 << arg);

  }

  //GPMDAT寄存器写入数据

  iowrite32(tmp, S3C64XX_GPMDAT);

  return 0;

  default:

  return -EINVAL;

  }

}

//s3c6410_leds_write函数用于接收向/dev/s3c6410_leds设备文件写入的字符串

static ssize_t s3c6410_leds_write(struct file *file, const char __user *buf,

        size_t count, loff_t *ppos)

{

  unsigned tmp = count;

  unsigned long i = 0;

  memset(mem, 0, 4);//mem数组所有元素的值都设为0

  //最多写入4个字符,多余的字符将忽略

  if (count > 4)

  {

    tmp = 4;

  }

  if (copy_from_user(mem, buf, tmp))

  {

    return -EFAULT;

  }

  else

  {

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

    {

      tmp = ioread32(S3C64XX_GPMDAT);

      if (mem[i] == '1')

      {

        tmp &= (~(1 << i));

      }

      else

      {

        tmp |= (1 << i);

      }

      iowrite32(tmp, S3C64XX_GPMDAT);

    }

  return count;

  }

}

static struct file_operations dev_fops ={ .owner = THIS_MODULE, .unlocked_ioctl = s3c6410_leds_ioctl, .write =   s3c6410_leds_write };

static struct cdev leds_cdev;

/********2.创建设备文件(/dev/s3c6410_leds********/

static int leds_create_device(void)

{

  int ret = 0;

  int err = 0;

  // 1步: 初始化cdev的成员,并建立cdevfile_operations之间的连接

  cdev_init(&leds_cdev, &dev_fops);

  leds_cdev.owner = THIS_MODULE;

  //2步:主设备号>0,通过指定设备号的方式注册字符设备区域

  if (major > 0)

  {

    //指定设备号的两种方式:直接在代码中指定(硬编码),动态分配

    //  获取设备号(主设备号和次设备号)

    dev_number = MKDEV(major, minor);

    err = register_chrdev_region(dev_number, DEVICE_COUNT, DEVICE_NAME);

    if (err < 0)

    {

      printk(KERN_WARNING "register_chrdev_region() failed\n");

      return err;

    }

  }

  else

  {

    err = alloc_chrdev_region(&leds_cdev.dev, 10, DEVICE_COUNT,

         DEVICE_NAME);

    if (err < 0)//注册字符设备区域失败

    {

      printk(KERN_WARNING "alloc_chrdev_region() failed\n");

      return err;

    }

    //一般采用分别指定主设备号和次设备号的方式指定设备号。

    major = MAJOR(leds_cdev.dev);

    minor = MINOR(leds_cdev.dev);

    //dev_number = MKDEV(major, minor);

    dev_number = leds_cdev.dev; 

  }

  //3步:将字符设备添加到内核中的字符设备数组中

  ret = cdev_add(&leds_cdev, dev_number, DEVICE_COUNT);

  //4步:使用class_create宏创建struct class

  leds_class = class_create(THIS_MODULE, DEVICE_NAME);

  //5步:创建设备文件

  device_create(leds_class, NULL, dev_number, NULL, DEVICE_NAME);

  return ret;

}

/********4.设置寄存器与初始化LED驱动*******/

static void leds_init_gpm(int leds_default)

{

  int tmp = 0;

  //  初始化端口配置寄存器

  tmp = ioread32(S3C64XX_GPMCON);

  tmp &= (~0xFFFF);

  tmp |= 0x1111; // 0001000100010001

  iowrite32(tmp, S3C64XX_GPMCON);

  //  初始化端口上拉电路寄存器

  tmp = ioread32(S3C64XX_GPMPUD);

  tmp &= (~0xFF);

  tmp |= 0xAA; // 01010101

  iowrite32(tmp, S3C64XX_GPMPUD);

   //  初始化端口数据寄存器

  tmp = ioread32(S3C64XX_GPMDAT);

  tmp &= (~0xF);

  tmp |= leds_default;

  iowrite32(tmp, S3C64XX_GPMDAT);

}

/********1. 初始化LED驱动*********/

static int leds_init(void)

{

  int ret;

  ret = leds_create_device();

  leds_init_gpm(~leds_state);

  printk(DEVICE_NAME"\tinitialized\n");

  //输出params数组中的值

  printk("param0\t%s\n", params[0]);

  printk("param1\t%s\n", params[1]);

  printk("param2\t%s\n", params[2]);

  return ret;

}

/*********3.卸载LED驱动的设备文件*********/

static void leds_destroy_device(void)

{  //移除通过device_create函数建立的字符设备

  device_destroy(leds_class, dev_number);

  if (leds_class)  //销毁struct class

    class_destroy(leds_class);

  //注销字符设备区域

  unregister_chrdev_region(dev_number, DEVICE_COUNT);

  return;

}

static void leds_exit(void)

{

  leds_destroy_device();//卸载LED驱动的设备文件

  printk(DEVICE_NAME"\texit!\n");

}

//LED驱动的模块参数

module_init(leds_init);

module_exit(leds_exit);

module_param(leds_state, int, S_IRUGO|S_IWUSR);

//下面一行是指定数组类型模块参数的相关信息

module_param_array(params, charp, ¶m_size, S_IRUGO|S_IWUSR);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Lining");

posted @ 2016-05-22 11:19  bbbox  阅读(688)  评论(0编辑  收藏  举报