嵌入式实践教程--【Device Tree】设备树(一)——GPIO_arm9 gpio设备树-程序员宅基地

技术标签: 驱动  linux  嵌入式Linux实战开发教程  设备树  

以RK3328为例,介绍设备树在GPIO方面的应用。
引脚图如下
在这里插入图片描述

一、首先在DTS文件中增加GPIO资源描述:

gpio_demo: gpio_demo {
	            status = "okay";
	            compatible = "rk3328,gpio_demo";
	            firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;          /* GPIO0_B4 */
	            firefly-irq-gpio = <&gpio2 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO2_D5 */               
	            };
  1. GPIO_ACTIVE_HIGH:引脚状态为常高
  2. IRQ_TYPE_EDGE_RISING:上升沿触发
  3. RK系列GPIO分为GPIO0,GPIO1,GPIO2…等组,每组GPIO下分为A,B,C,D等小组,而每个A下有0-7个引脚,B,C,D同A。于是A=0,B与A相差8个引脚,C与B相差8个引脚,以此类推。。。GPIO0_B4 属于B组,基数为8,加4即为引脚序列号12。

二、在probe函数里添加解析

1.区别于无Device Tree驱动程序而言,首先要有一个device_id结构体用于匹配我们在设备树资源里添加的属性:

static const struct of_device_id gpio_demo_dt_ids[] = {
    { .compatible = "rk3328,gpio_demo", },
    {},
};

2.在平台设备驱动结构体里加上.of_match_table 属性,of_match_ptr(gpio_demo_dt_ids)是匹配设备树的函数

static struct platform_driver int_demo_driver = { .
				driver = { 
					.name = "gpio_demo", 
					.of_match_table = of_match_ptr(gpio_demo_dt_ids), 
				}, 
				.probe = int_demo_probe, 
				.remove = int_demo_remove,
		 };

enum of_gpio_flags {
OF_GPIO_ACTIVE_LOW = 0x1,
};

static int firefly_gpio_probe(struct platform_device *pdev)
{
	int ret; 
    int gpio; 
    enum of_gpio_flags flag; 
	struct firefly_gpio_info *gpio_info; 
    struct device_node *firefly_gpio_node = pdev->dev.of_node; //用于接收传入的设备树结点

	printk("Firefly GPIO Test Program Probe\n"); 
	
	//1、申请空间
    gpio_info = devm_kzalloc(&pdev->dev,sizeof(struct firefly_gpio_info *), GFP_KERNEL); 
    
    if (!gpio_info) { 
        return -ENOMEM;
        }
        
     //2、获取名为 "firefly-gpio"的gpio信息
	gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-gpio", 0, &flag); 

	if (!gpio_is_valid(gpio)) {
    	printk("firefly-gpio: %d is invalid\n", gpio); return -ENODEV;
        } 
        //请求控制获取的gpio
	if (gpio_request(gpio, "firefly-gpio")) { 
        printk("gpio %d request failed!\n", gpio); 
        gpio_free(gpio); 
        return -ENODEV;
        } 

	//设置操作的gpio
	gpio_info->firefly_gpio = gpio;
	 //根据flag判断使能值
    gpio_info->gpio_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;
    
    //设置为输出,首参为gpio的名字,设置gpio的电平值。
    gpio_direction_output(gpio_info->firefly_gpio, gpio_info->gpio_enable_value); 
    printk("Firefly gpio putout\n"); 
    }

#include <linux/gpio.h> //里面声明io口的操作函数

 1. int gpio_request(unsigned gpio, const char *label);//每个io只能被请求一次,可防止多个驱动来控制同一个IO口 
 
 2. void gpio_free(unsigned gpio); //释放已请求的io口 
 
 3. int gpio_direction_input(unsigned gpio); //把指定的IO口作输入功能, gpio用于指定具体哪个io口 
 
4. int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平 

5.  int gpio_get_value(unsigned gpio); //获取指定IO口的电平 

 6.  void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1)

 7. int gpio_to_irq(unsigned gpio); //根据io口,获取到它对应的中断号(io口大都有外部中断功能)

获取设备树里设备节点的gpio口信息:
#include <linux/of_gpio.h>

//只需一个函数即可
int of_get_named_gpio_flags(struct device_node *np, const char *propname,
    int index, enum of_gpio_flags *flags);

//返回值为int类型的gpio口.
//np为设备或设备子节点对象, propname为指定的属性名字, index表示获取属性里的第几个值
// 其中flags一定得注意,按文档里的说明应就是一个int类型的值,但根本就不能为int参数(会导致kernel panic),
// 通过阅读内核里的代码得出, flags的参数应为struct gpio_config类型. 定义在下面文件:
"include/linux/sys_config.h"
struct gpio_config {
     u32 gpio;       /* gpio global index, must be unique */
     u32     mul_sel;    /* multi sel val: 0 - input, 1 - output... */
     u32     pull;       /* pull val: 0 - pull up/down disable, 1 - pull up... */
     u32     drv_level;  /* driver level val: 0 - level 0, 1 - level 1... */
     u32 data;       /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */
 };

三、中断

firefly-irq-gpio = <&gpio2 29 IRQ_TYPE_EDGE_RISING>; /* GPIO2_D5 */

中断的类型

IRQ_TYPE_NONE //默认值,无定义中断触发类型
IRQ_TYPE_EDGE_RISING //上升沿触发
IRQ_TYPE_EDGE_FALLING //下降沿触发
IRQ_TYPE_EDGE_BOTH //上升沿和下降沿都触发
IRQ_TYPE_LEVEL_HIGH //高电平触发
IRQ_TYPE_LEVEL_LOW //低电平触发

static int firefly_gpio_probe(struct platform_device *pdev)
{   
	int ret; 
    int gpio; 
    enum of_gpio_flags flag; 
    struct firefly_gpio_info *gpio_info; 
    struct device_node *firefly_gpio_node = pdev->dev.of_node; 
    ...... 

 //1、获取名为 "firefly-irq-gpio"的gpio信息
	gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-irq-gpio", 0, &flag); 

    gpio_info->firefly_irq_gpio = gpio; 
    gpio_info->firefly_irq_mode = flag; 
    //把GPIO的PIN值转换为相应的IRQ值
    gpio_info->firefly_irq = gpio_to_irq(gpio_info->firefly_irq_gpio); 
    if (gpio_info->firefly_irq) { 
    
       if (gpio_request(gpio, "firefly-irq-gpio")) { 
          printk("gpio %d request failed!\n", gpio); gpio_free(gpio); return IRQ_NONE; 
        } 
        //申请中断
        ret = request_irq(gpio_info->firefly_irq, firefly_gpio_irq, flag, "firefly-irq-gpio", gpio_info); 
        if (ret != 0) free_irq(gpio_info->firefly_irq, gpio_info); 
           dev_err(&pdev->dev, "Failed to request IRQ: %d\n", ret); 
     } 
 return 0;
}
static irqreturn_t firefly_gpio_irq(int irq, void *dev_id) //中断函数
{ 
    printk("Enter firefly gpio irq test program!\n");
    return IRQ_HANDLED;
}

调用gpio_to_irq把GPIO的PIN值转换为相应的IRQ值,调用gpio_request申请占用该IO口,调用request_irq申请中断,如果失败要调用free_irq释放,该函数中gpio_info->firefly_irq是要申请的硬件中断号,firefly_gpio_irq是中断函数,gpio_info->firefly_irq_mode是中断处理的属性,”firefly-gpio”是设备驱动程序名称,gpio_info是该设备的device结构,在注册共享中断时会用到。

四、复用

如何定义 GPIO 有哪些功能可以复用,在运行时又如何切换功能呢?以 I2C4 为例作简单的介绍。

查规格表可知,I2C4_SDA 与 I2C4_SCL 的功能定义如下:

Pad# func0 func1 I2C4_SDA/GPIO1_B3
gpio1b3 i2c4_sda I2C4_SCL/GPIO1_B4 gpio1b4 i2c4_scl

在 kernel/arch/arm64/boot/dts/rockchip/rk3328xx.dtsi 里有:

i2c4: i2c@ff3d0000{
	compatible = "rockchip,rk3399-i2c"; 
	reg = <0x0 0xff3d0000 0x0 0x1000>; 
	clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru 				PCLK_I2C4_PMU>; 
	clock-names = "i2c", "pclk"; 
	interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>; 
	pinctrl-names = "default", "gpio"; 
	pinctrl-0 = <&i2c4_xfer>; 
	pinctrl-1 = <&i2c4_gpio>;   //此处源码未添加 
	#address-cells = <1>;  
	#size-cells = <0>;  
	status = "disabled"; 
};

此处,跟复用控制相关的是 pinctrl- 开头的属性:

pinctrl-names 定义了状态名称列表: default (i2c 功能) 和 gpio 两种状态。
pinctrl-0 定义了状态 0 (即 default)时需要设置的 pinctrl: &i2c4_xfer
pinctrl-1 定义了状态 1 (即 gpio)时需要设置的 pinctrl: &i2c4_gpio

这些 pinctrl 在kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi中这样定义:

pinctrl: pinctrl { 
	compatible = "rockchip,rk3399-pinctrl"; 
	rockchip,grf = <&grf>; 
	rockchip,pmu = <&pmugrf>; 
	#address-cells = <0x2>; 
	#size-cells = <0x2>; 
	ranges; 
	i2c4{
    i2c4_xfer: i2c4-xfer{
    	rockchip,pins = <1 12 RK_FUNC_1 &pcfg_pull_none>, <1 11 RK_FUNC_1 &pcfg_pull_none>;
        }; 
	i2c4_gpio: i2c4-gpio { 
		rockchip,pins = <1 12 RK_FUNC_GPIO &pcfg_pull_none>, <1 11 RK_FUNC_GPIO &pcfg_pull_none>; 
        };          
};

RK_FUNC_1,RK_FUNC_GPIO 的定义在 kernel/include/dt-bindings/pinctrl/rk.h 中:

 #define RK_FUNC_GPIO    0
 #define RK_FUNC_1   1
 #define RK_FUNC_2   2
 #define RK_FUNC_3   3
 #define RK_FUNC_4   4
 #define RK_FUNC_5   5                         
 #define RK_FUNC_6   6
 #define RK_FUNC_7   7

另外,像”1 11”,”1 12”这样的值是有编码规则的,编码方式与上一小节”输入输出”描述的一样,”1 11”代表GPIO1_B3,”1 12”代表GPIO1_B4。

在复用时,如果选择了 “default” (即 i2c 功能),系统会应用 i2c4_xfer 这个 pinctrl,最终将 GPIO1_B3 和 GPIO1_B4 两个针脚切换成对应的 i2c 功能;而如果选择了 “gpio” ,系统会应用 i2c4_gpio 这个 pinctrl,将 GPIO1_B3 和 GPIO1_B4 两个针脚还原为 GPIO 功能。

我们看看 i2c 的驱动程序 kernel/drivers/i2c/busses/i2c-rockchip.c 是如何切换复用功能的:

static int rockchip_i2c_probe(struct platform_device *pdev)
{
	struct rockchip_i2c *i2c = NULL; struct resource *res; 
    struct device_node *np = pdev->dev.of_node; int ret;// 
     ...
    i2c->sda_gpio = of_get_gpio(np, 0);
    if (!gpio_is_valid(i2c->sda_gpio)) {
		dev_err(&pdev->dev, "sda gpio is invalid\n");
		return -EINVAL;
        }
	ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));
	if (ret) {
    	dev_err(&pdev->dev, "failed to request sda gpio\n");return ret;}
		i2c->scl_gpio = of_get_gpio(np, 1);
		if (!gpio_is_valid(i2c->scl_gpio)) {
			dev_err(&pdev->dev, "scl gpio is invalid\n");
			return -EINVAL;
	}
	ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));
	if (ret) {
		dev_err(&pdev->dev, "failed to request scl gpio\n");
		return ret;
		}
	i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");
	if (IS_ERR(i2c->gpio_state)) {
		dev_err(&pdev->dev, "no gpio pinctrl state\n");return PTR_ERR(i2c->gpio_state);
        }
	pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);
	gpio_direction_input(i2c->sda_gpio);
	gpio_direction_input(i2c->scl_gpio);
	pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state);// ...}

首先是调用 of_get_gpio 取出设备树中 i2c4 结点的 gpios 属于所定义的两个 gpio:

gpios = <&gpio1 GPIO_B3 GPIO_ACTIVE_LOW>, <&gpio1 GPIO_B4 GPIO_ACTIVE_LOW>;

然后是调用 devm_gpio_request 来申请 gpio,接着是调用 pinctrl_lookup_state 来查找 “gpio” 状态,而默认状态 “default” 已经由框架保存到 i2c->dev-pins->default_state 中了。

最后调用 pinctrl_select_state 来选择是 “default” 还是 “gpio” 功能。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38131812/article/details/88956752

智能推荐

在ubuntu 8.04下安装Oracle 11g二-程序员宅基地

文章浏览阅读408次。 在ubuntu 8.04下安装Oracle 11g2008年05月22日 星期四 11:02oracle 11g 数据库虽然提供了linux x86的版本,但是支持的linux版本只有Red Hat,Novell and Solaris 这几个,debian 和 ubuntu 不在支持之列,所以在ubuntu下安装就相对麻烦一些,请照着下文的方法一步一步的安装,不

初一计算机知识点下册,初一英语下册语法知识点全汇总-程序员宅基地

文章浏览阅读166次。新东方在线中考网整理了《初一英语下册语法知识点全汇总》,供同学们参考。一. 情态动词can的用法can+动词原形,它不随主语的人称和数而变化。1. 含有can的肯定句:主语+can+谓语动词的原形+其他。2. 含有can的否定句:主语+can't+动词的原形+其他。3. 变一般疑问句时,把can提前:Can+主语+动词原形+其他? 肯定回答:Yes,主语+can。否定回答:No,主语+can't...._七年级下册计算机知识点

NX/UG二次开发—其他—UFUN函数调用Grip程序_uf调用grip-程序员宅基地

文章浏览阅读3k次。在平时开发中,可能会遇到UFUN函数没有的功能,比如创建PTP的加工程序(我目前没找到,哪位大神可以指点一下),可以使用Grip创建PTP,然后用UFUN函数UF_call_grip调用Grip程序。具体如下截图(左侧UFUN,右侧Grip程序):..._uf调用grip

Android RatingBar的基本使用和自定义样式,kotlin中文教程_ratingbar样式修改-程序员宅基地

文章浏览阅读156次。第一个:原生普通样式(随着主题不同,样式会变)第二个:原生普通样式-小icon第三个:自定义RatingBar 颜色第四个:自定义RatingBar DrawableRatingBar 各样式实现===============原生样式原生样式其实没什么好说的,使用系统提供的style 即可<RatingBarstyle="?android:attr/ratingBarStyleIndicator"android:layout_width=“wrap_cont.._ratingbar样式修改

OpenGL环境搭建:vs2017+glfw3.2.1+glad4.5_vs2017的opengl环境搭建(完整篇)-程序员宅基地

文章浏览阅读4.6k次,点赞6次,收藏11次。安装vs2017:参考vs2017下载和安装。安装cmake3.12.3:cmake是一个工程文件生成工具。用户可以使用预定义好的cmake脚本,根据自己的选择(像是Visual Studio, Code::Blocks, Eclipse)生成不同IDE的工程文件。可以从它官方网站的下载页上获取。这里我选择的是Win32安装程序,如图所示:然后就是运行安装程序进行安装就行。配置glfw3...._vs2017的opengl环境搭建(完整篇)

在linux-4.19.78中使用UBIFS_ubifs warning-程序员宅基地

文章浏览阅读976次。MLC NAND,UBIFS_ubifs warning

随便推点

计算机系统内存储器介绍,计算机系统的两种存储器形式介绍-程序员宅基地

文章浏览阅读2.2k次。计算机系统的两种存储器形式介绍时间:2016-1-6计算机系统的存储器一般应包括两个部分;一个是包含在计算机主机中的主存储器,简称内存,它直接和运算器,控制器及输入输出设备联系,容量小,但存取速度快,一般只存放那些急需要处理的数据或正在运行的程序;另一个是包含在外设中的外存储器,简称外存,它间接和运算器,控制器联系,存取速度虽然慢,但存储容量大,是用来存放大量暂时还不用的数据和程序,一旦要用时,就..._计算机存储器系统采用的是主辅结构,主存速度快、容量相对较小,用于 1 分 程序,外

西门子PLC的编程工具是什么?_西门子plc编程软件-程序员宅基地

文章浏览阅读5.6k次。1. STEP 7(Simatic Manager):STEP 7或者Simatic Manager是西门子PLC编程最常用的软件开发环境。4. STEP 7 MicroWin:STEP 7 MicroWn是一款专门针对微型PLC(S7-200系列PLC)的编程软件,是Simatic Manager的简化版。如果需要与PLC系统配合使用,则需要与PLC编程工具进行配合使用。除了上述软件之外,西门子还提供了一些配套软件和工具,如PLC模拟器、硬件调试工具等,以帮助PLC编程人员快速地进行调试和测试。_西门子plc编程软件

HashMap扩容_hashma扩容-程序员宅基地

文章浏览阅读36次。【代码】HashMap扩容。_hashma扩容

Eclipse maven项目中依赖包不全,如何重新加载?_maven资源加载不全,怎么重新加载-程序员宅基地

文章浏览阅读2.9k次。1mvn dependency:copy-dependencies2 项目右键 -> Maven -> Disable Maven Nature3 项目右键 -> Configure -> Convert to Maven Project_maven资源加载不全,怎么重新加载

mysql dml全称中文_MySQL语言分类——DML-程序员宅基地

文章浏览阅读527次。DMLDML的全称是Database management Language,数据库管理语言。主要包括以下操作:insert、delete、update、optimize。本篇对其逐一介绍INSERT数据库表插入数据的方式:1、insert的完整语法:(做项目的过程中将字段名全写上,这样比较容易看懂)单条记录插入语法:insert into table_name (column_name1,......_dml的全称是

【小工匠聊Modbus】04-调试工具-程序员宅基地

文章浏览阅读136次。可以参考: http://git.oschina.net/jrain-group/ 组织下的Java Modbus支持库Modbus-系列文章1、虚拟成对串口(1)下载虚拟串口软件VSPD(可在百度中搜索)image.png(2)打开软件,添加虚拟串口。在设备管理中,看到如下表示添加成功。..._最好用的 modebus调试工具

推荐文章

热门文章

相关标签