itop-3568 开发板系统编程学习笔记(19)GPIO 应用编程

news2024/11/13 16:44:39

【北京迅为】嵌入式学习之Linux系统编程篇 https://www.bilibili.com/video/BV1zV411e7Cy/ 个人学习笔记

文章目录

  • 使用 sysfs 方式操作 GPIO
  • GPIO 应用编程

使用 sysfs 方式操作 GPIO

和上一篇笔记 LED 应用编程一样,GPIO 也可以通过 sysfs 方式来控制。

在串口终端命令行进入 /sys/class/gpio 目录,

在这里插入图片描述

在该目录下可以看到 export、unexport 及几个 gpiochipx 文件(x 为 0,32,64,96,128,分别对应 GPIO0,GPIO1,GPIO2,GPIO3,GPIO4,即每一个 gpiochipx 对应一组 GPIO)

进入 gpiochip32 目录,

在这里插入图片描述

base 就是 gpiochipx 的 x 值,
label 为 gpiochipx 所对应的 GPIO 组,比如 gpiochip32 的 label 为 “gpio1”,
ngpio 为 gpiochipx 所管理的 gpio 数量,该属性为 32(一个 gpio 组管理 32 个 gpio)。

接下来进入正题——如何操作某一个 gpio,

要实现 sysfs 操作 gpio, 需要用上 /sys/class/gpio 下的 export 文件,

export 用来导出指定的 GPIO,只有 GPIO 被导出到 /sys/class/gpio 目录后才可以操作它,export 是一个只写文件,导出方法为 echo num > export(num 为 gpio 编号)

关于 rk3568的 GPIO 编号,到网上找资料发现,RK3568 的 GPIO 编号很好计算,以 GPIO0_B7 为例,其 bank 为 0,group 为 1(A 为 0,以此类推),X 为 7,那么它的 pin 编号为 bank * 32 + group * 8 + X = 15,直白点讲,GPIO 的编号就是它在所有 IO 中所属序号

GPIO0_B7 也是我们需要用到的 IO,对应开发板上的 LED,接下来我们使用 echo 15 > export 导出 GPIO0_B7,不过这里我失败了,报了错误:write error: Device or resource busy(因为 GPIO0_B7 已经被用在 /sys/class/leds 中)

解决办法是将设备树里的 leds 节点注释掉,

在这里插入图片描述

重新编译内核然后烧录到开发板后再次导出 GPIO0_B7,这次没有出现报错了,

在这里插入图片描述

GPIO0_B7 导出成功后,会在 /sys/class/gpio 目录下新增 gpio15 文件夹,进入该文件夹,会看到 active_low,direction,edge 和 value 等属性,

在这里插入图片描述

direction 为 GPIO 的输入输出模式,“in” 为输入模式,“out” 为输出模式;
value 为 gpio 电平状态,默认 1 表示高电平,0 表示低电平;
active_low 用于控制极性,该属性默认为 0,当 active_low 为 1 时,value 极性反向,即 value 为 1 表示引脚为低电平,value 为 0 表示引脚为高电平;
edge 用于控制中断触发方式,只有 GPIO 为输入模式时,中断才可用。可取值包括:

属性值含义
none非中断
rising上升沿触发
falling下降沿触发
both双边沿触发

比如现在要让 GPIO0_B7 对应的 LED 点亮,只需要在 /sys/class/gpio/gpio15 目录下输入:

echo out > direction
echo 1 > value

在这里插入图片描述

测试结构就是 LED 被点亮,开发板效果就不展示了。

GPIO 应用编程

实验代码

参考原教程

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

int main(int argc, char** argv)
{
	int fd1, fd2, fd3;
	int ret = 0;
	int mode = 0, val = 0, edge = 0;
	char gpio_path[50] = {0};
	char tmp_path[60] = {0};
	char buff[20] = {0};
	
	// 判断输入的参数
	if(argc >= 3 && argc <= 4) 
	{
		// GPIO 编号不符合要求
		if((atoi(argv[1]) < 0 )|| (atoi(argv[1]) > 159))
		{
			ret = 1;
			goto format_err;
		}
		
		// 输出模式
		if(!strcmp(argv[2], "out"))
		{
			mode = 0;  // 输出模式
			
			// 参数有误
			if(argc != 4)
			{
				ret = 2;
				goto format_err;
			}
			
			if((atoi(argv[3]) != 0) && (atoi(argv[3]) != 1))
			{
				ret = 3;
				goto format_err;
			}
		} 
		// 输入模式
		else if(!strcmp(argv[2], "in"))
		{
			// 无中断
			if(argc == 3)
			{
				mode = 1; // 无中断输入模式
			}
			// 有中断
			else if(argc == 4)
			{
				mode = 2; // 有中断触发模式
				
				// 判断中断触发模式
				if(!strcmp(argv[3], "none"))
					edge = 0;
				else if(!strcmp(argv[3], "rising"))
					edge = 1;
				else if(!strcmp(argv[3], "falling"))
					edge = 2;
				else if(!strcmp(argv[3], "both"))
					edge = 3;
				else // 格式有误
				{
					ret = 4;
					goto format_err;
				}
			}
		}	
		else // 模式不符合要求
		{
			ret = 5;
			goto format_err;
		}	
	}
	else // 参数数量不符合要求
	{
		ret = 6;
		goto format_err;
	}

format_err:
	if(ret != 0) // 格式有误
	{
		printf("The right format: ./app <GPIO_NUM> <DIR> [VAL] [EDGE]\n\
		MODE1: ./app <0~159> out <0/1>\n\
		MODE2: ./app <0~159> in\n\
		MODE3: ./app <0~159> in <none/rising/falling/both>\n");	
		
		switch(ret)
		{
			case 1: printf("ERROR: gpio num is invalid.\n");break;
			case 2: printf("ERROR: value not specified.\n");break;
			case 3: printf("ERROR: value is invalid.\n");break;
			case 4: printf("ERROR: edge is invalid.\n");break;
			case 5: printf("ERROR: direction is invalid.\n");break;
		}		
		return 0;
	}
	
	// 合成路径,如 "/sys/class/gpio/gpio15"
	sprintf(gpio_path , "/sys/class/gpio/gpio%s", argv[1]);
	
	// gpio_path 不存在
	if(access(gpio_path, F_OK)) 
	{
		int fd, ret;
		fd = open("/sys/class/gpio/export", O_WRONLY);
		if(fd < 0)
		{
			printf("/sys/class/gpio/export open failed.\n");
			return 0;
		}
		printf("/sys/class/gpio/export open successfully.\n");
		
		ret = write(fd, argv[1], strlen(argv[1]));
		if(ret < 0)
		{
			printf("/sys/class/gpio/export write failed.\n");
			return 0;
		}
		close(fd);
	}
	
	// 打开 /sys/class/gpio/gpiox/direction 
	sprintf(tmp_path, "%s/direction", gpio_path);
	fd1 = open(tmp_path, O_WRONLY);
	if(fd1 < 0)
	{
		printf("%s open failed.\n", tmp_path);
		return 0;
	}
	printf("%s open successfully.\n", tmp_path);
	
	// 打开 /sys/class/gpio/gpiox/value
	sprintf(tmp_path, "%s/value", gpio_path);
	fd2 = open(tmp_path, O_RDWR);
	if(fd2 < 0)
	{
		printf("%s open failed.\n", tmp_path);
		return 0;
	}
	printf("%s open successfully.\n", tmp_path);
	
	// 打开 /sys/class/gpio/gpiox/edge
	sprintf(tmp_path, "%s/edge", gpio_path);
	fd3 = open(tmp_path, O_WRONLY);
	if(fd3 < 0)
	{
		printf("%s open failed.\n", tmp_path);
		return 0;
	}
	printf("%s open successfully.\n", tmp_path);
	
	// 输出模式
	if(mode == 0)
	{
		// 设置输出方向
		ret = write(fd1, argv[2], strlen(argv[2]));
		if(ret < 0)
		{
			printf("%s/direction write failed.\n", gpio_path);
			return 0;
		}
		printf("%s/direction write successfully.\n", gpio_path);
		
		// 设置电平
		ret = write(fd2, argv[3], strlen(argv[3]));
		if(ret < 0)
		{
			printf("%s/value write failed.\n", gpio_path);
			return 0;
		}
		printf("%s/value write successfully.\n", gpio_path);
	}
	
	// 无中断输入模式
	if(mode == 1)
	{
		// 设置输出方向
		ret = write(fd1, argv[2], strlen(argv[2]));
		if(ret < 0)
		{
			printf("%s/direction write failed.\n", gpio_path);
			return 0;
		}
		printf("%s/direction write successfully.\n", gpio_path);
		
		// 获取 IO 电平
		ret = read(fd2, buff, 1);
		if(ret < 0)
		{
			printf("%s/value read failed.\n", gpio_path);
			return 0;
		}
		printf("%s/value is %s.\n", gpio_path, buff);
	}
	
	// 中断触发模式
	if(mode == 2)
	{
		// 设置输出方向
		ret = write(fd1, argv[2], strlen(argv[2]));
		if(ret < 0)
		{
			printf("%s/direction write failed.\n", gpio_path);
			return 0;
		}
		printf("%s/direction write successfully.\n", gpio_path);
		
		// 设置触发模式
		ret = write(fd3, argv[3], strlen(argv[3]));
		if(ret < 0)
		{
			printf("%s/edge write failed.\n", gpio_path);
			return 0;
		}
		printf("%s/edge write successfully.\n", gpio_path);
	}

	// 关闭设备文件
	close(fd1);
	close(fd2);
	close(fd3);
	return 0;
}

实验结果:

在这里插入图片描述

由于时间原因,中断功能没有做测试(代码中只有设置了中断触发的功能,没有做相应测试),原文测试代码在设置中断触发后,使用 poll 的方式轮询监测 GPIO0_B7(POLLPRI 事件),从而验证中断触发功能。

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

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

相关文章

Elasticsearch:使用 Elastic APM 监控 Android 应用程序(一)

作者&#xff1a;Alexander Wert, Cesar Munoz 人们通过私人和专业的移动应用程序在智能手机上处理越来越多的事情。 拥有成千上万甚至数百万的用户&#xff0c;确保出色的性能和可靠性是移动应用程序和相关后端服务的提供商和运营商面临的主要挑战。 了解移动应用程序的行为、…

【计算机视觉】必须了解的图像数据底层技术

计算机视觉的主要目的是让计算机能像人类一样甚至比人类更好地看见和识别世界。计算机视觉通常使用C、Python和MATLAB等编程语言&#xff0c;是增强现实&#xff08;AR&#xff09;的一项重要技术。 文章目录 一、引言二、什么是计算机视觉&#xff08;Computer Vision&#xf…

Flink窗口函数

1.什么是窗口函数 Flink窗口函数是指对数据流中的数据进行分组和聚合操作的函数。 FlinkSQL支持对一个特定的窗口的聚合。例如有用户想统计在过去的1分钟内有多少用户点击了某个的网页。在这种情况下&#xff0c;我们可以定义一个窗口&#xff0c;用来收集最近一分钟内的数据…

codemirror 5前端代码编辑器资料整理。

CodeMirror 是基于js的源代码编辑器组件&#xff0c;它支持javascript等多种高级语言&#xff0c;tampermonkey内置的代码编辑器就是基于它。它的按键组合方式兼容vim&#xff0c;emacs等&#xff0c;调用者还可自定义”自动完成“的列表窗口&#xff0c;自由度极高&#xff0c…

Android studio 按钮状态列表

1.创建一个drawable&#xff0c;类型selector 。 <?xml version"1.0" encoding"utf-8"?> <selector xmlns:android"http://schemas.android.com/apk/res/android"><!--被按下状态 --><item android:state_pressed"…

信息安全复习三:古典密码之设计好的密码算法

一.章节梗概 讨论以下算法&#xff0c;理解怎么设计好的密码算法的关键问题 1.Caesar cipher 2.单字母表密码 3.Playfairmima 4.维吉尼亚密码 5.自动生成密码 二.Caesar cipher 2.1 穷举攻击 穷举攻击定义&#xff1a;尝试所有密钥直到有一个合法密钥能够把密文还原成明文&…

软考软件设计师 操作系统笔记

操作系统地位 程序顺序执行&#xff08;进程管理&#xff09; 程序顺序执行的特征&#xff0c;顺序性封闭性可再现性 前趋图 P1结束后 V操作 SS1 P2操作前先执行S S -1 此时S0 一个箭头对应一个信号量 程序并发执行和前驱图 找到输入i计算c输出p&#xff0c;如果找不到就…

结合实战,浅析GB/T28181(十)——媒体流保活

1 问题现象 在实际项目对接过程中&#xff0c;我们有时会碰到这样的问题&#xff1a;视频正在播放着&#xff0c;突然停止了。然后ping一下&#xff0c;也能ping通&#xff01;下级平台或上级平台看起来也在线&#xff0c;看起来不是网络的问题。这到底咋回事呢&#xff1f;一…

实验室电磁铁EM4S的技术参数

锦正茂科技自主研发的电磁铁&#xff0c;可以通过更换电磁铁极头在一定范围内改善磁场的大小和磁场的均匀度 &#xff0c;并且可以通过调整极头间距改变磁场的大小&#xff0c;该种类型的电磁铁能够很好的与客户设计的磁场平台兼容。主要用于磁滞现象研究、磁化系数测量、霍尔效…

公派访问学者签证申请需提交的材料

公派访问学者签证申请需提交的材料: 1、《公派留学人员基本情况表》。 2、留学基金委出具的《同意派出函》复印件一份(特殊项目除外)。 3、录取文件复印件一份。(如您是改派国别、延期派出、缩短在外留学期限等&#xff0c;还要提交留学基金委出具的相关文件复印件一份)。 4…

dtype = torch.float32到底有什么用

dtype torch.float32到底有什么用 解决&#xff1a;RuntimeError: expected scalar type Long but found Float 先看一个例子 要计算 z x0 w1x1 w2x2 其中w [-0.2,0.15,0.15] 于是你开始尝试 其中torch.mv用于矩阵*向量 此时你发现他需要你提供float格式的数据 你查看发…

(一)MYSQL实战——用户权限控制管理

前言 mysql作为目前最流行的关系型数据库&#xff0c;被广泛使用在各种系统服务中&#xff0c;本节内容主要是关于mysql数据库在生产环境中用户、权限等相关内容的设置说明&#xff0c;便于我们更好的使用和管理我们的数据库。 正文 SQL的分类 ①数据查询语言&#xff08;Da…

SpringBoot日志

日志有什么用&#xff1f; 日志最主要的用途就是排查和定位错误&#xff0c;除此之外&#xff0c;日志还可以将错误信息具体化&#xff0c;比如时间、位置等。 如何打印日志 使用Logger类 使用方法&#xff1a; Logger log LoggerFactory.getLogger&#xff08;类名/类名…

MVCC实现原理

MVCC实现原理 主要依赖隐藏字段undo logundolog生成的记录链 Read View可见性规则三个全局属性具体的比较规则 MVCC的整体处理流程RC、RR级别下的InnoDB快照读有什么不同 主要依赖 mvcc的实现原理主要依赖于记录中的三个隐藏字段&#xff08;对用户来说是不可见的&#xff09;…

【Spring Cloud Alibaba】8.路由网关(Gateway)

文章目录 简介什么是 Spring Cloud Gateway功能介绍工作流程 开始搭建创建项目修改POM文件添加启动类添加配置文件启动项目测试 网关全局过滤创建全局过滤器测试 结尾 简介 接下来对服务消费者添加路由网关来实现统一访问接口&#xff0c;本操作先要完成之前的步骤&#xff0c…

API 自动化测试难点总结与分享

笔者是 API 管理工具的项目参与者之一&#xff0c;在日常工作中会经常遇到 API 自动化测试难点&#xff0c;我决定总结分享给大家&#xff1a; API 自动化测试的难点包括&#xff1a; 接口的参数组合较多&#xff0c;需要覆盖各种可能的情况。 接口的状态和数据关联较多&#…

DJ4-1 存储器的层次结构

目录 4.1.1 存储器的层次结构 1. 主存储器&#xff08;内存&#xff0c;主存&#xff0c;可执行存储器&#xff09; 2. 寄存器 3. 高速缓存 4. 磁盘缓存 存储器层次结构的特点 4.1.2 存储器管理的目的和功能 1. 主存储器的分配和管理 2. 提高主存储器的利用率 3. 扩…

基于ArcGIS Pro、Python、USLE、INVEST模型等多技术融合的生态系统服务构建生态安全格局

近年来&#xff0c;由于社会经济的快速发展和人口增长&#xff0c;社会活动对环境的压力不断增大&#xff0c;人地矛盾加剧。虽然全球各国在生态环境的建设和保护上已取得不少成果&#xff0c;但还是未从根本上转变生态环境的恶化趋势&#xff1b;生态破坏、环境退化、生物多样…

OceanMind海睿思入选“2023爱分析·智能制造最佳实践案例”

近日&#xff0c;中国领先的产业数字化研究与咨询机构 爱分析 发布了《2023爱分析智能制造最佳实践案例》&#xff0c;该奖项旨在肯定智能制造领域领先企业的数字化创新应用和最佳实践。 中新赛克海睿思凭借为星宇股份构建的“星宇车灯数据智能解决方案”入选智能制造最佳实践…

AWR1642毫米波雷达实测行人、自行车和汽车等目标

本文编辑 | 调皮哥的小助理 AWR1642因为最大中频带宽 固定只有5MHz&#xff0c;最大中频带宽是发射信号与回波信号混频之后得到的最大中频频率&#xff0c;即代表着最大的回波延迟时间。 因此根据雷达方程和目标最大探测距离公式&#xff0c;如下所示&#xff1a; 复采样&…