Linux驱动开发:gpio子系统

目录

1、GPIO配置流程

2、GPIO子系统API

2.1 of_find_node_by_path

2.2 of_get_named_gpio

2.3 gpio_request 与 gpiod_get 与 gpiod_get_index

2.4 gpio_direction_input 与 gpiod_direction_input

2.5 gpio_direction_output 与 gpiod_direction_output

2.6 gpio_get_value 与 gpiod_get_value

2.7 gpio_set_value 与 gpiod_set_value

2.8  gpiod_get_from_of_node

2.9 gpio_free 与 gpiod_put

3、GPIO子系统驱动程序

3.1 没有platform总线前怎么得到gpio节点

3.1.1 修改设备树

3.1.2 驱动程序:老版接口

3.1.3 驱动程序:新版接口

3.2 platform总线下得到

3.2.1 修改设备树:配置pinctrl

3.2.2 修改设备树:配置自己的节点

3.2.3 驱动程序


1、GPIO配置流程

        如果要操作GPIO引脚的话,需要先将所用引脚配置为GPIO功能,这需要通过Pinctrl子系统来实现。

        在Pinctrl子系统将引脚配置为GPIO功能后,就可以使用GPIO子系统来设置GPIO的方向等。

        在设备树中,“GPIO组”就是一个GPIO Controller,这通常都由芯片厂家设置好。我们要做的是找到它名字,比如“gpio1”,然后指定要用它里面的哪个引脚,比如<&gpio1 0>。

        我们需要关心里面的这2个属性:

#gpio-controller; 表示这个节点是一个GPIO Controller,它下面有很多引脚。

#gpio-cells = <2>; 表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。

普遍的用法是,用第1个cell来表示哪一个引脚,用第2个cell来表示有效电平:

GPIO_ACTIVE_HIGH : 高电平有效

GPIO_ACTIVE_LOW : 低电平有效

可参考的帮助文档

Linux-4.9.88/Documentation/devicetree/bindings/gpio/gpio.txt 

Linux-4.9.88/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.txt

2、GPIO子系统API

2.1 of_find_node_by_path

struct device_node *of_find_node_by_path(const char *path)
/**/
功能:通过路径或者节点结构体
参数:
    @path:路径
返回值:成功返回节点的首地址,失败返回NULL

2.2 of_get_named_gpio

int of_get_named_gpio(struct device_node *np,const char *propname, int index)
/*
功能:根据节点结构体解析gpio编号
参数:
    @np:节点指针
	@propname:键
    @index:索引号
返回值:成功返回gpio的编号,失败返回错误码
*/

2.3 gpio_request 与 gpiod_get 与 gpiod_get_index

int gpio_request(unsigned gpio, const char *label)
/*
功能:申请要使用的gpio
参数:
    @gpio:gpio的编号
    @label:标签名
返回值:成功返回0,失败返回错误码
*/
//如果是一个GPIO就用gpiod_get,如果有多个就用gpiod_get_index
struct gpio_desc *__must_check gpiod_get(struct device *dev, 
    const char *con_id, enum gpiod_flags flags)

struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
					       const char *con_id,
					       unsigned int idx,
					       enum gpiod_flags flags)
/*
功能:申请要使用的gpio
参数:
    @dev: device结构体,如果有platform,则 &pdev->dev
    @con_id: 键
    @idx: 索引号
    @flags: 0
返回值:成功返回gpio_desc 结构体,失败返回错误码
*/

2.4 gpio_direction_input 与 gpiod_direction_input

int gpio_direction_input(unsigned gpio)
/*
功能:设置gpio的为输入
参数:
    @gpio:gpio的编号
返回值:成功返回0,失败返回错误码
*/
int gpiod_direction_input(struct gpio_desc *desc)
/*
功能:设置gpio为输入
参数:
    @desc:gpio_desc结构体
返回值:成功返回0,失败返回错误码
*/

2.5 gpio_direction_output 与 gpiod_direction_output

int gpio_direction_output(unsigned gpio, int value)
/*
功能:设置gpio的为输出
参数:
    @gpio:gpio的编号
    @value:默认电平的状态 1高电平  0低电平
返回值:成功返回0,失败返回错误码
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
/*
功能:设置gpio为输出
参数:
    @desc:gpio_desc结构体
返回值:成功返回0,失败返回错误码
*/

2.6 gpio_get_value 与 gpiod_get_value

int gpio_get_value(unsigned gpio)
/*
功能:读取管脚的值
参数:
    @gpio:gpio的编号
返回值:1高电平  0低电平
*/
int gpiod_get_value(const struct gpio_desc *desc)
/*
功能:读取管脚的值
参数:
    @desc: gpio_desc结构体
返回值:1高电平  0低电平
*/

2.7 gpio_set_value 与 gpiod_set_value

void gpio_set_value(unsigned gpio, int value)    
/*
功能:设置输出电平的值
参数:
     @gpio:gpio的编号
	 @value:1高电平  0低电平
返回值:无
*/
void gpiod_set_value(struct gpio_desc *desc, int value)
/*
功能:设置输出电平的值
参数:
    @desc: gpio_desc结构体
    @value: 1高电平  0低电平
返回值:无
*/

2.8  gpiod_get_from_of_node

struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, 
const char *propname, int index, enum gpiod_flags dflags, const char *label)
/*
功能:在设备树节点信息结构体中获取并申请要使用的gpio编号,然后输出
    此函数一个函数就完成了申请加上输出的功能
参数:
    @node:设备树节点信息结构体指针
    @propname:键名
    @index:索引
    @dflags:gpio状态值
        GPIOD_OUT_LOW
        GPIOD_OUT_HIGH
    @label:标签,填写NULL
返回值:成功返回gpio描述结构体指针,失败返回错误码指针
*/

2.9 gpio_free 与 gpiod_put

void gpio_free(unsigned gpio)
/*
功能:释放gpio
参数:
     @gpio:gpio的编号
返回值:无   
*/
void gpiod_put(struct gpio_desc *desc)
/*
功能:释放gpio
参数:
     @desc:gpio_desc结构体
返回值:无   
*/

3、GPIO子系统驱动程序

3.1 没有platform总线前怎么得到gpio节点

3.1.1 修改设备树

设备树文件在

arch/arm/boot/dts/100ask_imx6ull-14x14.dts

 在根节点下写自己的节点

myleds{ 
	led1=<&gpioe 10 0>; 
	led2=<&gpiof 10 0>; 
	led3=<&gpioe 8 0>;  
};

3.1.2 驱动程序:老版接口

struct device_node *node;
int gpiono;
static int __init mycdev_init(void)
{
	//1.获取节点
	node = of_find_node_by_path("/myleds");

	//2.解析得到gpio
	gpiono = of_get_named_gpio(node, "led1", 0);

	//3.申请使用的gpio
	gpio_request(gpio,NULL));

	//4.通过gpio申请设备
	gpio_direction_output(gpiono, 0);

	return 0;
}

static void __exit mycdev_exit(void) 
{
	gpio_set_value(gpiono,0);
	gpio_free(gpiono);
}

3.1.3 驱动程序:新版接口

static int __init mycdev_init(void)
{
	//1.获取节点
	node = of_find_node_by_path("/myleds");
	
    //2、申请GPIO
    //此函数一个函数就完成了申请加上输出的功能
	desc = gpiod_get_from_of_node(node, "led2", 0, GPIO_OUT_HIGH, 0);

	return 0;
}


static void __exit mycdev_exit(void) 
{
	gpiod_set_value(desc,0);
	gpiod_put(desc);
}

3.2 platform总线下得到

3.2.1 修改设备树:配置pinctrl

使用imx图形化界面来配置引脚

//在pinctrl下设置引脚
&iomuxc_snvs

myled_for_gpio_subsys: myled_for_gpio_subsys{       
    fsl,pins = <
        MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03        0x000110A0
    >;
};

3.2.2 修改设备树:配置自己的节点

在根节点下写自己的节点

myled {
    compatible = "100ask, leddrv" ;
    pinctrl-names = "default";
    pinctrl-0 = <&myLed_for_gpio_subsys>;
    led-gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
};

3.2.3 驱动程序

static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	gpiod_set_value(led_gpio, status);

}

static struct file_operations led_drv = {
	.write   = led_drv_write,
};

static int chip_demo_gpio_probe(struct platform_device *pdev)
{	    
    led_gpio = gpiod_get(&pdev->dev, "led", 0);
    register_chrdev(0, "100ask_led", &led_drv);
    	
    return 0;
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    unregister_chrdev(major, "100ask_led");
	gpiod_put(led_gpio);
    
    return 0;
}


static const struct of_device_id ask100_leds[] = {
    { .compatible = "100ask,leddrv" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
        .of_match_table = ask100_leds,
    },
};

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

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

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux GPIO子系统是一个用于控制嵌入式系统中通用输入/输出(GPIO)的软件子系统。它提供了一种标准的接口,使得应用程序可以通过文件系统接口来访问GPIO。这个子系统可以用于控制各种设备,例如LED、按钮、传感器等等。如果你需要更多的信息,可以查看Linux内核文档。 ### 回答2: Linux GPIO子系统是一种用于管理通用输入输出(GPIO)引脚的软件层。GPIO引脚是一种通用可编程引脚,可以在嵌入式系统中用来通过读取输入或设置输出与外部设备进行通信。 Linux GPIO子系统负责将底层硬件 GPIO 引脚的操作抽象为文件系统的接口,使开发者可以通过读写文件的方式来访问和控制 GPIO 引脚。通过该子系统,可以实现对 GPIO 引脚的配置、读取和写入等操作,以满足不同应用下对 GPIO 的需求。 Linux GPIO子系统的核心是GPIO驱动程序,它与底层硬件层进行交互,完成对GPIO引脚的操作。驱动程序将GPIO引脚映射到内存,通过读写该内存地址即可对引脚进行操作。用户通过访问特定目录下的文件来和引脚进行交互,例如将引脚配置为输入模式、输出模式,以及读取或写入引脚的状态。 通过Linux GPIO子系统开发者可以方便地进行GPIO引脚的控制。可以根据不同的应用需求,灵活配置引脚的输入输出模式,监听引脚上的状态变化,并根据需要对其他外设进行控制。 总之,Linux GPIO子系统开发者提供了便捷的接口,使得在嵌入式系统中使用GPIO引脚更加简单和灵活。它允许开发者通过读写文件的方式访问和控制GPIO引脚,满足各种不同嵌入式应用对GPIO的需求。 ### 回答3: LinuxGPIO(General Purpose Input/Output)子系统是通过软件对硬件上的通用输入/输出引脚进行控制的一种机制。它使得开发者可以利用这些GPIO引脚实现各种功能,比如控制LED灯、读取外部传感器的数据等。 LinuxGPIO子系统提供了许多功能和接口来管理和操作GPIO。首先,它使用sysfs文件系统来组织GPIO资源的目录树,并通过文件的方式来读取和写入GPIO的状态。在/sys/class/gpio目录下,每个GPIO引脚都会有一个对应的目录,在该目录中的文件可以用于配置GPIO的方向(输入或输出)、读取和写入GPIO的电平状态。开发者可以使用命令行工具或者编程语言(如Python、C等)来操作这些文件,从而控制GPIO引脚的行为。 其次,LinuxGPIO子系统还提供了设备树(Device Tree)来描述硬件平台上的GPIO资源。设备树是一种描述硬件的数据结构,在启动时通过设备树绑定机制将设备树中定义的GPIO资源与内核驱动程序关联起来。这样,开发者就可以通过调用相应的驱动程序来控制GPIO引脚,而不需要手动操作sysfs文件系统。 此外,LinuxGPIO子系统还支持中断机制,可以让GPIO引脚在特定事件发生时触发中断。通过注册中断处理函数,开发者可以实现对GPIO输入信号的快速响应,提高系统的实时性。 总之,LinuxGPIO子系统开发者提供了一种方便且灵活的方式来控制硬件上的GPIO引脚。通过sysfs文件系统或设备树,开发者可以轻松地配置、读取和控制GPIO的状态,从而实现各种功能和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值