首页 > 其他分享 >15_pinctl和gpio子系统

15_pinctl和gpio子系统

时间:2024-04-25 22:48:17浏览次数:16  
标签:15 引脚 pinctrl beep GPIO gpio pinctl printk

pinctl和gpio子系统

1.什么是pinctrl和gpio子系统?

​ pinctrl子系统是用来设置引脚的复用关系和电气属性的, gpio子系统是当pinctrl子系统把引脚的复用关系设置为gpio功能以后就可以使用gpio子系统来操作引脚了, 比如引脚的输入输出,高低电平等

2.Linux Pinctrl子系统提供的功能是什么?

(1)管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。

(2)管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能。

(3)配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength。

3.不同soc厂家的pin controller的节点

这些节点里都是把某些引脚复用成某些功能。

NXP 的 pinctrl 子系统如下所示 :

img

fsl,pins 属性主要用于描述设备的引脚和引脚控制信息。它是一个通用的属性,可以用于描述各种类型的设备。在具体的设备树节点中,它通常包含一个复杂的数据结构,用于描述设备的引脚控制信息。

三星pinctrl 子系统如下所示:

image-20240425123300072

瑞星微pinctrl 子系统如下所示:

img

4. 不同soc厂家的pin controller的节点里面的属性都是什么意思?

可以通过Linux源码目录Documentation/devicetree/bindings下的txt文档查看。

5. 怎么在代码里面使用pin controller里面定义好的节点?

例1:

pinctrl-names = "default"; 
pinctrl-0 = <&pinctrl_hog_1>;

含义:

​ <1>pinctrl-names = "default";

​ 设备的状态,可以有多个状态,default为状态0

​ <2>pinctrl-0 = <&pinctrl_hog_1>;

​ 第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置

例2:

pinctrl-names = "default","wake up"; 
pinctrl-0 = <&pinctrl_hog_1>;
pinctrl-1 = <&pinctrl_hog_2>;

含义

​ <1>pinctrl-names = "default","wake up";

​ 设备的状态,可以有多个状态,default为状态0,wake up为状态1

​ <2>pinctrl-0 = <&pinctrl_hog_1>;

​ 第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置

​ <3>pinctrl-1 = <&pinctrl_hog_2>;

​ 第1个状态所对应的引脚配置,也就是wake up状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_2里面的管脚配置

例3:

pinctrl-names = "default"; 
pinctrl-0 = <&pinctrl_hog_1
			&pinctrl_hog_2>;

含义

​ <1>pinctrl-names = "default";

​ 设备的状态,可以有多个状态,default为状态0

​ <2>pinctrl-0 = <&pinctrl_hog_1 &pinctrl_hog_2>;

​ 第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点,对应pinctrl_hog_1和pinctrl_hog_2这俩个节点的管脚配置

​ Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动,当然 pinctrl 子系统负责的就不仅仅是 GPIO 的驱动了而是所有 pin 脚的配置。pinctrl 子系统是随着设备树的加入而加入的,依赖于设备树。GPIO 子系统在之前的内核中也是存在的,但是 pinctrl 子系统的加入 GPIO 子系统也是有很大的改变。

​ 在以前的内核版本中,如果要配置 GPIO 的话一般要使用 SOC 厂家实现的配置函数,例如三星的配置函s3c_gpio_cfgpin等,这样带来的问题就是各家有各家的接口函数与实现方式,不但内核的代码复用率低而且开发者很难记住这么多的函数,如果要使用多种平台的话背函数都是很麻烦的,所以在引入设备树后对 GPIO 子系统进行了大的改造,使用设备树来实现并提供统一的接口。通过 GPIO 子系统功能主要实现引脚功能的配置,如设置为 GPIO,特殊功能,GPIO 的方向,设置为中断等。

​ 那么我们先来看一下怎么在设备树中pinctrl和gpio子系统描述一个gpio。

<1>设备树使用pinctrl和gpio子系统描述一个gpio。

test1:test{
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "test";
	reg = <0x020ac000 0x00000004>;
	
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_beep>;
	beep-gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
};

<2>常用gpio子系统提供的api函数

goio头文件:

#include <linux/gpio.h>
#include <linux/of_gpio.h>

1 、gpio_request 函数

作用:

gpio_request 函数用于申请一个 GPIO 管脚

函数原型:

int gpio_request(unsigned gpio, const char *label);

参数:

gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。

label:给 gpio 设置个名字。

返回值:

0,申请成功;其他值,申请失败。

2 、gpio_free函数

作用:如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。

函数原型:

void gpio_free(unsigned gpio);

参数:

gpio:要释放的 gpio 标号。

返回值:无。

3 、gpio_direction_input 函数

作用:

此函数用于设置某个 GPIO 为输入

函数原型:

int gpio_direction_input(unsigned gpio);

参数:

gpio:要设置为输入的 GPIO 标号。

返回值:设置成功返回0;设置失败返回负值

4 、gpio_direction_output 函数

作用:此函数用于设置某个 GPIO 为输出,并且设置默认输出值

函数原型:

int gpio_direction_output(unsigned gpio, int value);

参数:

gpio:要设置为输出的 GPIO 标号。

value:GPIO 默认输出值。

返回值:设置成功返回0;设置失败返回负值

5 、gpio_get_value 函数

作用:

此函数用于获取某个 GPIO 的值(0 或 1)

函数原型:

int __gpio_get_value(unsigned gpio);

参数:

gpio:要获取的 GPIO 标号。

返回值:成功返回GPIO值,失败返回负值。

6 、gpio_set_value 函数

作用:

此函数用于设置某个 GPIO 的值

函数原型:

void __gpio_set_value(unsigned gpio, int value);

参数:

gpio:要设置的 GPIO 标号。

value:要设置的值。

返回值:无

7 、 of_get_named_gpio 函数

作用:

此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio1 3 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号,函数原型如下:

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

参数:

np:设备节点。

propname:包含要获取 GPIO 信息的属性名。

index:因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。

返回值:

成功返回到的 GPIO 编号,失败返回一个负数。

Pinctrl 和 GPIO 子系统实验

driver.c

#include <linux/init.h>   //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/platform_device.h> //平台设备所需要的头文件
#include <linux/of.h> //of函数
#include <linux/of_address.h> //of_iomap函数
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

struct device_node *test_device_node; //节点
u32 out_values[2] = {0}; //读取到的数组值
unsigned int *vir_gpio5_dr; //地址映射
int beep_gpio = 0; //gpio标号

int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n");
    return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
    printk("hello mise_release bye bye\n");
    return 0;
}

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
    char kbuf[64] = "heheh";
    if (copy_to_user(ubuf, kbuf, strlen(kbuf) + 1) != 0)
    {
        printk("copy_to_user error\n");
        return -1;
    }
    printk("hello misc_read bye bye\n");
    return 0;
}

ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
    char kbuf[64] = {0};
    if (copy_from_user(kbuf, ubuf, size) != 0)
    {
        printk("copy_from_user error\n");
        return -1;
    }
    printk("hello misc_write bye bye\n");
    if(kbuf[0] == 1) //打开蜂鸣器
    {
        __gpio_set_value(beep_gpio, 1);
    }else if(kbuf[0] == 0)//关闭蜂鸣器
    {
        __gpio_set_value(beep_gpio, 0);
    }
    return 0;
}

/* 文件操作集 */
struct file_operations misc_fops = {
    .owner = THIS_MODULE, // 当前模块
    .open = misc_open,
    .release = misc_release,
    .write = misc_write,
    .read = misc_read,
};

/* 杂项设备结构体 */
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号
    .name = "hello_mise",        // 设备节点的名字
    .fops = &misc_fops           // 文件操作集

};

int beep_probe(struct platform_device *pdev)
{
    int ret = 0;

    printk("beep_probe\n");
    
    test_device_node = of_find_node_by_path("/test"); //查找根节点下的test节点
    if(test_device_node == NULL)
    {
        printk("of_find_node_by_path is error\n");
        return -1;
    }
    printk("test_device_node name is %s\n", test_device_node->name);
    beep_gpio = of_get_named_gpio(test_device_node, "beep-gpio", 0); //获取GPIO标号
    if(beep_gpio < 0)
    {
        printk("of_get_named_gpio is error\n");
        return -1;
    }
    printk("beep_gpio is %d\n", beep_gpio);
    ret =  gpio_request(beep_gpio, "beep"); //申请一个 GPIO 管脚
    if(ret != 0)
    {
        printk("gpio_request is error\n");
        return -1;
    }
    printk("gpio_request is ok\n");
    ret = gpio_direction_output(beep_gpio, 0); //设置GPIO为输出,并且设置默认输出值
    if(ret < 0)
    {
        printk("gpio_direction_output is error\n");
        return -1;
    }
    printk("gpio_direction_output is ok\n");
    ret = misc_register(&misc_dev); // 注册杂项设备
    if (ret < 0)
    {
        printk("misc register is error!\n");
        return -1;
    }
    printk("mise register is ok!\n");
    vir_gpio5_dr = of_iomap(test_device_node, 0); //直接内存映射
    if(vir_gpio5_dr == NULL)
    {
        printk("of_iomap is error\n");
        return -1;
    }
    printk("of_iomap is ok!\n");
    return 0;
}

int beep_remove(struct platform_device *platform_device)
{
    printk("beep_remove\n");
    return 0;
}

struct platform_device_id beep_id_table = {
    .name = "123"
};

struct of_device_id	of_match_table_test[] = {  //与设备树的 compatible 匹配
    {
        .compatible = "test1234"
    },
    {
        
    }
};

/* platform 驱动结构体 */
struct platform_driver beep_platform_driver = {
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "beep_test", //匹配优先级3
        .of_match_table = of_match_table_test, //中的.compatible匹配优先级1
    },
    .id_table = &beep_id_table //中的.name匹配优先级2
};

static int beep_driver_init(void)
{
    int ret = 0;
    printk("hello world\n");
    ret = platform_driver_register(&beep_platform_driver); //注册平台驱动
    if(ret < 0)
    {
        printk("platform_driver_register is error\n");
        return ret;
    }
    return 0;
}

static void beep_driver_exit(void)
{
    printk("byby\n"); // 内核模块卸载的时候打印"byb byb
    gpio_free(beep_gpio); //释放GPIO
    platform_driver_unregister(&beep_platform_driver); //卸载 platform 驱动
    misc_deregister(&misc_dev); //注销杂项设备
    iounmap(vir_gpio5_dr); //释放掉ioremap映射的地址
    
}

module_init(beep_driver_init); // 驱动模块的入口
module_exit(beep_driver_exit); // 驱动模块的出口

MODULE_LICENSE("GPL"); // 声明模块拥有开源许可证

Makefile

obj-m +=driver.o
KDIR:=/home/mzx/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga 
PWD?=$(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    int fd;
    char buf[64] = {0};
    
    if(argc < 2)
    {
        printf("Usage: %s <1:beep open / 0:beep close>\n", argv[0]);
        return -1;
    }
    
    fd = open("/dev/hello_mise", O_RDWR);
    if (fd < 0)
    {
        perror("open error\n");
        return fd;
    }
    buf[0] = atoi(argv[1]); //字符串转int
    write(fd, buf, strlen(buf)+1);
    close(fd);
    return 0;
}

标签:15,引脚,pinctrl,beep,GPIO,gpio,pinctl,printk
From: https://www.cnblogs.com/mzx233/p/18158794

相关文章

  • 【vue3入门】-【15】侦听器
    侦听器我们可以使用watch选项在每次响应式属性发生变化时触发一个函数<template><h3>侦听器</h3><!--不可以被监听,是固定的数据--><p>{{message}}</p><!--可以被监听,只能监听响应式数据(变化的数据)--><button@click="updateHandle">修改数据</button>&l......
  • 1500PLC通过Modbus转Profinet网关与流量计Modbus通讯
     Modbus转Profinet网关(XD-MDPN100)是一种能够实现Modbus协议和Profinet协议之间转换的设备。通过使用Modbus转Profinet网关,可以实现流量计与1500PLC之间的高效通讯,使得设备之间的数据交换更加便捷和高效。1500PLC作为控制器,与Modbus转Profinet网关的结合,为工业控制系统的稳定运行......
  • 4.15
    我们都知道,在Android开发中,会遇到要请求服务器拿到图片的一种情况,这种情况又怎么进行处理,我主要就是整理了下面的一些简单方法。其实总之就是,要得到图片的URL,而不是直接得到图片的一种方式(这样处理就可能存在URL改变了,那么图片就无法显示)。//传输网络图片publicBitmapgetPic(......
  • SP1557 GSS2 - Can you answer these queries II
    link题目大意:给一个\(n\)个元素的序列,\(q\)次询问\([l_i,r_i]\)的最大子段和(相同元素只算一个)。\(n,q\le10^5,-10^5\lea_i\le10^5\).解法:首先考虑最大子段和的经典动态解法:维护\(pre_i,suf_i,sum_i,mxsum_i\)。这个时候你会发现无法合并。Tips:对于区间询问问......
  • CF1591F Non-equal Neighbours
    题面:thissolution:容斥神仙题qwq考虑全集-补集,此时补集就是一些集合的并,可使用容斥设至少\(j\)个点满足\(b[i]==b[i+1]\)时方案数为\(f_j\)直接求不好求,考虑转化:有\(j\)个点时就把原序列隔成了\(n-j\)段,段内无所谓,但是用于分割的之间的段需要一样此时自然而然的......
  • CodeForces 115D Unambiguous Arithmetic Expression
    洛谷传送门CF传送门直接区间dp可以做到\(O(n^3)\),卡常可过,在此就不赘述了。为了方便先把连续的数字缩成一段。我们考虑直接从前往后扫,扫的过程中dp。设\(f_{i,j}\)为考虑了\([1,i]\),还有\(j\)个没配对的左括号的方案数。但是我们发现我们不知道一个数字前要添加几......
  • 15.常用模块(二)
    【一】pickle模块1)序列化方法(dumps)反序列化方法(loads)importpickleeg={'a':1,'b':2}#字典转二进制eg_byt=pickle.dumps(eg)print(eg_byt)#二进制转字典eg_dic=pickle.loads(eg_byt)print(eg_dic)2)写入(dump)读取(load)写入的是乱码可不知道文件后缀impo......
  • CMU 15-751 CS Theory Toolkit Lecture Lecture 3 - Factorials & Binomial Coefficie
    CMU15-751课程第三课笔记。接上回CMU15-751-2。同样照抄参考了LectureNote。今天学习的是阶乘和二项式系数的渐进分析,这两种的出现频率非常高,因此我们很有必要熟悉他们的渐进方法。这也是我们做更多渐进分析的练习的机会。阶乘(Factorials)\(n!=2^{\Theta(n\logn)}\)......
  • UVA1500 Alice and Bob
    Statement:link给\(n\)个数\(a_1,a_2...,a_n\)。先手\(\rmAlice\)和后手\(\rmBob\)有两个操作。\(del(i)\)令\(a_i=a_i-1\),必须满足\(a_i>0\)。\(merge(i,j)\),将\(i,j\)合并,必须满足\(a_i,a_j>0\)若一个人不能进行操作,则判他输。若两人都......
  • CMU 15-751 CS Theory Toolkit Lecture 2 - Basic Asymptotics
    CMU15-751课程第二课笔记。CSTheoryToolkitatCMU-YouTube照抄参考了LectureNote。渐进标记(AsymptoticNotation)我们知道\[\sum_{i=1}^ni=\frac{n(n+1)}2=\frac12n^2+\frac12n\]在\(n\)很大的时候,平方项这个函数值的影响会更显著。我们可以把这一个特......