9.GPIO子系统

news2025/4/18 2:37:38

目录

GPIO1节点

内核设备树新增rgb_led节点(使用gpio子系统)

常用的对外接口

头文件

of_find_node_by_path()函数

of_get_named_gpio()函数

gpio_request()函数

gpio_free()函数

gpio_direction_output()函数

gpio_direction_input()函数

gpio_get_value()函数

gpio_set_value()函数

GPIO子系统实验:IO引脚高低电平控制

修改设备树

dts_led.c文件

执行过程


GPIO1节点

gpio1: gpio@209c000 {
				// 用于和gpio子系统平台驱动做匹配,对应的驱动文件为 drivers/gpio/gpio-mxc.c
				compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
				//对照数据手册,gpio1寄存器组的起始地址即为0x209c000,第二个值表示范围
				reg = <0x209c000 0x4000>;
				//描述中断相关的内容
				interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
					         <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
				//描述初始化外设时钟
				clocks = <&clks IMX6UL_CLK_GPIO1>;
				//表明gpio1节点是一个gpio控制器
				gpio-controller;
				//表示要用2个cell描述一个 GPIO引脚
				#gpio-cells = <2>;
				//表示 gpio1 节点是个中断控制器
				interrupt-controller;
				//表示要用2个cell描述一个中断
				#interrupt-cells = <2>;
				//gpio编号转换成pin编号,如gpio子系统的0~9对应pinctrl的23~32号pin
				gpio-ranges = <&iomuxc  0 23 10>, <&iomuxc 10 17 6>,
					      <&iomuxc 16 33 16>;
};

内核设备树新增rgb_led节点(使用gpio子系统)

rgb_led{
    #address-cells = <1>;
    #size-cells = <1>;
    pinctrl-names = "default";
    compatible = "fire,rgb_led";
    pinctrl-0 = <&pinctrl_rgb_led>;
    // 以下每个属性都有一个属性值
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
    status = "okay";
};

rgb_led_red:自定义属性

&gpio1 4表示GPIO1_IO04

GPIO_ACTIVE_LOW表示低电平有效

常用的对外接口

头文件

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

of_find_node_by_path()函数

// path:设备树节点的绝对路径
// 获取指定路径节点的device_node结构体
inline struct device_node *of_find_node_by_path(const char *path)
/*
 * 返回值:
 *    成功:目标节点
 *    失败:NULL 
 */

of_get_named_gpio()函数

/*
 * np:指定的设备树节点
 * propname:GPIO属性名,比如rgb_led_red、rgb_led_green、rgb_led_blue
 * index:引脚索引值。若一个属性有多个属性值,index表示选第几个属性值,0表示第一个属性值
 */
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index);
/*
 * 返回值:
 *    成功:GPIO编号
 *    失败:负数
 */

gpio_request()函数

/*
 * 把指定的gpio引脚添加到pin_ctrl子系统管理,避免不同的外设使用同一个引脚
 * gpio:要申请的GPIO编号,就是在gpio子系统里的编号
 * label:给gpio设置个名字
 */
static inline int gpio_request(unsigned gpio, const char *label);
/* 返回值:
 *    成功:0
 *    失败:负数
 */

gpio_free()函数

/*
 * 释放在pinctrl子系统中注册的gpio编号
 * gpio:要释放的GPIO编号(pinctrl子系统的编号)
 */
static inline void gpio_free(unsigned gpio);
// 返回值:无

gpio_direction_output()函数

/*
 * gpio:要操作的GPIO编号
 * value:设置默认输出值
 */
static inline int gpio_direction_output(unsigned gpio , int value);
/*
 * 成功:0
 * 失败:负数
 */

gpio_direction_input()函数

//gpio:要操作的GPIO编号
int gpio_direction_input(unsigned gpio);
/*
 * 返回值:
 * 成功:0
 * 失败:负数
 */

gpio_get_value()函数

//gpio:要操作的GPIO编号
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio);
/*
 * 返回值:
 * 成功:GPIO的电平值(0、1)
 * 失败:负数
 */

gpio_set_value()函数

/*
 * gpio:要操作的GPIO编号
 * value:要设置的输出值
 */
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value);
// 返回值:无

GPIO子系统实验:IO引脚高低电平控制

修改设备树

打开内核/arch/arm/boot/dts/imx6ull-mmc-npi.dts,在/内修改用户加入内容。

rgb_led{
    #address-cells = <1>;
    #size-cells = <1>;
    pinctrl-names = "default";
    compatible = "fire,rgb_led";
    pinctrl-0 = <&pinctrl_rgb_led>;

    // 以下每个属性都有一个属性值
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
    status = "okay";
};

虚拟机:

重新编译设备树:make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

然后拷贝到共享文件夹:sudo cp 内核/arch/arm/boot/dts/imx6ull-mmc-npi.dtb /home/couvrir/桌面/sharedir

开发板:

替代旧的二进制设备树:sudo cp /mnt/imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dtb

同步缓冲区:sync

重启设备:sudo reboot

dts_led.c文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#include <asm/mach/map.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>

#define DEV_NAME        "rgb_led"
#define DEV_CNT         (1)

int rgb_led_red;
int rgb_led_green;
int rgb_led_blue;

/* 定义字符设备的设备号 */
static dev_t led_devno;
/* 定义字符设备结构体 */
static struct cdev led_chrdev;
/* 保存创建的类 */
struct class *class_led;
/* 保存创建的设备 */
struct device *device;
/* rgb_led的设备树节点结构体 */
struct device_node *rgb_led_device_node;

/* 定义led资源结构体,保存获取的节点信息以及转换后的虚拟寄存器地址 */
struct led_resource{
        struct device_node *device_node;        //rgb_led_red的设备树节点
        void __iomem *virtual_CCM_CCGR;
        void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;
        void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;
        void __iomem *virtual_DR;
        void __iomem *virtual_GDIR;
};

/* 定义RGB三个灯的led_resource结构体,保存获取的节点信息 */
struct led_resource led_red;
struct led_resource led_green;
struct led_resource led_blue;

static int led_chrdev_open(struct inode *inode, struct file *filp)
{
        printk("open form driver\n");
        return 0;
}

static ssize_t led_chrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
        int ret, error;
        unsigned int register_data = 0;         //暂存读取的寄存器数据
        unsigned char receive_data[10];         //用于保存接收到的数据
        unsigned int write_data = 0;

        if(cnt > 10)    cnt = 10;

        error = copy_from_user(receive_data, buf, cnt);
        if(error < 0)   return -1;

        ret = kstrtoint(receive_data, 16, &write_data);
        if(ret)         return -1;

        /* 设置GPIO1_04输出电平 */
        if(write_data & 0x04){
                gpio_set_value(rgb_led_red, 0);
        }else{
                gpio_set_value(rgb_led_red, 1);
        }

		/* 设置GPIO4_20输出电平 */
        if(write_data & 0x02){
                gpio_set_value(rgb_led_green, 0);
        }else{
                gpio_set_value(rgb_led_green, 1);
        }

        /* 设置GPIO4_19输出电平 */
        if(write_data & 0x01){
                gpio_set_value(rgb_led_blue, 0);
        }else{
                gpio_set_value(rgb_led_blue, 1);
        }

        return cnt;
}

static struct file_operations led_chrdev_fops = {
        .owner = THIS_MODULE,
        .open = led_chrdev_open,
        .write = led_chrdev_write,
};

static int led_probe(struct platform_device *pdv)
{
        int ret = -1;   //保存错误状态码
        unsigned int register_data = 0;

        printk(KERN_ALERT "match successed!\n");

        /* 获取rgb_led的设备树节点 */
        rgb_led_device_node = of_find_node_by_path("/rgb_led");
        if(rgb_led_device_node == NULL){
                printk(KERN_ERR "get rgb_led failed!\n");
                return -1;
        }

		/* 获取red led GPIO 引脚号 */
        rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
        if(rgb_led_red < 0){
                printk(KERN_ERR "rgb_led_red failed!\n");
                return -1;
        }

        /* 获取green led GPIO 引脚号 */
        rgb_led_green = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
        if(rgb_led_green < 0){
                printk(KERN_ERR "rgb_led_green failed!\n");
                return -1;
        }

        /* 获取blue led GPIO 引脚号 */
        rgb_led_blue = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
        if(rgb_led_blue < 0){
                printk(KERN_ERR "rgb_led_blue failed!\n");
                return -1;
        }

        /* 设置GPIO为输出模式,并默认高电平 */
        gpio_direction_output(rgb_led_red, 1);
        gpio_direction_output(rgb_led_green, 1);
        gpio_direction_output(rgb_led_blue, 1);

        /* 第一步
         * 采用动态分配的方式获取设备编号,次设备号为0
         * 设备名称为rgb-leds,可通过命令cat /proc/devices查看
         * DEV_CNT为1,当前只申请一个设备编号
         */
        ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
        if(ret < 0){
                printk("fail to alloc led_devno\n");
                goto alloc_err;
        }

        /* 第二步
         * 关联字符设备结构体cdev与文件操作结构体file_operations
         */
        led_chrdev.owner = THIS_MODULE;
        cdev_init(&led_chrdev, &led_chrdev_fops);

		/* 第三步
         * 添加设备到cdev_map哈希表中
         */
        ret = cdev_add(&led_chrdev, led_devno, DEV_CNT);
        if(ret < 0){
                printk("fail to add cdev\n");
                goto add_err;
        }

        /* 第四步:创建类 */
        class_led = class_create(THIS_MODULE, DEV_NAME);

        /* 第五步:创建设备 */
        device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);

        return 0;

alloc_err:
        return -1;
add_err:
        //添加设备失败时,需要注销设备号
        unregister_chrdev_region(led_devno, DEV_CNT);
        printk("error!\n");
}

static const struct of_device_id rgb_led[] = {
        {.compatible = "fire,rgb_led"},
        {/* sentinel */}
};

/* 定义平台设备结构体 */
struct platform_driver led_platform_driver = {
        .probe = led_probe,
        .driver = {
                .name = "rgb-leds-platform",
                .owner = THIS_MODULE,
                .of_match_table = rgb_led,
        }
};


static int __init led_platform_driver_init(void)
{
        int DriverState;

        DriverState = platform_driver_register(&led_platform_driver);
        printk(KERN_ALERT "DriverState is %d\n", DriverState);

        return 0;
}

static void __exit led_platform_driver_exit(void){
        /* 取消物理地址映射到虚拟地址 */
        iounmap(led_red.virtual_CCM_CCGR);
        iounmap(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
        iounmap(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
        iounmap(led_red.virtual_DR);
        iounmap(led_red.virtual_GDIR);

        iounmap(led_green.virtual_CCM_CCGR);
        iounmap(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
        iounmap(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
        iounmap(led_green.virtual_DR);
        iounmap(led_green.virtual_GDIR);

        iounmap(led_blue.virtual_CCM_CCGR);
        iounmap(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
        iounmap(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
        iounmap(led_blue.virtual_DR);
        iounmap(led_blue.virtual_GDIR);


        /* 销毁设备 */
        device_destroy(class_led, led_devno);
        /* 删除设备号 */
        cdev_del(&led_chrdev);
        /* 取消注册字符设备 */
        unregister_chrdev_region(led_devno, DEV_CNT);
        /* 销毁类 */
        class_destroy(class_led);

		platform_driver_unregister(&led_platform_driver);

        printk(KERN_ALERT "led_platform_driver exit\n");
}

module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");

执行过程

虚拟机:执行makemake copy。生成.ko文件。

开发板(在挂载目录下执行):

sudo insmod dts_led.ko

ls /dev/rgb_led

sudo sh -c "echo 1 > /dev/rgb_led"

sudo sh -c "echo 2 > /dev/rgb_led"

sudo sh -c "echo 4 > /dev/rgb_led"

sudo sh -c "echo 7 > /dev/rgb_led"

sudo sh -c "echo 0 > /dev/rgb_led"

sudo rmmod dts_led.ko

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/961322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gradio Auth登录页设置中文

最近入坑了gradio, 就是一款python框架&#xff0c;可以通过简单的几行代码&#xff0c;就能够帮我们构建一个简易的页面网站&#xff0c;并且可以在里面做相应的逻辑处理。目前该框架在AI领域相对比较火爆&#xff0c;用于给自己的大模型构建操作页面。 官网地址&#xff1a;…

离散数据编码方式总结(OneHotEncoder、LabelEncoder、OrdinalEncoder、get_dummies、DictVector

写在前面 在机器学习的特征选择的时候&#xff0c;往往有一些离散的特征不好计算&#xff0c;此时需要对这些特征进行编码&#xff0c;但是编码方式有很多&#xff0c;不同的包也会有不同的编码方式。&#xff08;明白OneHotEncoder、LabelEncoder、OrdinalEncoder、get_dummi…

网络基础之重中之重

目录 IP协议 ​编辑 基本概念&#xff1a; 协议头格式&#xff1a; ​编辑 网段划分 DHCP &#xff1a; CIDR&#xff1a; 特殊的IP地址&#xff1a; IP地址的数量限制&#xff1a; 私有IP和公网IP 路由 路由的过程&#xff1a; 数据链路层 认识以太网&#x…

【JAVA】类和对象,继承

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录 什么是类&#xff0c;对象类和类的实例化字段的初始化类中实现方法static 静态关键字 封装priva…

HarmonyOS/OpenHarmony(Stage模型)应用开发单一手势(二)

三、拖动手势&#xff08;PanGesture&#xff09; .PanGestureOptions(value?:{ fingers?:number; direction?:PanDirection; distance?:number}) 拖动手势用于触发拖动手势事件&#xff0c;滑动达到最小滑动距离&#xff08;默认值为5vp&#xff09;时拖动手势识别成功&am…

IP初学习

1.IP报文 首部长度指的是报头长度&#xff0c;用于分离报头和有效载荷 2.网段划分 IP地址 目标网络 目标主机 3.例子 4.特殊的IP地址 5.真正的网络环境 6.调制解调器 “猫”&#xff0c;学名叫宽带无线猫 7.NAT 源IP在内网环境不断被替换 8.私有IP不能出现在公网上 因…

Python学习之三 变量与数据类型深度学习

本部分内容参考自以下书。 在学习之二中说道,所有的数据类型都是类,那么变量以及对应的值都是对象。为了后续更容易理解,我们先来看一下内置函数id的帮助文档。 它用于返回对象的标识。此标识在生命周期内必须是唯一且恒定的。 在Python中,变量只是名称,赋值操作并不会复…

做西班牙语翻译好不好?西班牙语薪酬如何?

众所周知&#xff0c;西班牙语作为全球应用广泛的“小语种”&#xff0c;市场对它的翻译需求极为庞大。西班牙语翻译是你寻求优秀事业的新赛道&#xff01;那么&#xff0c;西班牙语翻译薪酬如何&#xff0c;北京西语翻译哪家好&#xff1f; 美国的调查数据告诉你&#xff0c;西…

Linux下的系统编程——进程(七)

前言&#xff1a; 程序是指储存在外部存储(如硬盘)的一个可执行文件, 而进程是指处于执行期间的程序, 进程包括 代码段(text section) 和 数据段(data section), 除了代码段和数据段外, 进程一般还包含打开的文件, 要处理的信号和CPU上下文等等.下面让我们开始对Linux进程的学…

spring6详细讲解

Spring6 1、概述 1.1、Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单…

粉丝经验分享:13:00 开始的面试,13:06 就结束了,问题真是变态

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

hdfs滚动升级(rollingUpgrade )

最近对hdfs的升级过程很感兴趣&#xff0c;所以准备研究下其升级的过程&#xff0c;本篇文章是依据官网文档进行的升级过程&#xff08;地址&#xff1a;Apache Hadoop 2.10.2 – HDFS Rolling Upgrade&#xff09;&#xff0c;该文章中还有关于降低的介绍&#xff0c;感兴趣的…

【快手小玩法-弹幕游戏】开发者功能测试报告提交模板

背景 快手有明确的要求&#xff0c;准入和准出更加严格&#xff0c;要求有明确的测试报告。格式如下&#xff1a; *本文参考字节wiki&#xff1a;CP侧测试报告模板(复制填写轻雀文档) 其他文章推荐&#xff1a;【抖音小玩法-弹幕游戏】开发者功能测试报告提交模板 一、前言…

Lesson03---类与对象(中篇)

lesson5: 一、类的6个默认成员函数&#xff08;2&#xff1a;29&#xff1a;10&#xff09; 1.什么是空类&#xff1f;&#xff08;2&#xff1a;29&#xff1a;40&#xff09; a.空类中真的什么都没有吗&#xff1f;&#xff08;2&#xff1a;29&#xff1a;50&#xff09;…

学习MATLAB

今日&#xff0c;在大学慕课上找了一门关于MATLAB学习的网课&#xff0c;MATLAB对于我们这种自动化的学生应该是很重要的&#xff0c;之前也是在大三的寒假做自控的课程设计时候用到过&#xff0c;画一些奈奎斯特图&#xff0c;根轨迹图以及伯德图&#xff0c;但那之后也就没怎…

网络协议三要素

计算机语言作为程序员控制一台计算机工作的协议&#xff0c;具备了协议的三要素。 语法&#xff0c;就是这一段内容要符合一定的规则和格式。例如&#xff0c;括号要成对&#xff0c;结束要使用分号等。语义&#xff0c;就是这一段内容要代表某种意义。例如数字减去数字是有意…

单目标追踪——【Transformer】Autoregressive Visual Tracking

目录 文章侧重点网络结构Encoder&#xff08;特征提取与融合得到Vision Features&#xff09;Decoder&#xff08;Vision FeatureMotion Feature 特征融合与输出&#xff09;运动特征生成特征融合与输出 消融实验N与VocabularyBins 论文链接&#xff1a;https://paperswithcode…

CocosCreator3.8研究笔记(三)CocosCreator 项目结构说明及编辑器的简单使用

我们通过Dashboard 创建一个2d项目&#xff0c;来演示CocosCreator 的项目结构。 等待创建完成后&#xff0c;会得到以下项目工程&#xff1a; 一、assets文件夹 assets文件夹&#xff1a;为资源目录&#xff0c;用来存储所有的本地资源&#xff0c;如各种图片&#xff0c;脚本…

一文快速了解代理https和sock5协议的区别

一、什么是https协议 HTTPS 协议全称&#xff1a;HyperText Transfer Protocol Secure&#xff08;超文本传输安全协议&#xff09;&#xff0c;是一种通过计算机网络进行安全通信的传输协议。 作用于应用层&#xff08;application layer&#xff09;中。 HTTPS 是在HTTP 的…

[C++]学习中的一个问题

#include<iostream> #include<string> #include<cstring> using namespace std;int main(){char str[31];cout << "----" << sizeof(str) << endl;// 给了memset(str, 0, sizeof(str));for (size_t i 0; i < 31; i){cout &l…