【RV1126】使用gpiolib框架

news2024/11/26 0:42:25

文章目录

  • 史上最简单:增加GPIO控制功能
  • 是如何实现的呢?GPIOLIB框架
    • Linux 驱动实现
  • 控制引脚输出高低电平
  • 综合测试

这一套非常方便!

史上最简单:增加GPIO控制功能

如果是想增加GPIO控制只需要修改设备树就可以做到!

下面这个设备树描述了复用功能,比如你想将UART3的RXD引脚用作GPIO输入中断,你就得在下面的设备树中将UART3节点部分注释掉。

kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi

然后在下面的设备树中写入该引脚的相关描述

kernel/arch/arm/boot/dts/pro-rv1126.dts

直接在rp_gpio节点下面添加buzzer即可:

// GPIO_FUNCTION_OUTPUT      0 
// GPIO_FUNCTION_INPUT       1
// GPIO_FUNCTION_IRQ         2
// GPIO_FUNCTION_FLASH       3
// GPIO_FUNCTION_OUTPUT_CTRL 4

//rpgpio init
rp_gpio {
	status = "okay";
	compatible = "rp_gpio";
	/**********************新增GPIO***************************/
	buzzer{
		gpio_num = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
		gpio_function = <0>;			 //0:output 1:input
	};
	/********************************************************/
	ir_led{
		gpio_num = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>;
		gpio_function = <0>;			 //0:output 1:input
	};
	
	bl_led{
		gpio_num = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>;
		gpio_function = <0>;			 //0:output 1:input
	};
	     
	gpio3c2{
		gpio_num = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; 
		gpio_function = <0>;
	};  
	...
};
  • compatible: 表示兼容的意思,它的作用就是定义一系列的字符串,用来指定硬件是否兼容相应的驱动,其它的部分接下来分析。

添加成功后又该如何操作控制 GPIO呢?

以控制摄像头补光灯亮灭为例,摄像头的补光灯对应的 proc 设备节点为:bl_led

在/proc/rp_gpio目录下:

echo 0 > bl_led # 该引脚输出低电平,关闭灯光
echo 1 > bl_led # 该引脚输出高电平,打开灯光
# 又以蜂鸣器为例
echo 0 > buzzer # 该引脚输出低电平,关闭蜂鸣器
echo 1 > buzzer # 该引脚输出高电平,打开蜂鸣器

是如何实现的呢?GPIOLIB框架

Linux 驱动实现

内核中 rp_gpio 这个驱动可以用来驱动它,经过 linux grep 命令的查看,可以看到该驱动源代码的位置位于drivers/rongpin/rp_gpio.c :

./drivers/rongpin/rp_gpio.c:275:static int rp_gpio_remove(struct platform_device *pdev)
./drivers/rongpin/rp_gpio.c:281:static const struct of_device_id
rp_gpio_of_match[] = {
./drivers/rongpin/rp_gpio.c:282:  { .compatible = "rp_gpio" },
./drivers/rongpin/rp_gpio.c:286:static struct platform_driver rp_gpio_driver = {
./drivers/rongpin/rp_gpio.c:287:  .probe = rp_gpio_probe,
./drivers/rongpin/rp_gpio.c:288:  .remove = rp_gpio_remove,
./drivers/rongpin/rp_gpio.c:290:        .name      = "rp_gpio",
./drivers/rongpin/rp_gpio.c:291:        .of_match_table =
of_match_ptr(rp_gpio_of_match),
./drivers/rongpin/rp_gpio.c:295:module_platform_driver(rp_gpio_driver);

这部分是厂商自己实现的 gpio 驱动,驱动代码提供了普适的 GPIO 操作接口:open、write、read:

static const struct file_operations gpio_ops = {
   .owner      = THIS_MODULE,
   .open      = gpio_open,
   .write      = gpio_write,
   .read      = gpio_read,
};

文件目录:kernel/drivers/rongpin/rp_gpio.c

驱动实现也很简单,使用的是 GPIOLIB 框架来控制这些 GPIO ,驱动会去设备树中搜索 compatible 属性:rp_gpio,找到了这个属性设备树和驱动就匹配上了,具体定义如下:

static const struct of_device_id rp_gpio_of_match[] =
{
    { .compatible = "rp_gpio" },
    { }
};
static struct platform_driver rp_gpio_driver =
{
    .probe = rp_gpio_probe,
    .remove = rp_gpio_remove,
    .driver = {
        .name = "rp_gpio",
        .of_match_table = of_match_ptr(rp_gpio_of_match),
    },
};
module_platform_driver(rp_gpio_driver);
MODULE_LICENSE("GPL");

驱动匹配成功正常工作时最先开始运行的是 probe 函数,也就是最先调用的是 rp_gpio_probe ,实现如下:

static int rp_gpio_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct device_node *child_np;
    struct device *dev = &pdev->dev;
    static struct proc_dir_entry *root_entry_gpio;
    enum of_gpio_flags  gpio_flags;
    int ret = 0;
    int gpio_cnt = 0;
    char gpio_name_num[20];
    int gpio_in_cnt = 0;
    int cnt = 0;
    //向内核申请内容给gpio_data变量
    gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct
                             rp_gpio_data), GFP_KERNEL);

    if (!gpio_data)
    {
        dev_err(&pdev->dev, "failed to allocate memory\n");
        return -ENOMEM;
    }

    //获取子节点的数量
    gpio_data->gpio_dts_num = of_get_child_count(np);
    printk("rp_gpio prepare build %d gpio\n", gpio_data->gpio_dts_num);

    if (gpio_data->gpio_dts_num == 0)
    {
        dev_info(&pdev->dev, "no gpio defined\n");
    }

    /* create node */
    //在proc目录下创建rp_gpio节点
    root_entry_gpio = proc_mkdir("rp_gpio", NULL);
    /*遍历所有节点*/
    for_each_child_of_node(np, child_np)
    {
        /* parse dts */
        /*从设备树中获取节点对应的GPIO编号以及标志*/
        gpio_data->rp_gpio_num[gpio_cnt].gpio_num =
            of_get_named_gpio_flags(child_np, "gpio_num", 0, &gpio_flags);

        if (!gpio_is_valid(gpio_data->rp_gpio_num[gpio_cnt].gpio_num))
        {
            return -1;
        }

        gpio_data->rp_gpio_num[gpio_cnt].gpio_name = (char*)child_np -> name;
        gpio_data->rp_gpio_num[gpio_cnt].action = gpio_flags;
        gpio_data->rp_gpio_num[gpio_cnt].gpio_ctrl = gpio_cnt;
        /*读取节点中的 32 位整数的值*/
        of_property_read_u32(child_np, "gpio_function", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_function));
        printk("rp_gpio request %s\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_name);

        /*通过获取到的gpio_function的数值进行判断*/
        switch(gpio_data->rp_gpio_num[gpio_cnt].gpio_function)
        {
            /*如果配置为输入,则走这个分支*/
            case GPIO_FUNCTION_INPUT : /* init input gpio */
                /*申请GPIO*/
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                }
                else
                {
                    printk("success request gpio %d in\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    /*设置GPIO方向为输入*/
                    gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    //gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
                    event_flag = gpio_flags;
                    of_property_read_u32(child_np, "send_mode", &(gpio_data->rp_gpio_num[gpio_cnt].send_mode));
                    of_property_read_u32(child_np, "gpio_event", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_event));
                    gpio_in_cnt++;
                }

                break;

            /*如果配置为输出,则走这个分支*/
            case GPIO_FUNCTION_OUTPUT : /* init output gpio */
                /*申请GPIO*/
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    return ret;
                }
                else
                {
                    /*设置GPIO的方向为输出,并且设置具体的数值*/
                    ret = gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, !gpio_data->rp_gpio_num[gpio_cnt].action);
                    printk("success request gpio%d out\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                }

                break;

            /*这个是厂商自己定义的,暂时不清楚是什么作用,应该是预留的,和GPIO_FUNCTION_OUTPUT的功
            能一致*/
            case GPIO_FUNCTION_FLASH :
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    return ret;
                }
                else
                {
                    /*设置GPIO的方向为输出,并且设置具体的数值*/
                    gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, !gpio_data->rp_gpio_num[gpio_cnt].action);
                    printk("success request gpio%d flash\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    gpio_cnt++;
                }

                break;
        }

        /*拼接GPIO名称以及编号*/
        sprintf(gpio_name_num, gpio_data -> rp_gpio_num[gpio_cnt].gpio_name, gpio_cnt);
        /*在PROC目录下创建设备节点*/
        proc_create(gpio_name_num, 0666, root_entry_gpio, &gpio_ops);
        gpio_cnt++;
    }

    /*如果GPIO的功能被定义为输入的作用*/
    if (gpio_in_cnt > 0)
    {
        /*定义一个定时器*/
        timer_setup(&(gpio_data->mytimer), send_event, 0);
        gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
        add_timer(&(gpio_data->mytimer));
        /*注册input子系统,将这个功能描述成一个输入设备*/
        /* init struct input_dev */
        gpio_data->input = devm_input_allocate_device(dev);
        gpio_data->input->name = "gpio_event";
        gpio_data->input->phys = "gpio_event/input1";
        gpio_data->input->dev.parent = dev;
        gpio_data->input->id.bustype = BUS_HOST;
        gpio_data->input->id.vendor = 0x0001;
        gpio_data->input->id.product = 0x0001;
        gpio_data->input->id.version = 0x0100;

        for(cnt = 0; cnt < gpio_cnt; cnt++)
        {
            if (gpio_data->rp_gpio_num[cnt].gpio_function == 1)
            {
                input_set_capability(gpio_data->input, EV_KEY, gpio_data -> rp_gpio_num[cnt].gpio_event);
            }
        }

        ret = input_register_device(gpio_data->input);
    }

    /*设置platform驱动数据,将数据挂载到pdev->dev.driver_data*/
    platform_set_drvdata(pdev, gpio_data);
    return 0;
}

控制引脚输出高低电平

使用UART3的RXD引脚
在这里插入图片描述修改:kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi

/*&uart3 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart3m2_xfer>;
	status = "okay";
};*/

修改:kernel/arch/arm/boot/dts/pro-rv1126.dts

//rpgpio init
	rp_gpio {
		status = "okay";
		compatible = "rp_gpio";

        buzzer{
            gpio_num = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
			gpio_function = <0>;			 //0:output 1:input
        };
...

重新编译Kernel和设备树就可以了。
在开发板上看看设备节点

[root@RV1126_RV1109:/]# ls /proc/rp_gpio/
bl_led  gpio0b7  gpio2b7  gpio2c2  gpio2c4  gpio3c2  otg_host
buzzer  gpio2b4  gpio2c1  gpio2c3  gpio2c6  ir_led

OK,出来了!
可以使用命令进行测试:

  • 引脚输出高电平:echo 1 > /proc/rp_gpio/buzzer
  • 引脚输出低电平:echo 0 > /proc/rp_gpio/buzzer

引脚接LED、蜂鸣器、继电器都可以。

综合测试

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd = 0;
    struct input_event buttons_event;
    unsigned long cur_ms = 0;

    static int flg = 0;
    
    fd = open("/dev/input/event3", O_RDWR);
    if (fd < 0)
        printf("can't open!\n");
 
    while (1)
    {
        read(fd, &buttons_event, sizeof(struct input_event));
 
//        if(buttons_event.type == EV_SYN)
//            continue;

        if((buttons_event.code == 11) && (buttons_event.value == 0x00))
        {
            if(flg == 0)
            {
                system("echo 1 > /proc/rp_gpio/buzzer");
                printf("echo 1\r\n");
                flg = 1;
            }
            else
            {
                system("echo 0 > /proc/rp_gpio/buzzer");
                printf("echo 0\r\n");
                flg = 0;
            }
        }
        
 
        cur_ms = (buttons_event.time.tv_sec * 1000) + (buttons_event.time.tv_usec/1000);
 
        //打印时间,事件类型,事件码,事件值
        printf("cur_ms:%ld type:0x%x code:%d value:%d\n",
            cur_ms,
            buttons_event.type,
            buttons_event.code,
            buttons_event.value);
    }
    close(fd);
    return 0;
}

编译:

arm-linux-gnueabihf-gcc key-buzzer.c

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

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

相关文章

谷粒商城第二天-项目环境搭建

目录 一、前言 二、学习的内容 一、虚拟平台的安装&#xff0c;远程连接虚拟机的工具的安装 二、Docker以及常用软件的安装 一、安装Docker&#xff1a; 二、安装相关软件 三、开发环境的统一 1. 这里就是调整Maven的下载依赖的地址&#xff0c;改用阿里云镜像地址 2. …

11.vue3医疗在线问诊项目 - _药品订单 ==> 支付页面、支付详情、支付结果、订单详情、物流信息、高德地图工具

11.vue3医疗在线问诊项目 - _药品订单 &#xff1e; 支付页面、支付详情、支付结果、订单详情、物流信息、高德地图工具 药品订单-支付页面-路由 目标&#xff1a;配置路由&#xff0c;分析药品支付组件结构 1&#xff09;路由与组件 {path: /medicine/pay,component: () >…

系列十一、MongoDB副本集

一、概述 MongoDB副本集&#xff08;Replica Set&#xff09;是有自动故障恢复功能的主从集群&#xff0c;有一个Primary节点和一个或者多个Secondary节点组成。副本集没有固定的主节点&#xff0c;当主节点发生故障时&#xff0c;整个集群会选举一个主节点 为系统提供服务以保…

大数据测试基本知识

常用大数据框架结构 1.大数据测试常用到的软件工具 工具推荐&#xff0c;对于测试数据构造工具有&#xff1a;Datafaker、DbSchema、Online test data generator等&#xff1b;ETL测试工具有&#xff1a;RightData、QuerySurge等&#xff1b;数据质量检查工具&#xff1a;great…

SpringBoot医药管理系统设计+第三稿+文档

博主介绍&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 项目名称 SpringBoot医药管理系统设计第三稿文档 视频演示 SpringBoot医药管理系统设计第三稿中期检查表ppt外文文献翻译文献综述开题任务书查重报告安装视频讲…

【计算机网络】第五章数据链路层-电子科技大学2023期末考试

第五章 数据链路层 学习目的 目的1&#xff1a;理解链路层服务的主要功能 差错检查、纠错 共享广播信道&#xff1a;多点接入问题(multiple access) 链路层寻址(link layer addressing) 局域网技术&#xff1a;Ethernet, VLANs 目的2&#xff1a;链路层技术的实现 点到点…

【Java入门】-- Java基础详解之【程序控制结构】

目录 1.程序流程控制介绍 2.顺序控制 3.分支控制if-else 4.嵌套分支 5.switch分支语句 6.for循环控制&#xff08;重点&#xff09; 7.while循环控制 8.do...while循环控制 9.多重循环控制&#xff08;重难点&#xff01;&#xff09; 10.跳转控制语句break 11.跳转…

计算机课程个性化内容推荐系统的设计与实现+文档等

博主介绍&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 项目名称 计算机课程个性化内容推荐系统的设计与实现文档等 视频演示 计算机课程个性化内容推荐系统的设计与实现 系统介绍 计算机课程个性化内容推荐系统的设…

Car Guide

文章目录 科目一第一章 机动车驾驶证申领和使用规定第一节 驾驶证的许可&#xff1f;种类和有效期第二节 驾驶证的申领第三节 驾驶证的使用第四节 驾驶考试第五节 违法记分制度 第二章 交通信号第一节 交通信号灯第二节 交通标志第三节 交通标线第四节 交警手势 第三章 道路交通…

Ansible之playbooks剧本

目录 一、playbooks1、playbooks简述2、playbooks剧本格式3、playbooks组成部分4、playbooks启动 二、playbook编写 apache 的yum安装部署剧本三、playbook编写 nginx 的yum安装并且能修改其监听端口的剧本四、 playbook的定义、引用变量1、基础变量的定义与引用2、引用fact信息…

Stable Diffusion webui 基础参数学习

哈喽&#xff0c;各位小伙伴们大家好&#xff0c;最近一直再研究人工智能类的生产力&#xff0c;不得不说随着时代科技的进步让人工智能也得到了突破性的发展。而小编前段时间玩画画也是玩的不可自拔&#xff0c;你能想想得到&#xff0c;一个完全不会画画的有一天也能创作出绘…

【计算机网络】第一章概论-电子科技大学2023期末考试

相关术语 URI&#xff1a;Uniform Resource Identifier 统一资源标识符&#xff0c;指的是一个资源 URL&#xff1a;Uniform Resource Location 统一资源定位符&#xff0c;URI的子集&#xff0c;用地址定为的方式指定一个资源 URN&#xff1a;Uniform Resource Name 统一资…

MySQL事务相关笔记

杂项 InnoDB最大特点&#xff1a;支持事务和行锁&#xff1b; MyISAM不支持事务 介绍 一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元&#xff0c;只有当事务中的所有操作都正常执行完了&#xff0c;整个事务才会被提交给数据库。事务有如下特性…

【使用指导】wifi蓝牙二合一模块LCS2028与服务器的数据收发功能测试指导

在物联网智能家居、智能照明、智能楼宇、智慧工厂、智能制造等领域的数据透传、智能控制应用中&#xff0c;支持UART串口通信的低功耗WiFi蓝牙二合一模块应用极为广泛。模块性能测试环节中会测试模块与服务器的数据收发功能&#xff0c;确保功能性能够满足项目应用需求。本篇就…

深入理解 SpringBoot 日志框架:从入门到高级应用——(四)Logback 输出日志到 QQ邮箱

文章目录 获取 QQ 邮箱授权码添加依赖编写 SMTPAppender运行结果 要将 Logback 输出日志到 QQ 邮箱&#xff0c;需要执行以下步骤&#xff1a; 在 QQ 邮箱中获取授权码。在你的 SpringBoot 项目中添加 Logback 依赖和 SMTP 协议实现库&#xff0c;例如 Email 依赖。在 Logback…

NOTA-Me-Tetrazine,NOTA-甲基四嗪,大环化合物具有良好的配位和鳌合能力

文章关键词&#xff1a;甲基四嗪修饰大环配体&#xff0c;双功能螯合剂&#xff0c;大环化合物 ●中文名&#xff1a;NOTA-甲基四嗪 ●英文名&#xff1a;NOTA-Me-Tetrazine ●外观以及性质&#xff1a; 西安凯新生物科技有限公司供应的​NOTA-Me-Tetrazine中四嗪修饰大环配体&…

LaTeX插入参考文献

接着上一篇&#xff0c;用EndNote组织参考文献&#xff0c;然后再导入到LeTex中感觉不太好用&#xff0c;然后就学习了一下BibTeX来管理参考文献&#xff0c;发现还可以&#xff0c;这里记录一下&#xff0c;方便以后查阅。 LaTeX插入参考文献 thebibliographyBibTeX参考资料 t…

总结901

目标规划&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化5讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 每日规划 今日已做 1.回环复习之前背过的文章。 2.背单词&#xf…

想了解3,4,6-Tri-O-acetyl-D-galactal,4098-06-0,D-三乙酰半乳糖烯?点击这里查看详细信息!

文章关键词&#xff1a;糖化学试剂&#xff0c;三乙酰半乳糖烯 3,4,6-Tri-O-acetyl-D-galactal |3,4,6-O-三乙酰基-D-半乳糖烯&#xff0c;D-三乙酰半乳糖烯|CAS&#xff1a;4098-06-0 | 纯度&#xff1a;95%一、结构式&#xff1a; 二、试剂参数信息&#xff1a; CAS&#x…

Redis从入门到精通进阶篇之持久化RDB和AOF机制详解

文章目录 RDB持久化AOF持久化RDB与AOF的混合持久化 Redis 6的持久化机制主要有两种&#xff1a;RDB&#xff08;Redis DataBase&#xff09;和AOF&#xff08;Append Only File&#xff09;。本文将详细介绍这两种持久化方式的工作原理和配置要点。 RDB持久化 RDB持久化是将当…