控制RK3568的GPIO

news2024/11/18 11:36:13

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、设备树中是如何描述引脚的?
    • 1.pinctrl子系统
    • 2.gpio子系统
  • 二、使用步骤
  • 总结


前言

RK3568的引脚资源还是相当多的,一共有5组GPIO,每组有A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分,当然这里面也有一些引脚是专用的,比如mipi的引脚,就不能作为普通的引脚使用,本小节就来学习如何在设备树的支持下使用引脚


一、设备树中是如何描述引脚的?

一个引脚的使用无非就是使能该引脚的时钟,复用功能,电特性,中断等操作,这些如果实在裸机中是要自己去查看芯片手册的寄存器,但是这里使用设备树就相对简单了。

1.pinctrl子系统

pinctrl子系统就是专门来初始化引脚的状态的,比如该引脚是否使用复用功能,电特性(上拉、下拉等),对于我们使用者来说,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由pinctrl 子系统来完成,pinctrl 子系统源码目录 drivers/pinctrl
不同 soc 厂家的 pin controller 的节点是不一样的,但是这些节点里都是把某些引脚复用成某些功能。如

NXP 的 pinctrl 子系统如下所示:
在这里插入图片描述

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

        i2c2 {
                /omit-if-no-ref/
                i2c2m0_xfer: i2c2m0-xfer {
                        rockchip,pins =
                                /* i2c2_sclm0 */
                                <0 RK_PB5 1 &pcfg_pull_none_smt>,
                                /* i2c2_sdam0 */
                                <0 RK_PB6 1 &pcfg_pull_none_smt>;
                };

                /omit-if-no-ref/
                i2c2m1_xfer: i2c2m1-xfer {
                        rockchip,pins =
                                /* i2c2_sclm1 */
                                <4 RK_PB5 1 &pcfg_pull_none_smt>,
                                /* i2c2_sdam1 */
                                <4 RK_PB4 1 &pcfg_pull_none_smt>;
                };
        };

这里解释一下瑞芯微的:
在这里插入图片描述

具体的可以参考瑞芯微写好的设备树,或者看帮助文档下面的设备树绑定的文档

2.gpio子系统

前面我们使用pinctrl子系统可以很方便的配置我们要使用的引脚的复用、电特性等操作,但是如果想要在我们驱动中读取某个引脚的值,或者设置引脚的方向的话只靠pinctrl是不够的,这个时候就需要GPIO子系统,有了这个子系统之后,我们就可以轻松的调用内核提供的API来获取到GPIO的资源达到我们控制引脚的目的。
使用gpio还有一个好处:
之前我们控制一个 GPIO 可以直接来操作我们的寄存器,还有一种方法是使用 SOC 厂家实现的配置函数,例如三星的配置函数为 s3c_gpio_cfgpin 等,这样带来的问题就是各家有各家的接口函数与实现方式,不但内核的代码复用率低而且开发者很难记住这么多的函数,如果要使用多种平台的话背函数都是很麻烦的,所以在引入设备树后对 GPIO 子系统进行了大的改造,使用设备树来实现并提供统一的接口。
下面来看看设备树中GPIO长什么样:

 leds: leds {
                compatible = "gpio-leds";
                work_led: work {
                        lable = "user1";
                        gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;//表示使用gpio0,B7  高电平有效
                        linux,default-trigger = "none";
                        default-state = "off";
                };
        };

设置好以后,我们就可以调用内核提供的同统一接口来调用GPIO

经过 GPIO 子系统,我们可以通过如下的方式来配置 GPIO
gpio_request(leddev.led0, "led0");
gpio_direction_output(leddev.led0, 1);
gpio_set_value(leddev.led0, 0);
gpio_direction_input(key.irqkeydesc[0].gpio)
gpio_get_value(keydesc->gpio);
gpio_free(leddev.led0);

还有很多gpio相关的函数,具体可以参考#include <linux/gpio.h> 和 #include <linux/of_gpio.h>

二、使用步骤

一般情况下我们会查看对应硬件的引脚原理图,之后使用pinctrl节点来复用引脚(如果没有复用功能可以不用),再之后添加自己的节点调用pinctrl和gpio,这样我们就可以在驱动中调用内核的API函数来使用GPIO:

这里以点灯为例:
原理图:
在这里插入图片描述
修改设备树:

这里我把需要的led节点添加到rk3568-evb1-ddr4-v10.dtsi的/下面

leds: leds {
                compatible = "gpio-leds";
                work_led: work {
                        lable = "user1";
                        gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
                        linux,default-trigger = "none";
                        default-state = "off";
                };
        };


由于这个引脚默认就是普通gpio,所以可以不用Pinctrl

编写驱动

#include<linux/init.h>
#include<linux/fs.h>
#include<linux/module.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/platform_device.h>
#include<linux/of.h> //设备树相关的节点函数
#include<linux/of_gpio.h>   //设备树gpio
#include<linux/interrupt.h>   //中断
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/uaccess.h> //包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函数定义。
#include <linux/io.h> //包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。
#include <linux/gpio.h>
#include <linux/of_gpio.h>

 dev_t dev_num;//设备号
 struct cdev cdev;
 struct class *class;//创建的类
 struct device * device;//设备节点
//gpio

int led_num;

static int char_open (struct inode *inode, struct file *file)
{
    printk("open dev ok\n ");

    return 0;
}


ssize_t char_read (struct file *file, char __user *user, size_t size, loff_t *lofft)
{
    return 0;
}


ssize_t char_write (struct file *file, const char __user *ubuf, size_t size, loff_t *lofft)
{
    // kbuf 保存的是从应用层读取到的数据
    char kbuf[64] = {0};
    // copy_from_user 从应用层传递数据给内核层
    if (copy_from_user(kbuf, ubuf, size) != 0)
    {
    // copy_from_user 传递失败打印
    printk("copy_from_user error \n ");
    return -1;
    }
    //打印传递进内核的数据
    printk("kbuf is %d ", kbuf[0]);
    //如果传递进的数据是 1,则 led 亮
    if (kbuf[0] == 1)
    {
        gpio_set_value(led_num, 1);
    }
    //如果传递进的数据是 0,则 led 不亮
    else if (kbuf[0] == 0)
    gpio_set_value(led_num, 0);
    return 0;

}
struct file_operations fops={
       .open = char_open,
       .read = char_read,
       .write = char_write,
       .owner =THIS_MODULE 
    };
int probe(struct platform_device *dev)
{
    int ret;
    printk("probe is ok \n");
    //获取led的GPIO端口号
    led_num = of_get_named_gpio(dev->dev.of_node,"gpios",0);//参数说明:找到的设备树节点结构,从那个属性里获取gpio,索引
    if (led_num < 0) 
    {
        printk("of_get_namd_gpio led_num is error \n"); 
        return -1; 
    }
    printk("of_get_namd_gpio led_num is %d\n",led_num); 

    //设置led的GPIO端口的方向
    gpio_direction_output(led_num,0);

    //led的GPIO申请
    ret = gpio_request(led_num,"led_gpio"); 
    if (ret < 0) 
    { 
        printk("gpio_request led_num is error \n"); 
        return -1; 
    }   

    //2.注册杂项设备/字符设备
    ret = alloc_chrdev_region(&dev_num,0,1,"my_char");//1.自动分配设备号
    
    if(ret < 0)
    {
        printk("alloc_chrdev_region fale\n");
        return -1;
    }
    printk("alloc_chrdev_region ok\n");
 
    cdev.owner = THIS_MODULE;
    cdev_init(&cdev,&fops);//初始化一个cdev结构体
    cdev_add(&cdev,dev_num,1);//2.把设备号添加到内核 
    class = class_create(THIS_MODULE,"my_class");//创建一个类,这个类在/sys/class里面
    device = device_create(class,NULL,dev_num,NULL,"my_led_char_platform");//3.在创建的类下面创建设备节点文件

    return 0;
}

int remove(struct platform_device *dev)
{
     printk("remove is ok \n");
    return 0;
}

const struct of_device_id of_match_device_tree[] ={
{.compatible = "myled"},//设备树节点匹配名字,和compatible = "button_interrupt";对应
{},
};
struct platform_driver drv={
.probe = probe,
.driver={
    .name = "my_platfrom",//和device.c匹配的名称
    .owner =THIS_MODULE,
    .of_match_table = of_match_device_tree//和设备树节点匹配的名称
},
.remove = remove

};
static int platfrom_init(void)
{
    platform_driver_register(&drv);//1.注册设备驱动平台
    return 0;
}
static void platfrom_exit(void)
{
    platform_driver_unregister(&drv);
    unregister_chrdev_region(dev_num,1);  
    cdev_del(&cdev);
    device_destroy(class,dev_num);
    class_destroy(class);
    gpio_free(led_num);//释放gpio
    printk("byby ok\n");
}
//驱动模块的入口和出口
module_init(platfrom_init);
module_exit(platfrom_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LJM");

编译、加载成功之后会在/dev目录下出现如下的设备文件

在这里插入图片描述
使用如下命令加载模块:insmod driver.ko

在这里插入图片描述
编写应用文件
这个就相关简单了;

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

#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    int fd;
    char buf[64] = {0};//定义 buf 缓存
    //打开设备节点
    fd = open("/dev/my_led_char_platform",O_RDWR);
    if(fd < 0)
    {
        //打开设备节点失败
        perror("open error \n");
        return fd;
    }
    // atoi()将字符串转为整型,这里将第一个参数转化为整型后,存放在 buf[0]中
    buf[0] = atoi(argv[1]);
    //把缓冲区数据写入文件中
    write(fd,buf,sizeof(buf));
    printf("buf is %d\n",buf[0]);
    close(fd);
    return 0;
}

之后交叉编译即可:
测试如下:
在这里插入图片描述
此时led也会亮


总结

本小节主要是针对设备树如何描述一个引脚,和在驱动中我们如何使用内核提供好的API使用引脚,这个只是看似简单,但也是操作其他的一个基础。

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

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

相关文章

数图互通高校房产管理——房屋修缮管理

数图互通房产管理系统在这方面做得比较全面&#xff1b; 实现房屋修缮改造、装修维护、零星维修线上管理&#xff0c;建立机制规范管理&#xff0c;避免私自改扩建。 建筑物立项审批全流程资料和过程管理&#xff0c;建筑物修建施工、维修审批流程管理。整套系统包含了建筑物从…

链下签名实现

什么是签名 比如我们在使用 opensea 的时候&#xff0c;经常会提示我们进行数字签名&#xff0c;如下图&#xff1a; 用户进行 sign 确认&#xff0c;即用自己的私钥对一段数据进行签名&#xff0c;得到一个 signature&#xff0c;其他人可以使用你私钥对应的公钥&#xff0c…

推荐5款Windows桌面效率工具

今天我想分享一些自己比较喜欢的桌面端软件&#xff0c;还请大家包涵指正。如果你曾搜索过 Windows 效率工具推荐&#xff0c;对下文的软件或许有所了解。不过为了凑字数&#xff0c;我还是会再介绍一遍。 1.文件定位——Listary Listary 是我使用频率最高的软件之一&#xf…

Java重点源码回顾——HashMap1.8

1. 概述 在之前的文章中&#xff0c;我们介绍了HashMap1.7的源码&#xff0c;今天我们来看下HashMap1.8的源码。HashMap1.8相比于1.7最大的改变就是改变了1.7中采用数组链表的方式存储键值对&#xff0c;转而由数组链表红黑树的方式来存储键值对。HashMap1.8的底层结构如下图所…

RPC 好,还是 RESTful 好

OSI网络七层模型 RPC服务 RPC架构 同步调用与异步调用 流行的RPC框架 HTTP服务 总之 RPC主要是基于TCP/IP协议的&#xff0c;而HTTP服务主要是基于HTTP协议的&#xff0c;我们都知道HTTP协议是在传输层协议TCP之上的&#xff0c;所以效率来看的话&#xff0c;RPC当然是要更…

【python】py课后作业程序题5「PTA」

py字典合集7-1 Python猜数游戏7-2 jmu-python-分段函数7-3 循环求e的近似值&#xff08;高教社&#xff0c;《Python编程基础及应用》习题6-7&#xff09;7-4 jmu-python-判断是否构成三角形7-5 jmu-python-输入输出-计算字符串中的数7-6 jmu-python-随机生成密码7-7 jmu-pytho…

vscode使用CMake Tool插件构建第一个CMake的helloworld工程

vscode使用CMake Tool插件构建第一个CMake的helloworld工程一、linux环境准备1.1 CMake安装1.2 gcc/g的安装二、vscode 插件安装2.1 C扩展2.2 CMake Tool三、使用CMake构建第一个工程3.1 创建工程目录3.2 使用CMake Tool创建第一个Project3.3 Configure 第一个project四、构建工…

Java中序列化接口Serializable的serialVersionUID的作用

原文网址&#xff1a;Java中序列化接口Serializable的serialVersionUID的作用_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java中序列化接口Serializable的serialVersionUID的作用。 序列化与反序列化 含义 序列化&#xff1a;将java对象转化为字节序列。反序列化&#xff1a…

【mysql】索引的基本使用

文章目录1. 索引的声明与使用1.1 索引的分类1.2 创建索引1.2.1 创建表的时候创建索引&#xff08;隐式&#xff09;1.2.2 在已经存在的表上创建索引&#xff08;显式&#xff09;1、创建普通索引2、创建唯一索引3、主键索引4、创建单列索引5、创建联合索引6、创建全文索引7、创…

33、基于STM32的计时器(Proteus仿真+程序)

编号&#xff1a;33 基于STM32的计时器 功能描述&#xff1a; 使用proteus 软件设计一个基于STM32的家用计时器&#xff0c;该系统包含多个按键、LED运行灯和时间显示(时间显示是LCD1602) 其功能如下: 1、利用按键实现设置计时时间功能&#xff0c;时间格式:AB:CD:E 例如01:5…

【攻防世界】江苏工匠杯 Web easyphp

打开页面是一个代码审计的题目&#xff0c;是我不太熟悉的东西&#xff0c;但是没关系&#xff0c;我们可以学是吧&#xff0c;以下为源代码 <?php highlight_file(__FILE__); $key1 0; $key2 0;$a $_GET[a]; $b $_GET[b];if(isset($a) && intval($a) > 60…

【ES实战】ES集群节点迁移与缩容

ES集群节点迁移与缩容 文章目录ES集群节点迁移与缩容master节点迁移场景一场景二场景三data节点迁移数据迁移操作1、查询集群原来的配置2、清空节点数据3、检查是否排空数据迁移原则缩容前置检查项master节点迁移 场景一 集群上的master部署情况&#xff0c;一台机器上同时部…

智能化IT运维平台建设方案,基于智和信通运维体系的高敏捷二次开发

随着企业信息进程不断加速&#xff0c;运维人员需要面对越来越复杂的业务和越来越多样化的用户需求&#xff0c;不断扩展的应用需要越来越合理的模式、越来越智能的工具来保障运维能灵活便捷、安全稳定地开展。企业网络规模的不断扩大&#xff0c;从初期的几台服务器发展到庞大…

Python3 | vscode配置环境

vscode版本&#xff1a;1.74.2python版本&#xff1a;3.9.0win10系统 准备工作&#xff0c;在win10系统 1&#xff0c;安装python3&#xff0c;配置环境变量2&#xff0c;安装vscode 接下来&#xff1a;就可以在vscode配置python环境 1&#xff0c;下载和安装python插件 快…

2022年总结以及2023年的计划

2022年总结以及2023年的计划 文章目录2022年总结以及2023年的计划年终复盘投资理财学习方面前端方面&#xff1a;后端方面&#xff1a;数据库&#xff1a;读书&#xff1a;疫情工作爱情新的一年的展望按照每年的惯例&#xff0c;我每年的总结&#xff0c;复盘会在这几天完成。 …

软件测试精准定位BUG小技巧

目录 一、前置知识 二、定位技巧 一、前置知识 1. 熟透系统业务、团队成员情况 2. 熟悉使用F12或抓包工具 3. 了解HTTP/HTTPS协议&#xff0c;能够区分请求URL、请求头、请求体、入参、响应数据、响应码 4. 具备操作常规Linux命令&#xff0c;能否登入服务器查看Log日志&…

通信原理 | 波段的划分

波段(wave band) 在无线电技术中,波段(wave band)这个名词具有两种含义。 电磁波频谱的划分,例如长波、短波、超短波等波段。 发射机、接收机等设备的工作频率范围的划分。若把工作频率范围分成几个部分,这些部分也称为波段,例如三波段收音机等。 波段划分 波段通常是…

Python实现A股股市情感分析,含数据集可直接运行

Python实现A股股市情感分析&#xff0c;含数据集可直接运行 Stock Market Sentiment Analysis: 股市情感分析 完整代码下载地址&#xff1a;Python实现A股股市情感分析 情绪与股市 情绪与股市关系的研究由来已久&#xff0c;情绪是市场的一个重要影响因素已成为共识。 15年…

【设计模式】状态模式

状态模式 状态机 在操作系统的调度中会存在三种状态&#xff1a;运行、就绪、阻塞。 这是比较典型的状态机的例子。 做产品的时候&#xff0c;我们总能遇到一些比较复杂的逻辑问题&#xff0c;而普通的流程图&#xff0c;或时序图对于对象和状态的解读缺乏直观的描述。 这…

2.16 SPI协议的4种模式

文章目录1、简介2、SPI通讯模式2.1 模式02.2 模式12.3 模式22.4 模式32.5 总述1、简介 四线控制 SDO - 主设备数据输出&#xff0c;从设备输入 对应MOSI SDI - 主设备数据输入&#xff0c;从设备输出 对应MISO SCLK - 时钟信号&#xff0c;由主设备产生 CS - 从设备使能信号&a…