嵌入式Linux应用开发笔记:GPIO编程

news2024/11/18 15:36:10

文章目录

  • 目的
  • 基础说明
  • 代码示例
    • 数字输出
    • 数字输入
    • 外部中断
  • 总结

目的

GPIO嵌入式设备中最基础的外设,使用上也是非常频繁的。这篇文章将记录下应用程序中GPIO操作相关内容。

这篇文章中内容均在下面的开发板上进行测试:
《新唐NUC980使用记录:自制开发板(基于NUC980DK61YC)》

开发板中提供了两组共四个直连到GPIO口上的轻触按钮和发光二极管,可以方便地进行GPIO功能测试:
在这里插入图片描述

这篇文章是在下面文章基础上进行的:
新唐NUC980使用记录(5.10.y内核):在用户应用中使用GPIO

基础说明

通常Linux内核中会启用 Device Drivers -> GPIO Support -> /sys/class/gpio/...(sysfs interface) 功能。这样的话系统启动后可以通过 /sys/class/gpio/ 目录下的文件来操作GPIO,基本的一些操作如下:

  • 启用GPIO口
    /sys/class/gpio/export 文件写入 GPIO编号 即可启用对应GPIO口;
    启用成功后会生成 /sys/class/gpio/gpio编号/ 目录,之后通过该目录中的文件对该特定的GPIO口进行操作;
  • 设置GPIO口方向(输入或输出)
    /sys/class/gpio/gpioX/direction 文件写数据可以设置端口方向, in 表示输入、 out 表示输出;
  • 设置或读取GPIO口电平
    /sys/class/gpio/gpioX/value 文件可以设置或者读取端口电平值,默认情况下 0 表示低电平、 1 表示高电平;
  • 设置GPIO口外部中断触发方式
    /sys/class/gpio/gpioX/edge 文件写数据可以设置外部中断触发方式, none 无、 rising 上升沿触发、 falling 下降沿触发、 both 双边触发;(有没有对应方式还得看硬件和设置等)
  • 取消使用GPIO口
    /sys/class/gpio/unexport 文件写入 GPIO编号 即可取消使用对应端口;
  • /sys/class/gpio/gpioX/active_low 用于设置逻辑值翻转,默认为0,即低电平逻辑值为0,高电平逻辑值为1,该文件值设置为1时将翻转逻辑值;

对于本文演示用的开发板上的GPIO口而言,其编号计算如下:
PB13 = 32 x 1(PA) + 13 = 45
PF10 = 32 x 5(PA/PB/PC/PD/PE) + 10 = 170
PE10 = 32 x 4(PA/PB/PC/PD) + 10 = 138
PE12 = 32 x 4(PA/PB/PC/PD) + 12 = 140

基于上面这些基础内容,嵌入式Linux中对于GPIO口的操作就是对上上面一些文件的操作。

代码示例

进入并创建相关目录:

cd ~/nuc980-sdk/
mkdir -p apps/gpio
cd apps/gpio/
gedit main.c
# 或者使用VS Code
# code .
# 创建 main.c 文件

编写代码后使用下面方式编译,然后拷贝程序到开发板上:

export PATH=$PATH:/home/nx/nuc980-sdk/buildroot-2023.02/output/host/bin
arm-linux-gcc main.c

# 开发板启用了SSH的话可以使用SCP命令将程序通过网络拷贝到开发板中
scp a.out root@192.168.31.142:/root/

数字输出

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

int open_write(const char *file, const char *value)
{
    int fd, len;

    fd = open(file, O_WRONLY); // 以只写的方式打开文件
    if (fd < 0)
    {
        return -1;
    }

    len = write(fd, value, strlen(value)); // 向文件写数据
    if (len != strlen(value))
    {
        return -2;
    }

    close(fd);
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("Usage: %s <gpio> <value>\n", argv[0]);
        return -1;
    }

    char *file_export = "/sys/class/gpio/export";
    char file_gpiox_direction[64];
    char file_gpiox_value[64];

    sprintf(file_gpiox_direction, "%s/gpio%s/direction", "/sys/class/gpio", argv[1]);
    sprintf(file_gpiox_value, "%s/gpio%s/value", "/sys/class/gpio", argv[1]);

    if (access(file_gpiox_value, F_OK)) // 检查文件是否存在
    {
        if (open_write(file_export, argv[1])) // 导出GPIO
        {
            printf("open_write %s %s failed!\n", file_export, argv[1]);
        }
    }

    if (open_write(file_gpiox_direction, "out")) // 设置为输出模式
    {
        printf("open_write %s %s failed!\n", file_gpiox_direction, argv[1]);
    }

    if (open_write(file_gpiox_value, argv[2])) // 设置输出值
    {
        printf("open_write %s %s failed!\n", file_gpiox_value, argv[2]);
    }

    return 0;
}

在这里插入图片描述
上面操作时板子上对应的引脚上接的LED灯会有响应。

数字输入

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

int open_write(const char *file, const char *value)
{
    int fd, len;

    fd = open(file, O_WRONLY); // 以只写的方式打开文件
    if (fd < 0)
    {
        return -1;
    }

    len = write(fd, value, strlen(value)); // 向文件写数据
    if (len != strlen(value))
    {
        return -2;
    }

    close(fd);
    return 0;
}

int open_read(const char *file, char *value, int nbytes)
{
    int fd, len;

    fd = open(file, O_RDONLY); // 以只读的方式打开文件
    if (fd < 0)
    {
        return -1;
    }

    len = read(fd, value, nbytes); // 从文件读取数据
    if (len != nbytes)
    {
        return -2;
    }

    close(fd);
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("Usage: %s <gpio>\n", argv[0]);
        return -1;
    }

    char *file_export = "/sys/class/gpio/export";
    char file_gpiox_direction[64];
    char file_gpiox_value[64];

    sprintf(file_gpiox_direction, "%s/gpio%s/direction", "/sys/class/gpio", argv[1]);
    sprintf(file_gpiox_value, "%s/gpio%s/value", "/sys/class/gpio", argv[1]);

    if (access(file_gpiox_value, F_OK)) // 检查文件是否存在
    {
        if (open_write(file_export, argv[1])) // 导出GPIO
        {
            printf("open_write %s %s failed!\n", file_export, argv[1]);
        }
    }

    if (open_write(file_gpiox_direction, "in")) // 设置为输入模式
    {
        printf("open_write %s %s failed!\n", file_gpiox_direction, argv[1]);
    }

    char value[2] = {0};
    if (open_read(file_gpiox_value, value, 1)) // 读取端口值
    {
        printf("open_read %s failed!\n", file_gpiox_value);
    }
    else
    {
        printf("open_read %s value is %s \n", file_gpiox_value, value);
    }

    return 0;
}

在这里插入图片描述
上面操作时板子上对应的引脚上接的按钮在按下和松开时得到的值会不同。

外部中断

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

int open_write(const char *file, const char *value)
{
    int fd, len;

    fd = open(file, O_WRONLY); // 以只写的方式打开文件
    if (fd < 0)
    {
        return -1;
    }

    len = write(fd, value, strlen(value)); // 向文件写数据
    if (len != strlen(value))
    {
        return -2;
    }

    close(fd);
    return 0;
}

int open_wait_read(const char *file, char *value, int nbytes, int timeout)
{
    int ret, len;
    struct pollfd fds;
    nfds_t nfds = 1;

    fds.fd = open(file, O_RDONLY); // 以只读的方式打开文件
    if (fds.fd < 0)
    {
        return -1;
    }
    read(fds.fd, value, nbytes); // 先读取一次,以免触发第一次不期望的中断
    lseek(fds.fd, 0, SEEK_SET);  // 移动文件指针到头部

    fds.events = POLLPRI;            // 有紧急数据需要读取
    ret = poll(&fds, nfds, timeout); // 等待事件触发, timeout 为 -1 时将不会超时
    if ((ret > 0) && (fds.revents & POLLPRI))
    {
        len = read(fds.fd, value, nbytes); // 从文件读取数据
        if (len != nbytes)
        {
            return -2;
        }
    }
    else if (ret == 0)
    {
        return -3; // timeout
    }
    else
    {
        return -4; // poll error
    }

    close(fds.fd);
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        printf("Usage: %s <gpio> <edge> <timeout>\n", argv[0]);
        printf("       <edge>: none, rising, falling, both\n");
        return -1;
    }

    char *file_export = "/sys/class/gpio/export";
    char file_gpiox_direction[64];
    char file_gpiox_value[64];
    char file_gpiox_edge[64];

    sprintf(file_gpiox_direction, "%s/gpio%s/direction", "/sys/class/gpio", argv[1]);
    sprintf(file_gpiox_value, "%s/gpio%s/value", "/sys/class/gpio", argv[1]);
    sprintf(file_gpiox_edge, "%s/gpio%s/edge", "/sys/class/gpio", argv[1]);

    if (access(file_gpiox_value, F_OK)) // 检查文件是否存在
    {
        if (open_write(file_export, argv[1])) // 导出GPIO
        {
            printf("open_write %s %s failed!\n", file_export, argv[1]);
        }
    }

    if (open_write(file_gpiox_direction, "in")) // 设置为输入模式
    {
        printf("open_write %s %s failed!\n", file_gpiox_direction, argv[1]);
    }

    if (open_write(file_gpiox_edge, argv[2])) // 设置中断触发方式
    {
        printf("open_write %s %s failed!\n", file_gpiox_edge, argv[2]);
    }

    char value[2] = {0};
    int ret = open_wait_read(file_gpiox_value, value, 1, atoi(argv[3])); // 等待事件触发读取数据
    if (ret == 0)
    {
        printf("open_wait_read %s value is %s \n", file_gpiox_value, value);
    }
    else if (ret == -3)
    {
        printf("open_wait_read timeout!\n", file_gpiox_value);
    }
    else
    {
        printf("open_wait_read %s failed!\n", file_gpiox_value);
    }

    return 0;
}

在这里插入图片描述

总结

嵌入式Linux中对基础GPIO的操作非常简单,其实就是对文件的操作而已。

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

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

相关文章

15、STM32驱动sht35温湿度传感器

本文使用模拟IIC驱动sht35温湿度传感器 踩坑点&#xff1a;购买的模块IIC上拉电阻为10KΩ&#xff0c;会导致IIC不稳定&#xff0c;抗干扰差&#xff0c;容易导致时序错误&#xff1b;建议更换为4.7KΩ 一、CubeMx配置 其余默认&#xff0c;生成工程 1、us精确延时 Delay_Dr…

2023年电子、通信与控制工程国际会议(SECCE 2023)

会议简介 Brief Introduction 2023年电子、通信与控制工程国际会议(SECCE 2023) 会议时间&#xff1a;2023年8月11日-13日 召开地点&#xff1a;韩国济州岛 大会官网&#xff1a;www.isecce.org 2023年电子、通信与控制工程国际会议(SECCE 2023)将围绕“电子、通信与控制工程”…

Spring Boot 整合流程引擎 Flowable(附源码地址)

一、导入依赖 flowable依赖&#xff1a; <dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version> </dependency>pom.xml: <?xml version"1…

驱动页面性能优化的3个有效策略

目录 引言 背景 前端性能优化 测试视角的解法 性能问题的发现 性能数据的采集 性能指标的确定 性能问题的分析 如何衡量性能问题严重性 分析性能瓶颈-分析思路 分析结论关键思路 引言 测试通过发现、分析、验证三板斧&#xff0c;驱动推进页面性能优化快速有效&…

关注 | 蛙色元宇宙,正式成为XRMA联盟成员单位

中国虚拟现实与元宇宙产业峰会&#xff0c;2023年3月22日于杭州圆满结束&#xff0c;在杭州市人民政府、浙江省经济和信息化厅指导&#xff0c;由杭州市经济和信息化局、杭州市西湖区人民政府主办&#xff0c;中国信息通信研究院承办。 蛙色元宇宙作为元宇宙的领先企业之一&…

【AI面试】CrossEntropy Loss 、Balanced Cross Entropy、 Dice Loss 和 Focal Loss 横评对比

样本不均衡问题一直是深度学习领域一个不可忽略的问题&#xff0c;常说的长尾效应&#xff0c;说的就是这个问题。一类占据了主导地位&#xff0c;导致其他类无论怎么优化&#xff0c;都不能好转。 无论是纯纯的分类任务&#xff0c;还是稍微复杂一些的目标检测任务和分割任务…

关于java在成员/全局变量上不同类型赋值遇到的问题(值传递)

一个疑惑 文件简介回答参考文献 文件简介 class ss{static class Student{int id;String name; /*public Student(int id, String name) {this.id id;this.name name;}*/public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {…

数字人入门文章速览

语音驱动三维人脸方法 OPPO 数字人语音驱动面部技术实践 【万字长文】虚拟人漫谈 Blendshape学习笔记 人脸重建速览&#xff0c;从3DMM到表情驱动动画 功能强大的python包&#xff08;四&#xff09;&#xff1a;OpenCV 从Blendshapes到Animoji 3D人脸重建算法汇总 一、3D人脸重…

windows 10 安装k8s环境 Kubernetes

主要命令有 1. iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex 2. choco install minikube 3. minikube start 4. minikube dashboard 使用管理员运行 PowerShell 执行下面这条命令 iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex choc…

开源赋能 普惠未来|铜锁/Tongsuo诚邀您参与2023开放原子全球开源峰会

铜锁/Tongsuo是一个提供现代密码学算法和安全通信协议的开源基础密码库&#xff0c;为存储、网络、密钥管理、隐私计算、区块链等诸多业务场景提供底层的密码学基础能力&#xff0c;实现数据在传输、使用、存储等过程中的私密性、完整性和可认证性&#xff0c;为数据生命周期中…

Linux:web基础与HTTP协议

Linux&#xff1a;web基础与HTTP协议 一、域名概述1.1 域名的概念1.2 域名空间结构1.3 域名注册 二、网页的概念2.1 网页2.2 网站2.3 主页2.4 网页2 三、HTML概述3.1 HTML概述3.2 HTML文档结构3.3 HTML 基本标签 四、web概述4.1 web概述4.2 Web1.0 vs Web2.04.3 静态网页4.3.1 …

【挑战自己】软件测试的7个级别,做到3级已经超越80%测试人

有人说&#xff1a;软件测试就是最low的点点点工作。 有人说&#xff1a;测试工作职位薪水到一定程度只能原地踏步无法提升 也有人说&#xff1a;测试行业相对于开发来说技术性很低&#xff0c;容易被取代。 这其实是对测试行业最大的误解。测试可深可浅&#xff0c;可窄可广…

QDir拼接路径解决各种斜杠问题

一般在项目中经常需要组合路径,与其他程序进行相互调用传递消息通信。 经常可能因为多加斜杠、少加斜杠等问题导致很多问题。 为了解决这些问题,我们可以使用QDir来完成路径的拼接,不直接拼接字符串。 QDir的静态方法QDir::cleanPath() 是为了规范化路径名的,在使用QDir组…

Unity第三方分享(微信)插件ShareSDK使用简记

Unity第三方分享&#xff08;微信&#xff09;插件ShareSDK使用简记 微信分享遇到的问题记录 链接官方链接参考链接 微信分享 官方文档&#xff1a;MobTech集成文档-MobTech 下载地址&#xff1a;GitHub - MobClub/New-Unity-For-ShareSDK: New sample of ShareSDK for Unity,…

ChatGPT:你真的了解网络安全吗?浅谈攻击防御进行时之网络安全新防御

ChatGPT&#xff1a;你真的了解网络安全吗&#xff1f;浅谈网络安全攻击防御进行时 网络安全新防御1. 针对人工智能2. 针对5G和物联网3. 针对云安全4.针对社交工程5. 针对加密技术6. 针对多层次的安全控制 总结 ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-traine…

去除氟离子的最好办法,矿井水现场氟离子超标RO浓水除氟

一、产品介绍 氟化物选择吸附树脂 Tulsimer CH-87 是一款去除水溶液中氟离子的专用的凝胶型选择性离子交换树脂。它是具有氟化物选择性官能团的交联聚苯乙烯共聚物架构的树脂。 去除氟离子的能力可以达到 1ppm 以下的水平。中性至碱性的PH范围内有较好的工作效率&#xff0c;并…

保姆级别!!!--全网绝对教你会!!教你如何使用MQTTFX连接阿里云平台中的设备----下期告诉你如何创建!

本期需要下载的软件 MQttfx安装包&#xff0c;本人打包的-嵌入式文档类资源-CSDN文库 目录 第一步&#xff1a;建造阿里云设备 这个可以先忽略建造步骤&#xff0c;下期将提供步骤。 第二步&#xff1a;下载mqttfx软件 第三步&#xff1a;填写密钥信息进行连接 查看三元…

如何显示物品词缀?

UE5 插件开发指南 前言0 什么是物品词缀?1 如何动态显示物品词缀?前言 读到这里读者应该已经知道如何解析这个题目了,拆分为如下问题: (1)什么是物品词缀? (2)如何动态显示? 0 什么是物品词缀? 首先要知道什么是物品词缀,如下图所示,物品词缀就是用来描述物品属性的…

助力服务智能医疗检测,基于yolov5开发构建结直肠息肉检测系统,实践训练n/s/m不同量级模型,对比性能结果

将人工智能技术应用于众多的生活真实场景中是一件很有前景的事情&#xff0c;在我前面的博文中已经有不少的相关的开发实践&#xff0c;应用于医学领域也是一个非常重要的细分分支领域&#xff0c;在前面的博文中也有一些实践&#xff0c;感兴趣的话可以自行移步阅读。 《服务…

无需公网IP,快速远程登录家里的威联通NAS

文章目录 前言1. 威联通安装cpolar内网穿透2. 内网穿透2.1 创建隧道2.2 测试公网远程访问 3. 配置固定二级子域名3.1 保留二级子域名3.2 配置二级子域名 4. 使用固定二级子域名远程访问 转载自cpolar内网穿透的文章&#xff1a;无需公网IP&#xff0c;在外远程访问威联通QNAP|N…