• 正文
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard ELF 1板卡-platform驱动控制LED

04/02 10:40
793
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码6_platformplatform_led

下面以控制开发板上的LED_R为例进行讲解。

修改设备树

(一)查看原理图引脚复用表格,可以得到LED_R由GPIO1_10控制,所以我们需要配置GPIO1_10引脚为输出,而且能够在用户空间控制它输出高电平还是低电平。

(二)在设备树arch/arm/boot/dts/imx6ull-elf1-emmc.dts中添加leds节点和引脚复用,并检查设备树中是否有其它的地方也用到了此引脚,如果用到了就需要将其屏蔽掉,避免复用冲突。

添加节点:

leds {

                compatible = "platform_led";

                pinctrl-names = "default";

                pinctrl-0 = <&pinctrl_led>;

                gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;

        };

添加后效果如下:

添加复用,在iomux中新建pinctrl_led:

pinctrl_led:ledgrp {

                        fsl,pins = <

                                MX6UL_PAD_JTAG_MOD__GPIO1_IO10        0x10b0

                        >;

                };

添加后效果如下:

(三)编译设备树:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs

编译生成的设备树文件为imx6ull-elf1-emmc.dtb,参考《01-0 ELF1、ELF1S开发板_快速启动手册_V1》4.4节单独更新设备树。

驱动源码platform_led.c编写

(一)头文件引用

#include <linux/init.h>

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/platform_device.h>

#include <linux/miscdevice.h>

#include <linux/device.h>       //包含设备节点相关的头文件

#include <linux/gpio.h>         //包含gpio操作函数的相关头文件

#include <linux/of_gpio.h>

#include <linux/of.h>

(二)创建相关宏定义

#define DEVICE_NAME "my_device"

#define LED_IOC_MAGIC 'k'

#define SET_LED_ON _IO(LED_IOC_MAGIC, 0)

#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1)

 

struct device_node *node;   //设备树节点

int gpio;   //gpio编号

这里同样使用ioctl来控制,ioctl相关的定义本节不在重复讲解。

(三)定义platform_driver类型结构体

static struct platform_driver my_platform_driver = {

    .driver = {

        .name = "my_platform_driver",

        .owner = THIS_MODULE,

        .of_match_table = of_platform_match,

    },

    .probe = my_platform_probe,

    .remove = my_platform_remove,

};

(四)定义of_platform_match,用来与设备树中的compatible匹配,匹配成功后才会进入到probe函数中

static const struct of_device_id of_platform_match[] = {

        { .compatible = "platform_led", },

        {},

};

(五)probe函数的实现

static int my_platform_probe(struct platform_device *pdev)

{

        struct device *dev = &pdev->dev;

        int ret;

        //注册杂项设备

        ret = misc_register(&my_misc_device);

        if (ret) {

        pr_err("Failed to register misc devicen");

        return ret;

        }

        // 获取设备节点

        node = of_find_node_by_name(NULL,"leds");

        // 获取 gpios 属性中的引脚编号

        gpio = of_get_named_gpio(node, "gpios", 0);

        //判断是否获取成功

         if (!gpio_is_valid(gpio)) {

         pr_err("Failed to get GPIOn");

         return -1;

        }

 

        gpio_free(gpio);

        if (gpio_request(gpio, "led_run")) {

                printk("request %s gpio faile n", "led_run");

                 return -1;

         }

        printk(KERN_INFO "my_platform_probe: Platform device probedn");

    return 0;

}

(1)misc_register()用户注册杂项设备,原型如下:

int misc_register(struct miscdevice *misc);

参数说明:

misc:指向struct miscdevice结构体的指针,表示要注册的杂项设备。

返回值是一个整数,表示注册结果。如果注册成功,函数会返回0;如果注册失败,函数会返回一个负数,表示错误代码。

struct miscdevice结构体用于描述杂项设备的属性,包括设备的次设备号(minor number)、设备名称、设备文件操作函数等。在注册杂项设备之前,需要先初始化struct miscdevice 结构体的字段,然后将其作为参数传递给 misc_register()函数。

(2)of_find_node_by_name()用于查找设备树中的节点,原型如下:

struct device_node *of_find_node_by_name(struct device_node *prev, const char *name);

参数说明:

prev:指向前一个设备树节点的指针。如果要从整个设备树中开始查找节点,则可以传递NULL。

name:字符串,表示要查找的设备树节点的名称。

返回值是一个指向找到的设备树节点的指针。如果找到匹配的节点,则返回该节点的指针;如果未找到匹配的节点,则返回 NULL。

(3)of_get_named_gpio()用于从设备树中获取GPIO引脚编号,原型如下:

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

参数说明:

np:指向设备树节点的指针,表示要从该节点获取 GPIO 引脚。

propname:字符串,表示要获取的 GPIO 引脚属性的名称。

index:整数,表示在属性中指定的 GPIO 引脚的索引。如果属性中有多个 GPIO 引脚定义,可以通过指定索引来获取其中的一个引脚。

返回值是一个整数,表示获取到的 GPIO 引脚编号。如果获取失败,函数会返回一个负数。

(六)miscdevice类型结构体定义

static struct miscdevice my_misc_device = {

    .minor = MISC_DYNAMIC_MINOR,   // 动态分配次设备号

    .name = DEVICE_NAME,           // 设备名称

    .fops = &my_device_fops,          // 设备文件操作函数

};

(七)文件操作函数,file_operations类型结构体定义

static struct file_operations my_device_fops = {

    .owner = THIS_MODULE,

    .open = my_device_open,

    .release = my_device_release,

    .unlocked_ioctl = myled_ioctl,

};

(八)文件操作函数实现:

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

{

// 打开设备的操作

//将引脚配置为输出

    gpio_direction_output(gpio, 1);

    printk(KERN_INFO "This is device_open.n");

        return 0;

}

 

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

{

 

    switch (cmd) {

            case SET_LED_ON:

                printk(KERN_INFO "This is set_led_on.n");

            // 设置GPIO引脚为低电平

            gpio_set_value(gpio, 0);

            break;

 

        case SET_LED_OFF:

          //设置GPIO引脚为高电平

          printk(KERN_INFO "This is set_led_off.n");

          gpio_set_value(gpio, 1);

            break;

 

        default:

            return -ENOTTY;

    }

 

    return 0;

}

 

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

{

    // 关闭设备的操作

    return 0;

}

文件操作函数同样使用ioctl来实现,与之前的LED操作一样,接收到SET_LED_ON命令后将引脚拉低,接收到SET_LED_OFF命令后,将引脚电平拉高。

完整的驱动platform_led.c示例源码

#include <linux/init.h>

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/platform_device.h>

#include <linux/miscdevice.h>

#include <linux/device.h>       //包含设备节点相关的头文件

#include <linux/gpio.h>         //包含gpio操作函数的相关头文件

#include <linux/of_gpio.h>

#include <linux/of.h>

 

#define DEVICE_NAME "my_device"

#define LED_IOC_MAGIC 'k'

#define SET_LED_ON _IO(LED_IOC_MAGIC, 0)

#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1)

 

struct device_node *node;   //设备树节点

int gpio;   //gpio编号

 

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

{

// 打开设备的操作

//将引脚配置为输出

    gpio_direction_output(gpio, 1);

    printk(KERN_INFO "This is device_open.n");

        return 0;

}

 

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

{

 

    switch (cmd) {

            case SET_LED_ON:

                printk(KERN_INFO "This is set_led_on.n");

            // 设置GPIO引脚为低电平

            gpio_set_value(gpio, 0);

            break;

 

        case SET_LED_OFF:

          //设置GPIO引脚为高电平

          printk(KERN_INFO "This is set_led_off.n");

          gpio_set_value(gpio, 1);

            break;

 

        default:

            return -ENOTTY;

    }

 

    return 0;

}

 

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

{

    // 关闭设备的操作

    return 0;

}

 

static struct file_operations my_device_fops = {

    .owner = THIS_MODULE,

    .open = my_device_open,

    .release = my_device_release,

    .unlocked_ioctl = myled_ioctl,

};

 

static struct miscdevice my_misc_device = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = DEVICE_NAME,

    .fops = &my_device_fops,

};

 

static int my_platform_probe(struct platform_device *pdev)

{

        struct device *dev = &pdev->dev;

        int ret;

        //注册杂项设备

        ret = misc_register(&my_misc_device);

        if (ret) {

        pr_err("Failed to register misc devicen");

        return ret;

        }

        // 获取设备节点

        node = of_find_node_by_name(NULL,"leds");

        // 获取 gpios 属性中的引脚编号

        gpio = of_get_named_gpio(node, "gpios", 0);

        //判断是否获取成功

         if (!gpio_is_valid(gpio)) {

         pr_err("Failed to get GPIOn");

         return -1;

        }

 

        gpio_free(gpio);

        if (gpio_request(gpio, "led_run")) {

                printk("request %s gpio faile n", "led_run");

                 return -1;

         }

        printk(KERN_INFO "my_platform_probe: Platform device probedn");

    return 0;

}

static int my_platform_remove(struct platform_device *pdev)

{

        misc_deregister(&my_misc_device);

 

        printk(KERN_INFO "my_platform_remove: Platform device removedn");

    return 0;

}

 

static const struct of_device_id of_platform_match[] = {

        { .compatible = "platform_led", },

        {},

};

 

static struct platform_driver my_platform_driver = {

    .driver = {

        .name = "my_platform_driver",

        .owner = THIS_MODULE,

        .of_match_table = of_platform_match,

    },

    .probe = my_platform_probe,

    .remove = my_platform_remove,

};

 

module_platform_driver(my_platform_driver);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Your Name");

MODULE_DESCRIPTION("Platform Driver Example");

编译

复制7.7.2驱动中的Makefile文件,将其中的platform.o修改为platform_led.o,效果如下:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/06_platform/platform_led$ make                          

将编译生成的platform_led.ko模块拷贝到开发板。

测试

测试app使用之前章节的app即可。

root@ELF1:~# insmod platform_led.ko

my_platform_probe: Platform device probed

root@ELF1:~# ./led_app

This is device_open.

This is set_led_on.

This is set_led_off.

This is set_led_on.

This is set_led_off.

This is set_led_on.

This is set_led_off.

This is set_led_on.

This is set_led_off.

This is set_led_on.

This is set_led_off.

root@ELF1:~# rmmod platform_led.ko

my_platform_remove: Platform device removed

此时可以看到开发板上的红色LED,循环闪烁5次。

相关推荐