led驱动——gpio和pinctrl子系统

本文介绍了如何在Linux系统中使用pinctrl控制GPIO引脚,涉及设备树文件的修改、platform_driver的实现以及GPIO操作函数的应用,如gpiod_get、direction_output等。
摘要由CSDN通过智能技术生成

韦东山老师b站视频地址

pinctrl

        pinctrl是用来指定引脚复用的和配置引脚(上下拉等等),引脚上的功能有很多,比如,串口,i2c等等。pinctrl语法每个芯片公司的都有所不同。

主要参考文档:在linux内核源码Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt

修改设备树文件

    my_led{
        compatible="ss,led";
        pinctrl-names = "default";
        pinctrl-0 = <&my_pinctrl_leds>;
        led-gpios= <&gpio5 3 GPIO_ACTIVE_LOW>;
    };
&iomuxc_snvs {
    pinctrl-names = "default_snvs";
    pinctrl-0 = <&pinctrl_hog_2>;
    imx6ul-evk {
        pinctrl_hog_2: hoggrp-2 {
            fsl,pins = <
                MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09     0x1b0b0 /* enet1 reset */
                MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06     0x1b0b0 /* enet2 reset */
                MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01     0x000110A0 /*key 1*/
            >;
        };

        //添加节点
        my_pinctrl_leds: my_pinctrl_leds {
            fsl,pins = <
                  MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03        0x000110A0
            >;
        };

        
    };
};

驱动文件

 1.先定义paltform_driver变量及初始化

static const struct of_device_id ss_led[]={
    {.compatible="ss,led"},
    {},
};


static struct platform_driver chip_demo_led_driver={
    .probe=led_probe,
    .remove=led_remove,
    .driver={
        .name="ss,led",
        .of_match_table=ss_led,
    },
};

 2.init,exit,出口入口函数,在入口函数中注册platform_driver和在出口函数中反注册platform_driver

static int __init led_init(void){
    return platform_driver_register(&chip_demo_led_driver);
}

static void __exit led_exit(void){
    platform_driver_unregister(&chip_demo_led_driver);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL"); 

  3.定义file_operations具体变量

static struct file_operations led_operations={
    .owner=THIS_MODULE,
    .write=led_write,
    .open=led_open,
};

4.probe函数编写 

static int led_probe(struct platform_device *pplat_dev){
    led_gpio_desc=gpiod_get(&(pplat_dev->dev),"led",0);
    if (IS_ERR(led_gpio_desc)) {
		dev_err(&(pplat_dev->dev), "Failed to get GPIO for led\n");
		return PTR_ERR(led_gpio_desc);
	}
    major=register_chrdev(0,"ss_led",&led_operations);
    
    led_class=class_create(THIS_MODULE,"ss_led");
    if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "ss_led");
		gpiod_put(led_gpio_desc);
		return PTR_ERR(led_class);
	}

    device_create(led_class,NULL,MKDEV(major,0),NULL,"ss_led0");
    return 0;
}

(1)获取引脚信息

//全局变量定义 static struct gpio_desc *led_gpio_desc;
//gpio_desc gpio描述符
led_gpio_desc=gpiod_get(&(pplat_dev->dev),"led",0);

(2)注册file_operations

  (3)   class_create和device_create

 5.remove函数编写

static int led_remove(struct platform_device *pplat_dev){
    device_destroy(led_class,MKDEV(major,0));
    class_destroy(led_class);
    unregister_chrdev(major,"ss_led");
    gpiod_put(led_gpio_desc); //释放引脚
    return 0;
}

6.open 和write函数编写

static int led_open (struct inode * inode, struct file * file){
    int minor=iminor(inode);
//设置引脚为输出,初始时为逻辑0电平
    gpiod_direction_output(led_gpio_desc,0);
    return 0;
}

static ssize_t led_write (struct file * file,  const char __user * user_buf, size_t size, loff_t *off_t){
    int minor=iminor(file->f_inode);
    char state;
    int err=copy_from_user(&state,user_buf,1);
    //设置引脚
    gpiod_set_value(led_gpio_desc,state);
    return 0;
}

7.要引用头文件

#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>

常用gpiod相关函数

gpiod_get_index

struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
					       const char *con_id,
					       unsigned int idx,
					       enum gpiod_flags flags);

        其中con_id是指设备树中的gpios的名字,如下图的"led"。

        led-gpios= <&gpio1 1 GPIO_ACTIVE_LOW
                    &gpio4 3 GPIO_ACTIVE_LOW
                    &gpio5 3 GPIO_ACTIVE_LOW>;

        idx是第几个引脚,因为一次获得一个引脚描述符,但是有三个,这里的led引脚是GPIO5_3,所以idx是2

led_gpio_desc=gpiod_get_index(&(pplat_dev->dev),"led",2,0);

        flags是配置引脚的状态,传进去在获取gpio描述符的同时设置引脚的输入,输出,这里设置为0,不配置。

/**
 * Optional flags that can be passed to one of gpiod_* to configure direction
 * and output value. These values cannot be OR'd.
 */
enum gpiod_flags {
	GPIOD_ASIS	= 0,
	GPIOD_IN	= GPIOD_FLAGS_BIT_DIR_SET,
	GPIOD_OUT_LOW	= GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
	GPIOD_OUT_HIGH	= GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |
			  GPIOD_FLAGS_BIT_DIR_VAL,
};

gpiod_count

int gpiod_count(struct device *dev, const char *con_id)

        获得设备树中指定名字的引脚数量,比如上面的"led-gpios"有3个引脚信息,调用gpiod_count就可以知道有3个引脚了。

gpiod_get

struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id,
					 enum gpiod_flags flags)
{
	return gpiod_get_index(dev, con_id, 0, flags);
}

        gpio_get是获取led-gpios中的第一个gpio描述符。

gpiod_direction_input

int gpiod_direction_input(struct gpio_desc *desc)

        设置引脚为输入。

gpiod_direction_output

int gpiod_direction_output(struct gpio_desc *desc, int value)
{
	VALIDATE_DESC(desc);
	if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
		value = !value;
	return _gpiod_direction_output_raw(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_direction_output);

        设置引脚为输出,value为引脚初始化逻辑电平。

gpiod_get_value

int gpiod_get_value(const struct gpio_desc *desc)

        返回逻辑电平。

gpiod_set_value

void gpiod_set_value(struct gpio_desc *desc, int value)

        设置引脚输出逻辑电平。

gpiod_put

void gpiod_put(struct gpio_desc *desc)
{
	gpiod_free(desc);
}
EXPORT_SYMBOL_GPL(gpiod_put);

        释放一个描述符。

gpiod_to_irq

int gpiod_to_irq(const struct gpio_desc *desc)

        获得引脚对应的中断号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值