利用设备树和平台总线驱动led(GPIO)

测试平台是讯为的itop-4412开发板

驱动led步骤

步骤:

  • 修改设备树,添加led相关的节点,编译后烧录进板卡
  • 编写driver驱动代码,初始化platform_driver结构体,使of_match_table属性的compatible与设备树中的一致
  • 在驱动入口函数中,向平台注册driver
  • 匹配成功
  • 在probe函数里获取gpio编号(从设备树获取)
  • 向内核申请gpio
  • 设置gpio方向
  • 注册杂项设备
  • 编写文件操作集里的ioctl函数,控制gpio
  • 编写应用程序,打开对应杂项设备,使用ioctl控制led灯

常用的gpio函数

获取gpio编号

<linux/of_gpio.h>

int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index);

参数:节点,属性名,标号

返回:得到GPIO编号;负值表示失败

向内核申请GPIO

申请gpio,申请后其他进程就不能再申请这个gpio,相当于被占用

int gpio_request(unsigned gpio, const char *label);

参数:gpio编号,给这个gpio起的名字

返回值:0成功,其他值失败

操作GPIO

设置gpio方向

int gpio_direction_input(unsigned gpio);  //设置输入,参数是gpio编号,返回0成功
int gpio_direction_output(unsigned gpio, int value);

读写gpio的值

int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value); 

将gpio导出到用户空间

int gpio_export(unsigned gpio, bool direction_may_change);
//gpio编号,用户是否可以改变gpio的方向

导出后用户层/sys/class/gpio/目录下会有gpio+编号的目录,和在用户层export申请gpio效果类似

对应的取消导出为gpio_unexport函数

释放gpio

gpio_free(unsigned gpio);

1. 修改设备树

在设备树中添加:

taxue_leds:taxue_leds {
		compatible = "taxue_leds";
		gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
		gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;
		status = "disabled";
	};


&taxue_leds {
      status = "okay";
};

2. 赋值of_match_table

定义并初始化platform_driver结构体

#define DEVICE_NAME "taxue_leds"  //和设备树节点中的compatible一致
//平台device和设备树device可以通过match_table匹配
static const struct of_device_id of_leds_match[] = {
    {.compatible = DEVICE_NAME},
    {},
};

static struct platform_driver pdrv = {
    .probe = drProbe,
    .remove = drRemove,
    .driver = {
        .name = "DEVICE_NAME",    //如果使用设备树中的设备,则不根据name匹配
        .owner = THIS_MODULE,
        .of_match_table = of_leds_match,  //使用match_table进行匹配
    },
};

3. 注册平台driver

//这段写在驱动入口处	
	int ret=0;
    ret = platform_driver_register(&pdrv);
    if(ret < 0){
        printk("platform driver regist failed\n");
        return -1;
    }

4. 匹配

由平台总线匹配

5. 在probe函数里获取gpio编号

int ledgpios[2];

{
    led_node = of_find_node_by_path("/taxue_leds");  //从设备树路径获取节点
    if(led_node == NULL){
        printk("find node failed\n");
        return -1;
    }

    ledgpios[0] = of_get_named_gpio( led_node, "gpios1", 0);  //获取gpio编号,存在ledgpios[0]里
}

6. 向内核申请gpio

在probe函数里

	ret = gpio_request(ledgpios[0], "led0");   //向内核申请gpio
    if(ret != 0){
        printk("request led gpio 0 failed\n");
        return -1;
    }

7. 设置gpio方向

在probe函数里

gpio_direction_output(ledgpios[0],0);  //设置为输出,默认低电平

8. 注册杂项设备

在probe里注册

  • 初始化file_operations文件操作集
  • 初始化miscdevice(主要包含:设备节点名,文件操作集)
  • 调用misc_register函数注册杂项设备

9. 编写文件操作集里的ioctl函数

#define CMD_TEST_2 _IOW('A', 2, int)
//参数:句柄、接口命令、传递的数据
//返回值:
long misc_ioctl(struct file *fd, unsigned int cmd, unsigned long b){
    switch(cmd){
        case CMD_TEST_2:
            printk("CMD_TEST_2 date=%ld\n",b);
            if(b >0){
                gpio_set_value(ledgpios[0], 1);  //如果传入的值大于0就将gpio置高,led亮
            }else{
                gpio_set_value(ledgpios[0], 0);  //如果传入的值等于0就将gpio置低,led灭
            }
            break;
    }
    return 0;
}

10. 编写应用程序

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include <unistd.h>
#include<sys/ioctl.h>

#define CMD_TEST_2 _IOW('A', 2, int)

int main(int argc, char *argv[]){
    int fd=0;
    int i=0;

    fd = open("/dev/hello_led", O_RDWR); //设备名具体需要根据驱动注册设备节点时的名字
    if(fd < 0){
        printf("open failed\n");
        exit(1);
    }
    printf("open success\n");

    //让led间隔一秒闪烁,循环10次
    for(i=0;i<10;i++){
        ioctl( fd, CMD_TEST_2, 0);
        sleep(1);
        ioctl( fd, CMD_TEST_2, 1);
        sleep(1);
    }

    close(fd);
    return 0;
}

驱动完整代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

#include <linux/miscdevice.h>

#include <linux/of.h>
#include <linux/of_gpio.h>

#define CMD_TEST_0 _IO('A', 0)
#define CMD_TEST_2 _IOW('A', 2, int)

#define DEVICE_NAME "taxue_leds"
struct device_node *led_node=NULL;
int ledgpios[2];

int misc_open(struct inode *a,struct file *b){
    printk("misc open \n");
    return 0;
}

int misc_release (struct inode * a, struct file * b){
    printk("misc file release\n");
    return 0;
}

//参数:句柄、接口命令、传递的数据
//返回值:
long misc_ioctl(struct file *fd, unsigned int cmd, unsigned long b){
    printk("cmd type=%c\t nr=%d\t dir=%d\t size=%d\n", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));

    switch(cmd){
        case CMD_TEST_0:
            printk("CMD_TEST_0\n");
            gpio_set_value(ledgpios[0], 0);
            break;
        case CMD_TEST_2:
            printk("CMD_TEST_2 date=%ld\n",b);
            if(b >0){
                gpio_set_value(ledgpios[0], 1);
            }else{
                gpio_set_value(ledgpios[0], 0);
            }
            break;
    }
    return 0;
}

//文件操作集
static struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open  = misc_open,
    .release = misc_release,
    .unlocked_ioctl = misc_ioctl
};

static struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,    //自动分配从设备号
    .name  = "hello_led",          //设备节点名
    .fops  = &misc_fops             //文件操作集
};

int drProbe(struct platform_device *dev){
    int ret;
    printk("probe here\n");

    led_node = of_find_node_by_path("/taxue_leds");  //从设备树路径获取节点
    if(led_node == NULL){
        printk("find node failed\n");
        return -1;
    }

    ledgpios[0] = of_get_named_gpio( led_node, "gpios1", 0);  //获取gpio编号

    ret = gpio_request(ledgpios[0], "led0");   //向内核申请gpio
    if(ret != 0){
        printk("request led gpio 0 failed\n");
        return -1;
    }

    gpio_direction_output(ledgpios[0],0);  //设置为输出,默认低电平

    ret = misc_register(&misc_dev);   //注册杂项设备
    if(ret < 0){
        printk("misc regist failed\n");
        return -1;
    }

    printk("misc regist succeed\n");
    return 0;
}

int drRemove(struct platform_device *dev){
    gpio_free(ledgpios[0]);
    misc_deregister(&misc_dev);
    printk("driver remove\n");
    return 0;
}

//平台device和设备树device可以通过match_table匹配
static const struct of_device_id of_leds_match[] = {
    {.compatible = DEVICE_NAME},
    {},
};

static struct platform_driver pdrv = {
    .probe = drProbe,
    .remove = drRemove,
    .driver = {
        .name = "DEVICE_NAME",    //如果使用设备树中的设备,则不根据name匹配
        .owner = THIS_MODULE,
        .of_match_table = of_leds_match,  //使用match_table进行匹配
    },
};

static int driver_init_led(void){
    int ret=0;
    ret = platform_driver_register(&pdrv); //向平台注册driver
    if(ret < 0){
        printk("platform driver regist failed\n");
        return -1;
    }
    return 0;
}

static void driver_exit_led(void){
   platform_driver_unregister(&pdrv);
   printk("platform driver exit!\n");
}

module_init(driver_init_led);  //模块入口,加载驱动时执行参数内的函数
module_exit(driver_exit_led);  //模块出口,卸载模块时执行参数内的函数

MODULE_LICENSE("Dual BSD/GPL"); //遵循BSD和GPL开源许可
MODULE_AUTHOR("TAXUE");  //模块作者
posted @ 2021-10-11 23:48  WuYunTaXue  阅读(782)  评论(0编辑  收藏  举报