Linux学习笔记(应用篇三)

news2025/4/21 14:01:31

基于I.MX6ULL-MINI开发板

  • LED学习
  • GPIO应用编程
  • 输入设备

开发板中所有的设备(对象)都会在/sys/devices 体现出来,是 sysfs 文件系统中最重要的目录结构

/sys下的子目录说明
/sys/devices这是系统中所有设备存放的目录,也就是系统中的所有设备在 sysfs 中的呈现、表达,也是 sysfs 管理设备的最重要的目录结构。
/sys/block块设备的存放目录,这是一个过时的接口,按照 sysfs 的设计理念,系统所有的设备都存放在/sys/devices 目录下,所以/sys/block 目录下的文件通常是链接到/sys/devices 目录下的文件。
/sys/bus这是系统中的所有设备按照总线类型分类放置的目录结构,/sys/devices 目录下每一种设备都是挂在某种总线下的,譬如 i2c 设备挂在 I2C 总线下。同样,/sys/bus 目录下的文件通常也是链接到了/sys/devices 目录。
/sys/class这是系统中的所有设备按照其功能分类放置的目录结构,同样该目录下的文件也是链接到了/sys/devices 目录。按照设备的功能划分组织在/sys/class 目录下,譬如/sys/class/leds目录中存放了所有的 LED 设备,/sys/class/input 目录中存放了所有的输入类设备。
/sys/dev这是按照设备号的方式放置的目录结构,同样该目录下的文件也是链接到了/sys/devices 目录。该目录下有很多以主设备号:次设备号(major:minor)命名的文件,这些文件都是链接文件,链接到/sys/devices 目录下对应的设备。
/sys/firmware描述了内核中的固件。
/sys/fs用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点。
/sys/kernel这里是内核中所有可调参数的位置。
/sys/module这里有系统中所有模块的信息。
/sys/power这里是系统中电源选项,有一些属性可以用于控制整个系统的电源状态。

应用层想要对底层硬件进行操控,通常可以通过两种方式

  1. /dev/目录下的设备文件(设备节点);
  2. /sys/目录下设备的属性文件。

LED学习

led设备路径

/sys/class/leds/sys-led

在这里插入图片描述

brightness:LED亮度,可读可写,0亮,非0灭
max_brightness:LED最大亮度,只读
trigger:触发模式,可读可写,模式如下:
none(无触发)、mmc0(当对 mmc0 设备发起读写操作的时候 LED 会闪烁)、timer(LED 会有规律的一亮一灭,被定时器控制住)、heartbeat(心跳呼吸模式,LED 模仿人的心跳呼吸那样亮灭变化)。

代码

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

#define LED_TRIGGER "/sys/class/leds/sys-led/trigger"
#define LED_BRIGHTNESS "/sys/class/leds/sys-led/brightness"
/*
在 C 语言中,反斜杠 \ 的作用是 行连接符(line continuation character)。
它允许你在多个行中编写一条连续的语句,而不需要写成一行。
具体来说,反斜杠告诉编译器:下一行是当前行的延续,继续合并在一起,而不加上额外的换行符。
*/
#define USAGE()     fprintf(stderr,"usage:\n" \
               "    %s<on|off>\n"   \
               "    %s<trigger><type>\n",argv[0],argv[0])  //argv[0]存放程序的名称或路径


int main(int argc, char *argv[])//argc参数个数,argv[]参数
{
    int fd1, fd2;

    //校验传参
    if(2>argc){
        USAGE();
        return 1;
    }

    // 打开sysfs中的trigger和brightness文件
    fd1 = open(LED_TRIGGER, O_RDWR);
    if (0 > fd1) {
        perror("open LED_TRIGGER error");
        return 1;
    }

    fd2 = open(LED_BRIGHTNESS, O_RDWR);
    if (0 > fd2) {
        perror("open LED_BRIGHTNESS error");
        close(fd1);
        return 1;
    }

    //根据传参控制LED
    if(!strcmp(argv[1],"on")){//判断传入的第一个参数是否是on
        write(fd1,"none",4);//往trigger写入none,共4个字节,trigger设置为无触发
        write(fd2,"1",1);//点亮LED
    }
    else if(!strcmp(argv[1],"off")){//判断传入的第一个参数是否是off
        write(fd1,"none",4);
        write(fd2,"0",1);//熄灭LED
    }
    else if(!strcmp(argv[1],"trigger")){//如果传入的第一个参数是trigger但是参数个数不是三个,提示用法
        if(3!=argc){
            USAGE();
            return 1;
        }
        if(0 > write(fd1,argv[2],strlen(argv[2]))){//判断写入是否正确,将第三个参数即触发模式传给fd1 (none、mmc0、timer、heartbeat)
            perror("write error");
            return 1;
        }
    }
    else
        USAGE();

    return 0;
}

在这里插入图片描述

GPIO应用编程

GPIO目录

/sys/class/gpio

一共包含了 5 个 GPIO控制器,分别为 GPIO1、GPIO2、GPIO3、GPIO4、GPIO5,在这里分别对应 gpiochip0、gpiochip32、gpiochip64、gpiochip96、gpiochip128 这 5 个文件夹,每一个 gpiochipX 文件夹用来管理一组 GPIO

在这里插入图片描述
每一个gpiochipX文件夹里面有以下内容

在这里插入图片描述

base:与 gpiochipX 中的 X 相同,表示该控制器所管理的这组 GPIO 引脚中最小的编号,如gpiochip32的base就是32
label:该组 GPIO 对应的标签,也就是名字
ngpio:该控制器所管理的 GPIO 引脚的数量,引脚编号范围是:base ~ base+ngpio-1

GPIO5_IO10在sysfs中对应的编号:128+10=138

export

export:用于将指定编号的 GPIO 引脚导出。在使用 GPIO 引脚之前,需要将其导出,导出成功之后才能使用它。注意 export 文件是只写文件,不能读取,将一个指定的编号写入到 export 文件中即可将对应的 GPIO 引脚导出

如下图,这个文件夹就是导出来的 GPIO 引脚对应的文件夹,用于管理、控制该 GPIO 引脚

在这里插入图片描述

unexport

unexport:将导出的 GPIO 引脚删除。当使用完 GPIO 引脚之后,我们需要将导出的引脚删除,同样该文件也是只写文件、不可读

在这里插入图片描述

gpioX文件夹里的文件
在这里插入图片描述

direction:配置 GPIO 引脚为输入或输出模式。该文件可读、可写,读表示查看 GPIO 当前是输入还是输出模式,写表示将 GPIO 配置为输入或输出模式;读取或写入操作可取的值为"out"(输出模式)和"in"(输入模式)
在这里插入图片描述
value:在 GPIO 配置为输出模式下,向 value 文件写入"0"控制 GPIO 引脚输出低电平,写入"1"则控制 GPIO 引脚输出高电平。在输入模式下,读取 value 文件获取 GPIO 引脚当前的输入电平状态。
active_low:这个属性文件用于控制极性,可读可写,默认情况下为 0,如果设置为1,则逻辑1为低电平,逻辑0为高电平
edge:控制中断的触发模式,该文件可读可写。在配置 GPIO 引脚的中断触发模式之前,需将其设置为输入模式
非中断引脚echo "none" > edge
上升沿触发echo "rising" > edge
下降沿触发echo "falling" > edge
边沿触发 echo "both" > edge

输出代码

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


static char gpio_path[100];//存放gpio设备文件路径

//修改GPIO的状态
static int gpio_config(const char *attr, const char *value)//attr:修改的选项(direction、active_value、value)value:传入的值
{
    char file_path[100];
    int fd;
    int len;

    sprintf(file_path, "%s/%s", gpio_path, attr);//将/sys/class/gpio/gpiox/(direction或active_value或value)传给file_path
    if(0 > (fd = open(file_path,O_WRONLY)))
    {
        perror("open error");
        return fd;
    }
    len = strlen(value);
    if(len!= write(fd,value,len))//向file_path写入value,即修改gpio的状态
    {
        perror("write error");
        close(fd);
        return 1;
    }
    close(fd);
    return 0;
}

int main(int argc, char *argv[])
{
    //判断传参是否正确
    if(3!=argc)
    {
        fprintf(stderr,"usage: %s<gpio><value>\n", argv[0]);
        return 1;
    }


    //判断gpiox文件是否导出
    sprintf(gpio_path,"/sys/class/gpio/gpio%s",argv[1]);

    if(access(gpio_path,F_OK))//判断路径是否存在,不存在则创建,F_OK参数用于判断文件是否存在
    {
        int fd;
        int len;

        if(0 > (fd = open("/sys/class/gpio/export",O_WRONLY)))//打开export文件并将路径存放在fd,准备写入
        {
            perror("open error");
            return 1;
        }

        len = strlen(argv[1]);

        if(len != write(fd,argv[1],len))//向export写入gpio号,即导出gpio文件
        {
            perror("write error");
            close(fd);
            return 1;
        }

        close(fd);//关闭文件
    }
    //修改gpio的状态
    if(gpio_config("direction","out"))//配置为输出模式
    {
        return 1;
    }

    if(gpio_config("active_low","0"))//默认极性
    {
        return 1;
    }

    if(gpio_config("value",argv[2]))//控制GPIO高低电平
    {
        return 1;
    }

    //退出程序
    return 0;

}

中断输入代码

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


static char gpio_path[100];//存放gpio设备文件路径

//修改GPIO的状态
static int gpio_config(const char *attr, const char *value)//attr:修改的选项(direction、active_low、value)value:传入的值
{
    char file_path[100];
    int fd;
    int len;

    sprintf(file_path, "%s/%s", gpio_path, attr);//将/sys/class/gpio/gpiox/(direction或active_low或value)传给file_path
    if(0 > (fd = open(file_path,O_WRONLY)))
    {
        perror("open error");
        return fd;
    }

    len = strlen(value);
    if(len!= write(fd,value,len))//向file_path写入value,即修改gpio的状态
    {
        perror("write error");
        close(fd);
        return 1;
    }
    close(fd);
    return 0;
}

int main(int argc, char *argv[])
{
    struct pollfd pfd;//用于poll调用来监视文件描述符的事件,记得包含头文件poll.h
    char file_path[100];//存储/sys/class/gpio/gpiox/value的路径
    int ret;            //poll函数的返回值
    char value;         //从GPIO引脚读取的值

    //判断传参是否正确
    if(2!=argc)
    {
        fprintf(stderr,"usage: %s<gpio> <value>\n", argv[0]);
        return 1;
    }


    //判断gpiox文件是否导出
    sprintf(gpio_path,"/sys/class/gpio/gpio%s",argv[1]);

    if(access(gpio_path,F_OK))//判断路径是否存在,不存在则创建,F_OK参数用于判断文件是否存在
    {
        int fd;
        int len;

        if(0 > (fd = open("/sys/class/gpio/export",O_WRONLY)))//打开export文件并将路径存放在fd,准备写入
        {
            perror("open error");
            return 1;
        }

        len = strlen(argv[1]);

        if(len != write(fd,argv[1],len))//向export写入gpio号,即导出gpio文件
        {
            perror("write error");
            return 1;
        }

        close(fd);//关闭文件
    }
    //修改gpio的状态
    if(gpio_config("direction","in"))//配置为输入模式
    {
        return 1;
    }

    if(gpio_config("active_low","0"))//默认极性
    {
        return 1;
    }

    if(gpio_config("edge","both"))//设置中断触发方式,both是边沿触发
    {
        return 1;
    }

    //打开value属性文件
    sprintf(file_path,"%s/%s",gpio_path,"value");

    if(0 > (pfd.fd = open(file_path,O_RDONLY)))//用pfd.fd存储vlaue的文件路径
    {
        perror("open error");
        return 1;
    }

    /*
    调用poll
    struct pollfd{
        int fd;         //文件描述符
        short events;   //等待的事件
        short revents;  //返回的事件
    }
    */
    pfd.events = POLLPRI;//只关心高优先级数据可读(中断)

    read(pfd.fd,&value, 1);//在进入循环之前,先读取一次GPIO状态,以清除先前的状态
    
    for( ; ; )
    {
        ret = poll(&pfd,1,-1);//调用poll,阻塞直到事件发生
        if(0 > ret)
        {
            perror("poll error");
            return 1;
        }
        else if(0 == ret)
        {
            fprintf(stderr,"poll timeout.\n");
            continue;
        }

        //校验高优先级数据是否可读
        if(pfd.revents & POLLPRI)
        {
            if(0 > lseek(pfd.fd,0,SEEK_SET))//使用lseek重置文件指针,确保每次读取最新的GPIO状态
            {
                perror("lseek error");
                return 1;
            }

            if(0 > read(pfd.fd,&value,1))
            {
                perror("read error");
                return 1;
            }
            printf("gpio中断触发<value=%c\n",value);
        }
    }
    //退出程序
    return 0;
}

poll函数复习

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
/*
fds:指向一个 struct pollfd 类型的数组,储存关心的文件描述符
nfds:fds数组中元素的个数
timeout:决定poll函数的阻塞行为
	-1:一直阻塞,直到有信号来
	 0:不阻塞
	>0:阻塞时间上限
*/

//struct pollfd 结构体
struct pollfd {
 int fd;        /* 文件描述符 */
 short events;  /* 等待的事件 */
 short revents; /* 返回的事件 */
};

在这里插入图片描述

输入设备

读取输入设备时,应用程序打开输入设备对应的设备文件,向其发起读操作,每一次 read 操作获取的都是一个 structinput_event 结构体类型数据,该结构体定义在<linux/input.h>头文件中

struct input_event {
 struct timeval time;
 __u16 type;		//描述发生了哪一种类型的时间
 __u16 code;		//该类事件具体是哪个事件,如键盘上不同的按键1、2、3...
 __s32 value;		
/*
内核每次上报事件都会向应用层发送一个数据 value,对 value 值的解释随着 code 的变化而变化。譬如对于按键事件(type=1)来说,如果 code=2(键盘上的数字键 1,也就是 KEY_1),那么如果 value 等于 1,则表示 KEY_1 键按下;value 等于 0 表示 KEY_1 键松开,如果 value 等于 2则表示 KEY_1 键长按。再比如,在绝对位移事件中(type=3),如果 code=0(触摸点 X 坐标 ABS_X),那么 value 值就等于触摸点的 X 轴坐标值;同理,如果 code=1(触摸点 Y 坐标 ABS_Y),此时value 值便等于触摸点的 Y 轴坐标值;所以对 value 值的解释需要根据不同的 code 值而定
*/
};

type
在这里插入图片描述

code
Key

在这里插入图片描述

相对位移

在这里插入图片描述

绝对位移

在这里插入图片描述

通过数据同步使得应用程序得知本轮已经读取到完整的数据同步类事件有如下,所有的输入设备都需要上报同步事件,上报的同步事件通常是SYN_REPORT,而 value 值通常为 0
在这里插入图片描述

查看输入设备是哪个设备节点

cat /proc/bus/input/devices

在这里插入图片描述

输入设备代码

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

int main(int argc, char *argv[])
{
    struct input_event in_ev = {0};
    int fd = -1;
    int value = -1;

    //校验传参
    if(2 != argc)
    {
        fprintf(stderr, "Usage: %s <input_dev>\n", argv[0]);
        return 1;
    }

    // 打开输入事件文件
    if(0 > (fd = open(argv[1], O_RDONLY)))
    {
        perror("open error");
        return 1;
    }

    for( ; ; )
    {
        // 读取输入事件
        if(sizeof(struct input_event) != (read(fd, &in_ev, sizeof(struct input_event))))
        {
            perror("read error");
            break;
        }

        // 按键输入的类型
        if(EV_KEY == in_ev.type)
        {
           switch (in_ev.value)
           {
           case 0:
                printf("code<%d>:松开\n", in_ev.code);
                break;
            case 1:
                printf("code<%d>:按下\n", in_ev.code);
                break;  
            case 2:
                printf("code<%d>:长按\n", in_ev.code);
                break;                       
           }
        }
    }
}

开发板KEY0按键测试

在这里插入图片描述
键盘测试

接入键盘,我使用的是2.4G连接方式
在这里插入图片描述

查看设备时发现有两个

在这里插入图片描述

event3是键盘上的按键

在这里插入图片描述

event4是键盘上的旋钮
由此可知这两个东西不是同一个设备

在这里插入图片描述

鼠标测试

在这里插入图片描述
在这里插入图片描述

只能读取鼠标的左、右和滚轮的按下和松开

在这里插入图片描述

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

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

相关文章

【redis】事务详解,相关命令multi、exec、discard 与 watch 的原理

文章目录 什么是事务原子性一致性持久性隔离性 优势与 MySQL 对比用处 事务相关命令开启事务——MULTI执行事务——EXEC放弃当前事务——DISCARD监控某个 key——WATCH作用场景使用方法实现原理 事务总结 什么是事务 MySQL 事务&#xff1a; 原子性&#xff1a;把多个操作&am…

数据库基础知识点(系列七)

视图和索引相关的语句 1&#xff0e;引入视图的主要目的是什么? 答&#xff1a;数据库的基本表是按照数据库设计人员的观点设计的&#xff0c;并不一定符合用户的需求。SQL Server 2008可以根据用户需求重新定义表的数据结构&#xff0c;这种数据结构就是视图。视图是关系数据…

3.3 Taylor公式

1.定义 1.1 taylor公式 1.2 麦克劳林公式 1.3 推论 1.4 拉格朗日余项和皮亚诺型余项 2. 例题 3.几种特殊函数的麦克劳林展开

2000-2019年各省地方财政行政事业性收费收入数据

2000-2019年各省地方财政行政事业性收费收入数据 1、时间&#xff1a;2000-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政行政事业性收费收入 4、范围&#xff1a;31省 5、指标说明&#xff1a;地方财政行政事业…

Ftrans飞驰云联受邀参加“2025汽车零部件CIO年会“并荣获智象奖

2025年3月6日&#xff0c;由栖观汽车、栖观资讯和飞羽商务主办的“2025第二届中国汽车&零部件CIO年会暨智象奖颁奖盛典”于上海盛大召开&#xff0c;Ftrans飞驰云联作为国内领先的企业文件传输与数据交换解决方案提供商&#xff0c;受邀出席了年会&#xff0c;并凭借卓越的…

oracle查询归档日志使用量

1.统计最近30天的数据 SELECT TRUNC(first_time, DD) "日期", SUM(blocks * block_size) / 1024 / 1024 / 1024 "大小(GB)" FROM v$archived_log WHERE first_time > SYSDATE - 30 -- 统计最近30天的数据 GROUP BY TRUNC(first_time, DD) ORDER BY 1 D…

2025-03-26 学习记录--C/C++-PTA 6-3 求链式表的表长

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 6-3 求链式表的表长 本题要求实现一个函数&#xff0c;求链式表的表长。 函数接口定义&#xff1a; &…

PHP框架 ThinkPHP 漏洞探测分析

目录 1. PHP历史利用最多的漏洞有哪些&#xff1f; 2. 如何在信息收集的过程中收到框架信息&#xff1f;有什么根据&#xff1f; 3. ThinkPHP框架漏洞扫描有哪些工具&#xff1f;红队攻击有哪些方式&#xff1f; 漏洞扫描工具 红队攻击方式 4. TPscan工具的主要作用及实际…

SylixOS 中 select 原理及使用分析

1、select接口简介 1.1 select接口使用用例 select 是操作系统多路 I/O 复用技术实现的方式之一。 select 函数允许程序监视多个文件描述符&#xff0c;等待所监视的一个或者多个文件描述符变为“准备好”的状态。所谓的”准备好“状态是指&#xff1a;文件描述符不再是阻塞状…

软考笔记——软件工程基础知识

第五章节——软件工程基础知识 软件工程基础知识 第五章节——软件工程基础知识一、软件工程概述1. 计算机软件2. 软件工程基本原理3. 软件生命周期4. 软件过程 二、软件过程模型1. 瀑布模型2. 增量模型3. 演化模型&#xff08;原型模型、螺旋模型)4. 喷泉模型5. 基于构建的开发…

FastGPT原理分析-数据集创建第二步:处理任务的执行

概述 文章《FastGPT原理分析-数据集创建第一步》已经分析了数据集创建的第一步&#xff1a;文件上传和预处理的实现逻辑。本文介绍文件上传后&#xff0c;数据处理任务的具体实现逻辑。 数据集创建总体实现步骤 从上文可知数据集创建总体上来说分为两大步骤&#xff1a; &a…

STM32学习笔记之存储器映射(原理篇)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

如何通过数据可视化提升管理效率

通过数据可视化提升管理效率的核心方法包括清晰展示关键指标、及时发现和解决问题、支持决策优化。其中&#xff0c;清晰展示关键指标尤为重要。通过数据可视化工具直观地呈现关键绩效指标&#xff08;KPI&#xff09;&#xff0c;管理者能快速、准确地理解业务现状&#xff0c…

数据结构:利用递推式计算next表

next 表是 KMP 算法的核心内容&#xff0c;下面介绍一种计算 next 表的方法&#xff1a;利用递推式计算 如图 6.3.1 所示&#xff0c;在某一趟匹配中&#xff0c;当对比到最后一个字符的时候&#xff0c;发现匹配失败&#xff08;s[i] ≠ t[j]&#xff09;。根据 BF 算法&…

每日算法-250326

83. 删除排序链表中的重复元素 题目描述 思路 使用快慢指针遍历排序链表。slow 指针指向当前不重复序列的最后一个节点&#xff0c;fast 指针用于向前遍历探索。当 fast 找到一个与 slow 指向的节点值不同的新节点时&#xff0c;就将 slow 的 next 指向 fast&#xff0c;然后 …

trino查询mysql报Unknown or incorrect time zone: ‘Asia/Shanghai‘

问题 trino查询mysql时报Error listing schemas for catalog mysql: java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.&#xff0c;trino的日志中看到Unknown or incorrect time zone…

java学习笔记7——面向对象

关键字&#xff1a;static 类变量 静态变量的内存解析&#xff1a; 相关代码&#xff1a; public class ChineseTest {public static void main(String[] args) {System.out.println(Chinese.nation); //null 没赋值前System.out.println(Chinese.nation); //中国 静态变量赋值…

C++三大特性之继承

1.继承的概念及定义 回忆封装 C Stack类设计和C设计Stack对比。封装更好&#xff1a;访问限定符类的数据和方法放在一起 -> 避免底层接口的暴露&#xff0c;数据更加的安全&#xff0c;程序的耦合性更高迭代器的设计&#xff0c;封装了容器底层结构&#xff0c;在不暴露底层…

解决Vmware 运行虚拟机Ubuntu22.04卡顿、终端打字延迟问题

亲测可用 打开虚拟机设置&#xff0c;关闭加速3D图形 &#xff08;应该是显卡驱动的问题&#xff0c;不知道那个版本的驱动不会出现这个问题&#xff0c;所以干脆把加速关了&#xff09;