7.3 虚拟GPIO驱动

GPIO控制器驱动的核心

GPIO控制器驱动的核心就是创建struct gpio_chip对象,并初始化其中内容,然后利用devm_gpiochip_add_data或gpiochip_add_data注册GPIO控制器驱动(它会在内部创建并注册一个struct gpio_devic对象)

	//注册GPIO控制器驱动
	int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip, void *data)
	int gpiochip_add_data(struct gpio_chip *chip, void *data)
	//注销GPIO控制器驱动
	void gpiochip_remove(struct gpio_chip *chip)

驱动编写

GPIO控制器需要与pinctrl控制器驱动关联,这里会用到6.3 虚拟pinctrl驱动中的虚拟pinctrl驱动,并且还需要在 pinctrl 控制器驱动的 pinmux_ops 中实现 gpio_request_enable 或 request 函数、gpio_disable_free 或 free函数、pinctrl_gpio_direction_input 函数、 pinctrl_gpio_direction_output 函数。

设备树编写

设备树包括三个部分,分别是虚拟pinctr控制器l的节点、虚拟GPIO控制器节点设备、虚拟LED设备节点

	//虚拟pinctrl的节点
	virtual_pinctrl {
		compatible = "atk,virtual_pinctrl";
	
		//描述设备引脚的节点,节点内容通过dt_node_to_map解析到pinctrl_map中
		pinctrl_i2c: i2cgrp {
			functions = "af1", "af5";
			groups = "pin0", "pin1";
			configs = <0x1122 0x3344>;
		};
	};
	//虚拟GPIO节点
	virt_gpio: virtual_gpio {
		compatible = "atk,virtual_gpio";
		gpio-controller;
		#gpio-cells = <2>;
		ngpios = <6>;
		gpio-ranges = <&virt_pinctrl 0 0 6>;
	};
	//虚拟LED节点
	virt_led {
		compatible = "atk,led";
		led-gpios = <&virt_gpio 0 GPIO_ACTIVE_HIGH>;
		status = "okay";
		labe = "virtual";
	};

驱动代码编写

编写一个GPIO控制器驱动主要包括以下步骤

  1. 实现GPIO操作函数
  2. 创建并初始化struct gpio_chip对象
  3. 利用struct gpio_chip对象注册GPIO控制器驱动
#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/regmap.h>

//记录IO输入输出
static uint32_t direction = 0;
//记录IO电平状态
static uint32_t output = 0;

//获取GPIO方向,0输出,1输入
static int virtual_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
   printk("get pin direction %d, it's direction = %s\n", offset, (direction & (0x01 << offset)) ? "output" : "input");
   
   return (direction & (0x01 << offset)) ? 0 : 1;
}

//设置GPIO为输入
static int virtual_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
   printk("set pin %d as input\n", offset);
   
   direction &= ~(0x01 << offset);
   
   return 0;
}

//设置GPIO为输出,并输出高电平或者低电平
static int virtual_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
   printk("set pin %d as output %s\n", offset, value ? "high" : "low");
   
   direction |= 0x01 << offset;
   if(value)
   	output |= 0x01 << offset;
   else
   	output &= ~(0x01 << offset);
   
   return 0;
}

//获取GPIO电平状态
static int virtual_gpio_get(struct gpio_chip *chip, unsigned offset)
{
   printk("get pin value %d, it's value = %s\n", offset, (output & (0x01 << offset)) ? "high" : "low");
   
   return (output & (0x01 << offset)) ? 1 : 0;
}

//设置GPIO电平状态
static void virtual_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
   printk("set pin value %d as %s\n", offset, value ? "high" : "low");
   
   if(value)
   	output |= 0x01 << offset;
   else
   	output &= ~(0x01 << offset);
}

//配置 GPIO
static int virtual_gpio_set_config(struct gpio_chip *chip, unsigned offset, unsigned long config)
{
   printk("set pin %d config %ld\n", offset, config);
   
   return 0;
}

//设备和驱动匹配成功执行
static int virtual_probe(struct platform_device *pdev)
{
   int result;
   uint32_t value;
   struct gpio_chip *gpio_chip;

   printk("%s\n", __FUNCTION__);

   /* 分配gpio_chip */
   gpio_chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL);
   if(!gpio_chip) {
   	printk("alloc gpio_chip failed\r\n");
   	return -ENOMEM;
   }

   /* 设置gpio_chip */
   gpio_chip->parent = &pdev->dev;
   gpio_chip->owner = THIS_MODULE;
   gpio_chip->label = pdev->name;
   /* 设置函数,这里request直接调用gpiochip_generic_request,free直接调用gpiochip_generic_free */
   gpio_chip->request = gpiochip_generic_request;
   gpio_chip->free = gpiochip_generic_free;
   gpio_chip->get_direction = virtual_gpio_get_direction;
   gpio_chip->direction_input  = virtual_gpio_direction_input;
   gpio_chip->direction_output = virtual_gpio_direction_output;
   gpio_chip->get = virtual_gpio_get;
   gpio_chip->set = virtual_gpio_set;
   gpio_chip->set_config = virtual_gpio_set_config,
   
   /* 设置base、ngpio值,base==-1表示自动分配 */
   gpio_chip->base = -1;
   result = of_property_read_u32(pdev->dev.of_node, "ngpios", &value);
   if(result < 0) 
   {
   	printk("get ngpios failed\r\n");
   	return result;
   }
   gpio_chip->ngpio = value;

   /* 注册gpio_chip */
   return devm_gpiochip_add_data(&pdev->dev, gpio_chip, NULL);;
}

//设备或驱动卸载时执行
static int virtual_remove(struct platform_device *pdev)
{
   printk("%s\n", __FUNCTION__);
   return 0;
}

/* 匹配列表,用于设备树和平台驱动匹配 */
static const struct of_device_id virtual_of_match[] = {
   {.compatible = "atk,virtual_gpio"},
   { /* Sentinel */ }
};
/* 平台驱动 */
static struct platform_driver virtual_drv = {
   .driver = {
   	.name = "virtual_gpio",
   	.owner = THIS_MODULE,
   	.pm = NULL,
   	.of_match_table = virtual_of_match,
   },
   .probe = virtual_probe,
   .remove = virtual_remove,
};
static int __init virtual_drv_init(void)
{
   int result;

   printk("%s\r\n", __FUNCTION__);

   //注册平台驱动
   result = platform_driver_register(&virtual_drv);
   if(result != 0)
   	printk("add virtual driver failed\r\n");
   return result;
}

static void __exit virtual_drv_exit(void)
{
   printk("%s\r\n", __FUNCTION__);

   //注销平台驱动
   platform_driver_unregister(&virtual_drv);
}

module_init(virtual_drv_init);
module_exit(virtual_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("virtual_gpio_controller");

测试代码编写

测试代码使用使用Linux的GPIO驱动中的基于GPIO子系统的LED驱动即可

上机测试

  1. 修改设备树,然后编译设备树,并用新的设备树启动
  2. 这里下载代码并进行编译,然后拷贝到目标板
  3. 依次执行命令insmod virtual_pinctrl.ko、insmod virtual_gpio.ko、insmod led.ko,加载虚拟pinctrl驱动、虚拟GPIO驱动、LED驱动。
    在这里插入图片描述
  4. 执行命令echo 0 > /dev/virtual_led和echo 1 > /dev/virtual_led,通过调试输出可以发现LED驱动已经成功调用到GPIO驱动的接口
    在这里插入图片描述
    注意:卸载时必须按led.ko、virtual_gpio.ko、virtual_pinctrl.ko的顺序卸载,否则可能会报内存错误
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: GPIO是英文General Purpose Input/Output的缩写,翻译过来就是通用输入输出。Linux内核提供了GPIO驱动框架,可以通过该框架来控制硬件上的GPIO,实现对外设的控制。 在Linux内核中,GPIO驱动可以分为两类:基于平台的GPIO驱动和基于设备的GPIO驱动。基于平台的GPIO驱动是针对整个平台的GPIO控制,而基于设备的GPIO驱动则是针对单个设备的GPIO控制。 在使用GPIO驱动时,需要先找到所使用的GPIO引脚的编号,并将其映射到内存中的地址。然后通过读写内存中的寄存器来控制GPIO的状态。 对于GPIO的操作可以通过Linux内核提供的sysfs接口来实现。在sysfs中,每个GPIO都被表示为一个文件,可以通过读写文件来进行GPIO的操作。 需要注意的是,在使用GPIO驱动时,需要谨慎操作,避免对硬件造成损坏。同时,还需要了解所使用的硬件设备的特性和限制,以确保GPIO驱动的正确使用。补充说明: 在Linux内核中,GPIO驱动主要由GPIO子系统和GPIO控制器驱动两部分组成。GPIO子系统提供了一个通用的接口,用于操作GPIO控制器驱动,而GPIO控制器驱动则是实际控制硬件的部分。 GPIO子系统可以分为两个部分:GPIO框架和GPIO API。GPIO框架是一个通用的框架,用于管理GPIO控制器和GPIO设备,它定义了一些数据结构和函数接口,用于注册和管理GPIO控制器和GPIO设备。GPIO API是一个用户空间的API,提供了一些函数接口,用于操作GPIOGPIO控制器驱动是针对特定的GPIO控制器的驱动程序,它负责实际控制GPIO的硬件操作。在Linux内核中,每种GPIO控制器都有一个对应的GPIO控制器驱动程序。当使用GPIO时,首先需要通过GPIO子系统将GPIO控制器驱动注册到系统中,然后才能使用GPIO API对GPIO进行操作。 需要注意的是,在使用GPIO驱动时,需要注意GPIO的电气特性,避免对硬件造成损坏。同时,在进行GPIO操作时,还需要注意GPIO的并发访问和竞争问题,以确保系统的正确性和稳定性。 ### 回答2: Linux GPI驱动指的是Linux系统中通过General Purpose Input/Output(GPIO)接口与硬件设备进行交互的驱动程序。GPIO接口是一组通用的、可编程的多功能引脚,可用于连接各种外部设备,例如开关、LED、传感器、驱动器等。 Linux GPIO驱动可以实现对GPIO引脚的读写操作、中断处理等功能。它不仅可以与单片机等嵌入式设备进行通信,还可与各种外接硬件设备进行连接和通信。 在Linux系统中,用户可以通过/sys/class/gpio文件系统来访问GPIO引脚。在使用GPIO驱动时,用户需要首先加载相应的内核模块,然后使用GPIO API来对引脚进行读写操作或开启中断。 GPIO驱动程序需要实现以下功能: 1. 查询GPIO可用性及分配资源。通常,由于GPIO是多路的,因此设备需要分配资源共享GPIO。 2. 初始化GPIO引脚,包括定义方向及设置上下拉电阻等。 3. 实现GPIO引脚的读写操作。 4. 解除分配资源并释放相关资源。 正常情况下,GPIO驱动程序会提供一个设备文件,用户可以通过读写该文件实现GPIO引脚的操作。 总之,Linux GPIO驱动具有良好的可移植性和稳定性,可以方便地与其他硬件设备进行交互,因此被广泛应用于各种嵌入式设备和嵌入式系统中。 ### 回答3: Linux GPIO驱动是一种在嵌入式系统中实现通用输入输出(GPIO,General Purpose Input/Output)功能的软件驱动GPIO是一种非常有用的硬件资源,它可以连接到外部设备,例如LED灯、按键和触摸屏等。 Linux内核支持GPIO操作,当你的嵌入式系统上有GPIO设备时,你可以利用GPIO来读取或设置其状态。驱动程序能够将GPIO标记为输入或输出,并且它们可以在运行时进行配置。 在Linux中,一般有两种方式将GPIO驱动程序添加到内核中:一种是将其编译到内核中,另一种是将其作为模块加载。 GPIO驱动程序等价于操作系统提供的设备文件,例如/dev/gpiochip0,它允许用户空间应用程序访问GPIO。这些设备文件可用于读取或写入GPIO状态。例如,要控制一个LED,需要将GPIO设置为输出模式,然后将其电平设置为高或低即可。 除此之外,GPIO驱动程序也可以实现中断(interrupt)机制,GPIO的状态变化时可以产生中断事件,这常用于处理GPIO键盘或GPIO中断信号的应用场景。 总结来说,Linux内核支持GPIO驱动需要有以下几个步骤:配置GPIO硬件;添加驱动程序;编写用户空间应用程序,按需要读取或设置GPIO状态来和外设交互。GPIO驱动程序是嵌入式系统中非常必要的组成部分,它们能够随时提供接口以方便对外部设备的读写访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值