简单侶途  发表于 2015-11-9 11:57:11

五种方法点亮LED,入门驱动程序,更新完毕

本帖最后由 简单侶途  于 2015-11-13 15:35 编辑

首先介绍我用到的硬件平台和内核版本:Linux cubietruck 3.4.79内核版本,硬件为cubietruck开发板
学习点亮LED入门参考资料,先可以看看下面3份简单的介绍,程序将要用到的,不做其他简单解释咯。



1.必备搭建的工具
实现操作必备的命令:
安装工具
#apt-get install gcc git build-essential libusb-dev pkg-config libusb-1.0
#git clone https://github.com/linux-sunxi/sunxi-tools
#cd sunxi-tools
#make
#cp bin2fex fex2bin fexc /bin
操作script.fex
#vi script.fex

提示:如何找到script.fex进行修改 (以下对于cubietruck卡系统启动)
# mount /dev/mmcblk0p1 /mnt/
#cd /mnt
#ls                                                      
lost+foundpulse-PKdhtXMmr18nscript.binscript.fexuEnv.txtuImage


2.修改配置文件,#后面的是注释,请忽略添加在script.fex文件下,script.fex文件修改以下部分,来实现控制4个led
#-----------------------------------------------------------------------------表情那里是PH20,不知道为何是表情

gpio_used = 1   #1为使能,0为失能
gpio_num = 4    #4为应用4个引脚配置
gpio_pin_1 = port:PH20<1><default><default><1>#num = 1
gpio_pin_2 = port:PH21<1><default><default><1>#num = 2
gpio_pin_3 = port:PH11<1><default><default><1>#num = 3
gpio_pin_4 = port:PH7<1><default><default><1>   #num = 4


leds_used = 0   #失能led系统配置,这些引脚被我引用为4个led控制引脚了
leds_num = 4

3.修改完后,记得执行
root@cubietruck:/mnt# fex2bin script.fex script.bin
#reboot

4.开始我们的第一门入门学习了,刚开始学的时候可以参考韦东山视频教程的点亮LED,不过平台不一样,大体框架还算类似#include<linux/fs.h>
#include<linux/module.h>
#include<linux/errno.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/device.h>
#include<linux/types.h>
#include <asm/gpio.h>
#include <linux/gpio.h>
#include <plat/sys_config.h>

#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/io.h>

//设备类
static struct class *leddrv_class;
//分配 cdev
static struct class_device *leddrv_class_dev;
//主设备号
static int major;

static int status;//表示灯的状态

static int led_open(struct inode *inode, struct file *file)
{
      return 0;
}


static ssize_t led_read (struct file *file,char __user *buf,
                                                 size_t count,loff_t *f_pos)
{
      copy_to_user(buf,&status,4);
      return count;
}

static ssize_t led_write (struct file *file,char __user *buf,
                                                size_t count,loff_t *f_pos)
{
      int val;
      /*1.从用户空间读取数据到内核空间*/
      copy_from_user(&val,buf,4);

      /*2.判断传入值val,进行GPIO电平控制*/
      if(val == 1){
                gpio_set_value(1,1);
                gpio_set_value(2,1);
                gpio_set_value(3,1);
                gpio_set_value(4,1);
      }
      else{
                gpio_set_value(1,0);
                gpio_set_value(2,0);
                gpio_set_value(3,0);
                gpio_set_value(4,0);
      }
      return 0;
}

//分配驱动操作
static struct file_operations leddrv_fops = {

      .owner   =    THIS_MODULE,
      .open         =    led_open,
      .read    =    led_read,
      .write   =    led_write,
};

static intled_init(void)
{
      
         major = register_chrdev(0, "led", &leddrv_fops);
      leddrv_class = class_create(THIS_MODULE, "led");
      leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "ledwht"); /* /dev/ledwht */


      /*申请GPIO资源*/
      if(gpio_request(1,"gpio")){
                      printk("request gpio1 error\n");
      }
      if(gpio_request(2,"gpio")){
                      printk("request gpio2 error\n");
          }
          if(gpio_request(3,"gpio")){
                  printk("request gpio3 error\n");
          }
          if(gpio_request(4,"gpio")){
                   printk("request gpio4 error\n");
          }         
      
      /*配置为输出引脚*/
      gpio_direction_output(1,0);
          gpio_direction_output(2,0);
          gpio_direction_output(3,0);
          gpio_direction_output(4,0);

      return 0;
}

static void led_exit(void)
{
      /*1.释放 GPIO 资源*/
      gpio_free(1);
      gpio_free(2);
      gpio_free(3);
      gpio_free(4);
      unregister_chrdev(major, "led");
      /*2.删除设备节点*/
      device_destroy(leddrv_class, MKDEV(major, 0));
      class_destroy(leddrv_class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
5.测试驱动程序
1.insmod led_drv1.ko //加载驱动
2../led_app                  //同时点亮4个led
end--firstdriver---------------------------------------------------------------------------------------------------------------------------

6.对比一下两个文件可以看出,就寄存器部分不一样而已#include<linux/fs.h>
#include<linux/module.h>
#include<linux/errno.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/device.h>
#include<linux/types.h>
#include <asm/gpio.h>
#include <linux/gpio.h>
#include <plat/sys_config.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/io.h>

#include <linux/moduleparam.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/mutex.h>
#include <linux/compat.h>


/*两种方法寻找GPIO寄存器地址*/
//#define PIO_BASE   0x01C20800
#define PH_BASE   (PIO_BASE + 7*0x24)

#define PH_CFG2   (PH_BASE + 0x08)
#define PH_DAT    (PH_BASE + 0x10)

//#define PH_BASE(PIO_BASE + 0xFC)
//#define PH_CFG2(PIO_BASE + 0x104)
//#define PH_DAT   (PIO_BASE + 0X10C)

static volatile unsigned int *ph_cfg2;
static volatile unsigned int *ph_dat;

//设备类
static struct class *leddrv_class;
//分配 cdev
static struct class_device *leddrv_class_dev;
//主设备号
static int major;

static int led_open(struct inode *inode, struct file *file)
{
      /*bit20,bit21
         *open the roange_led and blue_led GPIO output
         *Output is 001
         */
      *ph_cfg2 &= ~((7<<16) | (7<<20));//清零000,这个步骤很重要,注释掉可以看下效果如何blue_led不能控制了
      *ph_cfg2 |= ((1<<16) | (1<<20));//设置001
      return 0;
}


static ssize_t led_write (struct file *file,char __user *buf,
                                                size_t count,loff_t *f_pos)
{
      int val;
      /*1.从用户空间读取数据到内核空间*/
      copy_from_user(&val,buf,4);

      /*2.判断传入值val,进行GPIO电平控制*/
      if(val == 1){
                *ph_dat |= ((1<<20) | (1<<21));               

      }else{
                *ph_dat &= ~((1<<20) | (1<<21));
      }
      return 0;
}

//分配驱动操作
static struct file_operations leddrv_fops = {

      .owner   =    THIS_MODULE,
      .open         =    led_open,
      .write   =    led_write,
};

static intled_init(void)
{
      ph_cfg2 = ioremap(PH_CFG2, 4);
      ph_dat= ioremap(PH_DAT, 4);
      
         major = register_chrdev(0, "led", &leddrv_fops);
      leddrv_class = class_create(THIS_MODULE, "led");
      leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "ledwht"); /* /dev/ledwht */

      return 0;
}

static void led_exit(void)
{

      unregister_chrdev(major, "led");
      /*2.删除设备节点*/
      device_destroy(leddrv_class, MKDEV(major, 0));
      class_destroy(leddrv_class);

      iounmap(ph_cfg2);
      iounmap(ph_dat);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
7.第二个应用操作寄存器方法控制LED点亮led_orange,led_blue
现在就可以直接打开script.fex修改回原来到配置了

gpio_used = 1   #1为使能,0为失能
gpio_num = 4    #4为应用4个引脚配置
gpio_pin_1 = port:PH10<1><default><default><1>#num = 1//随便乱搞可以了,因为你可以直接操作地址来控制它
gpio_pin_2 = port:PH11<1><default><default><1>#num = 2
gpio_pin_3 = port:PH11<1><default><default><1>#num = 3
gpio_pin_4 = port:PH7<1><default><default><1>   #num = 4

leds_used = 0#失能led系统配置,不失能也可以,但是LED在你打开./led_app后会被系统改变状态
leds_num = 4

驱动程序
1.insmod led_drvreg.ko //加载驱动
2../led_app            //同时点亮2个led

在注册设备和卸载驱动解释它的思想:
A. led_init编写顺序:
(1)映设虚拟地址,ioremap
(2)注册结构体,告诉内核leddrv_fops
(3)申请设备类class_create
(4)生成设备节点device_create /dev/ledwht    #ls /dev/ledwht确实在加载驱动时候生成
B. led_exit编写顺序:
(1)卸载结构体unregister_chrdev
(2)删除设备节点device_destroy
(3)删除设备类class_destroy
(4)释放虚拟地址,ioremap

这个顺序就好比如你开电脑之前必须
1.开电源
2.按下电脑开机按钮
3.打开应用程序
-----------------
关闭电脑的顺序是
1.关闭应用程序
2.找到关机界面,确定关机
3.最后才是关闭电源。
是不是很有道理呢!!!







简单侶途  发表于 2015-11-13 16:07:16

本帖最后由 简单侶途  于 2015-11-13 16:12 编辑

第五个程序目的是控制不同的LED实现方法是利用程序里面申请主设备号:
major = register_chrdev(0, "led", &leddrv_fops);
利用自动分配申请的major主设备号,接着利用major下自我分配利用的次设备号实现匹配同一类的不同minor,系统自动分配完major后,默认分配次设备号minor = 0,接着我们就在minor那里作文章了,minor使用范围是0~255
这个minor就可以让我们识别同一类设备类下的不同led设备节点
下图所示:242为major,242后面的0,1,2就是minor了

1.驱动程序部分:#include<linux/fs.h>
#include<linux/module.h>
#include<linux/errno.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/device.h>
#include<linux/types.h>
#include <asm/gpio.h>
#include <linux/gpio.h>
#include <plat/sys_config.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/io.h>

#include <linux/moduleparam.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/mutex.h>
#include <linux/compat.h>


/*两种方法寻找GPIO寄存器地址*/
//#define PIO_BASE   0x01C20800
#define PH_BASE   (PIO_BASE + 7*0x24)

#define PH_CFG2   (PH_BASE + 0x08)
#define PH_DAT    (PH_BASE + 0x10)

//#define PH_BASE(PIO_BASE + 0xFC)
//#define PH_CFG2(PIO_BASE + 0x104)
//#define PH_DAT   (PIO_BASE + 0X10C)

static volatile unsigned int *ph_cfg2;
static volatile unsigned int *ph_dat;

//设备类
static struct class *leddrv_class;
//分配 cdev
static struct class_device *leddrv_class_dev;
//主设备号
static int major;

static int led_open(struct inode *inode, struct file *file)
{
      /*bit20,bit21
         *open the roange_led and blue_led GPIO output
         *Output is 001
         */

      *ph_cfg2 &= ~((7<<16) | (7<<20));//清零000,这个步骤很重要,注释掉可以看下效果如何blue_led不能控制了
      *ph_cfg2 |= ((1<<16) | (1<<20));//设置001
      return 0;
}


static ssize_t led_write (struct file *file,char __user *buf,
                                                size_t count,loff_t *f_pos)
{
      int val;
      int minor = MINOR(file->f_dentry->d_inode->i_rdev);

      /*1.从用户空间读取数据到内核空间*/
      copy_from_user(&val,buf,4);

      /*2.判断传入值val,进行GPIO电平控制*/
      switch (minor){
      case 0:      /*/dev/leds*/
      {
                if(val == 1)
                        *ph_dat |= ((1<<20) | (1<<21));               
                else
                        *ph_dat &= ~((1<<20) | (1<<21));
      }
      break;
      case 1:/*/dev/led1*/
      {
                if(val == 1)
            *ph_dat |= (1<<20);
            else
            *ph_dat &= ~(1<<20);
      }
      break;
      case 2: /*/dev/led2*/
      {
                if(val == 1)
            *ph_dat |=(1<<21);
      else
            *ph_dat &=~(1<<21);
      }
      break;
      }
      return 0;
}

//分配驱动操作
static struct file_operations leddrv_fops = {

      .owner   =    THIS_MODULE,
      .open         =    led_open,
      .write   =    led_write,
};

static intled_init(void)
{      
      int minor = 0;
      
      ph_cfg2 = ioremap(PH_CFG2, 4);
      ph_dat= ioremap(PH_DAT, 4);
      
         major = register_chrdev(0, "led", &leddrv_fops);
      leddrv_class = class_create(THIS_MODULE, "led");
      leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led0"); /* /dev/ledwht */
      
      for(minor=1;minor<3;minor ++)
                leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, minor), NULL,
      return 0;
}

static void led_exit(void)
{
      int minor;
      
      unregister_chrdev(major,"led");
      /*2.删除设备节点*/
      for(minor = 0; minor<3; minor++)
                device_destroy(leddrv_class, MKDEV(major, minor));
      class_destroy(leddrv_class);
      iounmap(ph_cfg2);
      iounmap(ph_dat);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
2.应用程序部分:#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/*测试方法
*/led_test on
*/led_test off
*/

int main(int argc, char *argv[])
{
      int fd;
      int val = 1;
      char* filename;

      if (argc != 3) {
      printf("usage:\n%s </dev/led?> <on|off>\n", argv);
      return 0;
      }
      filename = argv;

      fd = open(filename, O_RDWR);
      if (fd < 0) {
         printf("open led failed.\n");
         return 0;
    }
      if (strcmp(argv, "on") == 0) {
      //开灯
                val = 1;
      }
      else if (strcmp(argv, "off") == 0) {
      //关灯
                val = 0;
      }

      write(fd, &val, 4);
      return 0;
}
3.show图ing
操作步骤如下
LED操作显示





简单侶途  发表于 2015-11-9 12:10:34

本帖最后由 简单侶途  于 2015-11-9 12:11 编辑

Makefile文件,注意自己编译路径,/work/driver_ct/evm/linux-sunxiifneq ($(KERNELRELEASE),)
      obj-m:=led_drv1.o
else
      KERNELDIR:=/work/driver_ct/evm/linux-sunxi
      PWD:=$(shell pwd)
default:
      $(MAKE) ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -C $(KERNELDIR) M=$(PWD) modules
clean:
      rm -rf *.o *.mod.c *.mod.o *.ko .*.cmd modules.order Module.symvers .tmp_versions
endif

简单侶途  发表于 2015-11-9 12:11:23

简单侶途  发表于 2015-11-9 12:10 static/image/common/back.gif
Makefile文件,注意自己编译路径,/work/driver_ct/evm/linux-sunxi

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/*测试方法
*/led_test on
*/led_test off
*/

int main(int argc, char *argv[])
{
        int fd;
        int val = 1;
        int status = 0;
        fd = open("/dev/ledwht", O_RDWR);
        if (fd < 0) {
                printf("open led failed.\n");
                return -1;
        }

        if (argc != 2) {
      printf("usage:\n%s <on|off>\n", argv);
      return -1;
        }

        if (strcmp(argv, "on") == 0) {
        //开灯
                val = 1;
        }
        else if (strcmp(argv, "off") == 0) {
        //关灯
                val = 0;
        }

        write(fd, &val, 4);
        return 0;
}

简单侶途  发表于 2015-11-9 12:12:31

APP:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/*测试方法
*/led_test on
*/led_test off
*/

int main(int argc, char *argv[])
{
        int fd;
        int val = 1;
        int status = 0;
        fd = open("/dev/ledwht", O_RDWR);
        if (fd < 0) {
                printf("open led failed.\n");
                return -1;
        }

        if (argc != 2) {
      printf("usage:\n%s <on|off>\n", argv);
      return -1;
        }

        if (strcmp(argv, "on") == 0) {
        //开灯
                val = 1;
        }
        else if (strcmp(argv, "off") == 0) {
        //关灯
                val = 0;
        }

        write(fd, &val, 4);

        return 0;
}

简单侶途  发表于 2015-11-9 12:12:43

APP:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/*测试方法
*/led_test on
*/led_test off
*/

int main(int argc, char *argv[])
{
        int fd;
        int val = 1;
        int status = 0;
        fd = open("/dev/ledwht", O_RDWR);
        if (fd < 0) {
                printf("open led failed.\n");
                return -1;
        }

        if (argc != 2) {
      printf("usage:\n%s <on|off>\n", argv);
      return -1;
        }

        if (strcmp(argv, "on") == 0) {
        //开灯
                val = 1;
        }
        else if (strcmp(argv, "off") == 0) {
        //关灯
                val = 0;
        }

        write(fd, &val, 4);

        return 0;
}

简单侶途  发表于 2015-11-11 11:32:28

接帖上的更新另外两种方法操作寄存器来实现控制GPIO
1.使用ioctl函数方便的操作GPIO
首先指出,我前面两个驱动程序只能传入一个参数值
app(val)---->   driver(copy_from_user)
所以我们只能控制LED的亮灭,要实现先进的选择控制哪一个LED的话最好是传入2个参数值
(1)指定操作哪个LED(2)点亮还是熄灭
2.关键部分看ioctl函数即可#include<linux/fs.h>
#include<linux/module.h>
#include<linux/errno.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/device.h>
#include<linux/types.h>
#include <asm/gpio.h>
#include <linux/gpio.h>
#include <plat/sys_config.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/moduleparam.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/mutex.h>
#include <linux/compat.h>
#define PH_BASE   (PIO_BASE + 7*0x24)
#define PH_CFG2   (PH_BASE + 0x08)
#define PH_DAT    (PH_BASE + 0x10)

static volatile unsigned int *ph_cfg2;
static volatile unsigned int *ph_dat;

//设备类
static struct class *leddrv_class;
//分配 cdev
static struct class_device *leddrv_class_dev;
//主设备号
static int major;

static int led_open(struct inode *inode, struct file *file)
{
        /*bit20,bit21
   *open the roange_led and blue_led GPIO output
   *Output is 001
   */
   *ph_cfg2 &= ~((7<<16) | (7<<20));//清零000,这个步骤很重要,注释掉可以看>    下效果如何blue_led不能控制了
   *ph_cfg2 |= ((1<<16) | (1<<20));//设置001
   return 0;
}

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

   switch(cmd){
   case 1:
            if(arg == 'a')
            *ph_dat |= (1<<20);
            else if(arg == 'b')
            *ph_dat |= (1<<21);
                 break;
   case 0:
            if(arg == 'a')
            *ph_dat &= ~(1<<20);
            else if(arg == 'b')
            *ph_dat &= ~(1<<21);
            break;
   default:
            break;
    }
    return 0;
}

static struct file_operations leddrv_fops = {
//分配驱动操作

        .owner   =    THIS_MODULE,
        .open       =    led_open,
        .unlocked_ioctl   =leds_ioctl,
};

static intled_init(void)
{
        ph_cfg2 = ioremap(PH_CFG2, 4);
        ph_dat= ioremap(PH_DAT, 4);
       
        major = register_chrdev(0, "led", &leddrv_fops);
        leddrv_class = class_create(THIS_MODULE, "led");
        leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "ledwht"); /* /dev/ledwht */
        return 0;
}

static void led_exit(void)
{
        unregister_chrdev(major, "led");
        /*2.删除设备节点*/
        device_destroy(leddrv_class, MKDEV(major, 0));
        class_destroy(leddrv_class);
        iounmap(ph_cfg2);
        iounmap(ph_dat);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
3.应用程序也是重点看ioctl函数#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        intfd;
        unsigned intcmd = 1;
        char val = 'z';
        fd = open("/dev/ledwht", O_RDWR);
        if (fd < 0) {
                printf("open led failed.\n");
                return -1;
        }

        if (argc != 3) {
      printf("usage:\n%s <led1|led2> <on|off>\n", argv);
      return -1;
        }

        if (strcmp(argv, "on") == 0) {
        //开灯
                cmd = 1;
        }
        else if (strcmp(argv, "off") == 0) {
        //关灯
                cmd = 0;
        }
        if(strcmp(argv,"led1") == 0)
                val = 'a';
    else if(strcmp(argv,"led2") == 0)
                val = 'b';
        ioctl(fd,cmd,val);
//        write(fd, &val, 4);
        return 0;
}

简单侶途  发表于 2015-11-11 12:07:35

首先指出平台总线驱动的方法和以上方法差别有点大,不过重点差别还是比较明显,如果前面看懂了,这个程序估计不难{:soso_e100:}
1.学习参考资料,可以粗略参考
http://blog.csdn.net/lwj103862095/article/details/17842437 //讲述大体驱动框架
http://bbs.ednchina.com/BLOG_ARTICLE_3008325.HTM      //platform_get_resource难点理解,很有必要看看具体细节
a.平台设备程序#include <linux/module.h>
#include <linux/version.h>      
#include <linux/init.h>      
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

#include<linux/uaccess.h>
#include<linux/device.h>
#include <asm/gpio.h>
#include <linux/gpio.h>
#include <plat/sys_config.h>
#include <asm/io.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>

#define PH_BASE   (PIO_BASE + 7*0x24)
#define PH_CFG2   (PH_BASE + 0x08)
#define PH_DAT    (PH_BASE + 0x10)

      
static struct resource led_resources[] = {
       = {
            .start= PH_CFG2,              /*起始地址*/
            .end    = PH_CFG2 + 4 - 1,      
            .flags= IORESOURCE_MEM,
      },
                = {
              .start= 5,                      /* LED-blue*/
              .end    = 5,
              .flags= IORESOURCE_IRQ,
    },
};
      
static void led_release(struct device * dev)
{
}
            
static struct platform_device led_device = {
      .id             =-1,
      .name         ="ledwht",/* 与led_driver的name一致 */
      .resource       =led_resources,
      .num_resources=ARRAY_SIZE(led_resources),
      .dev            ={
      .release      =led_release,
      },
};
      
    /* 分配/设置/注册一个platform_device */
static int led_dev_init(void)
{
      return platform_device_register(&led_device);
}
      
static void led_dev_exit(void)
{
      platform_device_unregister(&led_device);
}
      
module_init(led_dev_init);
module_exit(led_dev_exit);
      
MODULE_LICENSE("GPL");b.平台驱动程序#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <asm/uaccess.h>   // copy_from_user
#include <asm/io.h>// ioremap

static struct class *leds_class;

static volatile unsigned int *ph_cfg2;
static volatile unsigned int *ph_dat;
static int led_pin;//定义点亮某一个灯的引脚

static int major;

static int led_open(struct inode * inode, struct file * filp)
{
       /*bit20,bit21
          *open the roange_led and blue_led GPIO output
          *Output is 001
          */
        //*ph_cfg2 &= ~((7<<16) | (7<<20));
        *ph_cfg2 &= ~(7<<led_pin*4);//清零000,这个步骤很重要,注释掉可以看下效果如何blue_led不能控制了

        *ph_cfg2 |= ((1<<led_pin*4));//设置001
       
        return 0;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        int val;
        copy_from_user(&val, buf, count);
        if(val == 1)
        {
                /* 点灯 */
                *ph_dat |= (1<<(led_pin+16));       
        }
        else
        {
                /* 灭灯 */
                *ph_dat &= ~(1<<(led_pin+16));
        }
        return 0;
}

/* File operations struct for character device */
static const struct file_operations led_fops = {
        .owner                = THIS_MODULE,
        .open                = led_open,
        .write      = led_write,
};

static int __devinit led_probe(struct platform_device *pdev)
{
        struct resource *res;
      
    /* 根据platform_device的资源进行ioremap */
    res   = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ph_cfg2 = ioremap(res->start, res->end - res->start + 1);
    ph_dat= ph_cfg2 + 2;

        res   = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    led_pin =res->start;
       
        /* 注册字符设备 */
        major   = register_chrdev(0, "ledwht", &led_fops);
        /*申请一个设备类*/
        leds_class = class_create(THIS_MODULE,"ledwht");
        /*申请设备节点*/
        device_create(leds_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led*/
        return 0;
}
static int __devexit led_remove(struct platform_device *pdev)
{
        /*释放设备节点,卸载类*/
        device_destroy(leds_class, MKDEV(major, 0));
        class_destroy(leds_class);
    /*卸载字符设备*/
        unregister_chrdev(major, "ledwht");
        iounmap(ph_cfg2);
        return 0;
}

static struct platform_driver leds_driver = {
        .probe                =         led_probe,
        .remove                =         led_remove,
        .driver                =         {
        .name                =        "ledwht",
        .owner                =         THIS_MODULE,
        }
};

/* 分配/设置/注册一个platform_driver */
static int led_drv_init(void)
{
        return platform_driver_register(&leds_driver);
}

static void led_drv_exit(void)
{
        platform_driver_unregister(&leds_driver);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("wht");c.应用程序直接可以自己写了吧,可以直接套用前两个程序的APP

2.平台总线重点讲解部分:
写代码之前,必须看清楚寄存器地址的规律,编程需要用到。



(1)首先说明平台总线目的是把控制的设备和驱动程序分离的思想,你要控制哪一个设备的时候,就可以直接修改设备总线程序的设备号就可以简单地切换到控制其他设备了。
(2) #define PH_CFG2      (PH_BASE + 0x08)
         #define PH_CFG3    (PH_BASE + 0x0C)   //解释的需要,添加的,程序里面没有
         #define PH_DAT         (PH_BASE + 0x10)

    static struct resource led_resources[] = {
         【0] = {
               .start= PH_CFG2,              /*起始地址*/
               .end    = PH_CFG2 + 4 - 1,         //为啥是4-1?,地址是从0开始的,0~3就为4byte   所以4-1=3
                                                   由PH_CFG3-PH_CFG2可以看出PH_CFG2占用地址范围为0x0C -0x08 = 0x04
                .flags= IORESOURCE_MEM,//表示IO资源
         },
                    = {
                 .start= 5,                      /* led-blue*//* 点亮led_orange的话,改为4即可*/
                    .end    = 5,
                 .flags= IORESOURCE_IRQ,   ///表示类似设备号吧,我觉得而已
       },
};
(3)再看这部分,两个都是相辅相成的
static int __devinit led_probe(struct platform_device *pdev)
{
        struct resource *res;
      
    /* 根据platform_device的资源进行ioremap */
    res   = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ph_cfg2 = ioremap(res->start, res->end - res->start + 1);
                  //映射范围:start~~~~end - start+1;为啥+1,我也不太懂
    ph_dat= ph_cfg2 + 2;
                //这里注意了,为啥是2呢?ph_dat和ph_cfg2之间隔了一个ph_cfg3对吧?
               //那好办了,只要你懂得指针指向另外偏移地址的方法的话,你可以知道,
               ph_cfg2+1=>ph_cfg3ph_cfg3+1=>ph_dat

        res   = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    led_pin =res->start;
       
        /* 注册字符设备 */
        major   = register_chrdev(0, "ledwht", &led_fops);
        /*申请一个设备类*/
        leds_class = class_create(THIS_MODULE,"ledwht");
        /*申请设备节点*/
        device_create(leds_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led*/
        return 0;
}


yulin 发表于 2015-11-11 12:58:58

韦东山的视屏能分享下么    如果不能就算了:D

简单侶途  发表于 2015-11-11 14:44:09

yulin 发表于 2015-11-11 12:58 static/image/common/back.gif
韦东山的视屏能分享下么    如果不能就算了

可以啊,不过只能看第一期的,二三期还是要买一下吧!最后面几个视频才是开始讲驱动的
https://100ask.taobao.com/p/rd974532.htm?spm=a1z10.1-c.w5001-1253507018.10.pfpA62&scene=taobao_shop

满月 发表于 2015-11-11 20:32:23

666666 有深度!
页: [1] 2
查看完整版本: 五种方法点亮LED,入门驱动程序,更新完毕