正点原子linux应用编程——提高篇1

在之前的入门篇学习中,都是直接在Ubuntu中进行验证的,对于嵌入式Linux系统来说,也是可以直接移植的,只需要使用嵌入式硬件平台对应的交叉编译工具编译应用程序即可运行

在嵌入式Linux系统中,编写的应用程序通常需要与硬件设备进行交互、操控硬件,譬如点亮开发板上的一颗LED灯、获取按键输入数据、在LCD屏上显示摄像头采集的图像、应用程序向串口发送数据或采集串口数据、网络编程等,那么本篇开始学习如何编写应用程序控制开发板上的各种硬件外设;Linux系统下,一切皆文件,也包括各种硬件设备,所以在Linux系统下,各种硬件设备是以文件的形式呈现给用户层,应用程序通过对文件的I/O操作来控制硬件设备

点亮LED

正点原子MP157/Mini开发板(包括核心板和底板)上一共有2颗供用户使用的LED小灯;LED通常是由GPIO所控制的,本章来学习如何编写应用程序控制LED灯的亮灭

应用层操控硬件的两种方法

设备文件便是各种硬件设备向应用层提供的一个接口,应用层通过对设备文件的I/O操作来操控硬件设备,设备文件通常在/dev/目录下,也把/dev 目录下的文件称为设备节点。当然还可以通过sysfs文件系统对硬件设备进行操控

sysfs文件系统

sysfs是一个基于内存的文件系统,同devfs、proc文件系统一样,称为虚拟文件系统;它的作用是将内核信息以文件的方式提供给应用层使用。sysfs文件系统的主要功能便是对系统设备进行管理,它可以产生一个包含所有系统硬件层次的视图

sysfs文件系统把连接在系统上的设备和总线组织成为一个分级的文件、展示设备驱动模型中各组件的层次关系。sysfs提供了一种机制,可以显式的描述内核对象、对象属性及对象间关系,用来导出内核对象(kernel object,譬如一个硬件设备)的数据、属性到用户空间,以文件目录结构的形式为用户空间提供对这些数据、属性的访问支持,如下图:

内核对象、对象属性及对象间关系

sysfs与/sys

sysfs文件系统挂载在/sys目录下。包括 block、bus、class、dev、devices、firmware、fs、kernel、module、power等,每个目录下又有许多文件或子目录。

devices就是设备存放的目录;bus是按照总线类型分类的目录;class是按照功能分类的,例如会有leds和input这些子目录;dev是按照设备号放置的目录。

总结

一般简单的设备会使用sysfs操控,例如LED、GPIO等;较复杂设备会使用设备节点操作/dev目录,例如LCD、触摸屏、摄像头等。

标准接口与非标准接口

Linux针对各种常见的设备进行分类,譬如LED类设备、输入类设备、FrameBuffer类设备、video类设备、PWM设备等等,并为每一种类型的设备设计了一套成熟的、标准的、典型的驱动实现的框架,这个就叫做设备驱动框架

如果不用内核的驱动框架而是自己写,那就是非标准接口。除此之外,还有很多被Linux系统归为杂散设备(misc device)。

LED硬件控制方式

MP157上有两个LED:

MP157的LED灯

对于MP157/Mini开发板出厂系统来说,这两颗LED设备使用的是Linux内核标准LED驱动框架注册而成,在/dev目录下并没有其对应的设备节点,其实现使用sysfs方式控制

可以进入/sys/class/leds目录进行查看,可以看到例如sys-led都是链接文件,链接到/sys/devices/platform/leds/leds/sys-led;而这个设备文件中,有三个文件,brightness,max_brightness以及trigger这三个关心的文件,分别控制亮度,显示最大亮度以及触发模式

如果通过cat命令进入trigger触发模式文件,方括号括起来的就是当前的触发方式。

直接控制,可以通过echo命令,示例如下:

echo timer > trigger //将 LED 触发模式设置为 timer
echo none > trigger //将 LED 触发模式设置为 none
echo 1 > brightness //点亮 LED echo 0 > brightness//熄灭 LED

编写LED应用程序

可以先通过宏定义完成文件路径的配置。

宏定义还可以用来直接定义传参错误时的printf信息,非常方便。

通过open打开LED灯的trigger和brightness文件,之后通过strcmp比较传参,借由write写入命令(均需要把trigger设置为none)。

开发板测试

把编译好的可执行文件,复制到开发板根文件系统中,然后可通过如下命令测试:

./testApp on # 点亮 LED
./testApp off # 熄灭 LED
./testApp trigger heartbeat # 将 LED 触发模式设置为 heartbeat

GPIO应用编程

应用层操控GPIO

进入到/sys/class/gpio目录下,包含了export、unexport以及许多gpiochipX的文件。

gpiochipX,就是当前SoC包含的GPIO控制器,而STM32MP157共有12个控制器,为GPIOA-GPIOK以及GPIOZ,分别对应gpiochip0、gpiochip16、……以此类推。

每一个gpiochipX中,还有base、label、ngpio以及其他一些不太关心的文件,这三个是属性文件,均为只读文件。base也就是最小的编号,label就是标签,ngpio就是引脚的个数

export,就是指定编号的GPIO引脚引出。导出成功后才能使用该GPIO。这是个只写文件,写入该文件即导出对应的GPIO引脚。导出后就会在/sys/class/gpio生成对应的文件夹

unexport,就是与export对立,取掉导出的GPIO。同样也是只写文件,使用完GPIO后需要调用来取消导出。

成功导出就会生成gpioX,其中主要关心四个属性文件active_low、direction、edge以及value。

  • direction:配置引脚的输入或输出模式。输出out,输入in。
  • value:输出模式下,配置0与1来对应低电平和高电平;输入模式下,读取value获取输入电平。
  • active_low:控制极性。
  • edge:控制中断触发模式。配置前需要先设为输入模式,通过none、rising、falling、both控制触发沿。

GPIO应用编程输出

gpio_config函数来配置GPIO,传入attr和val对应文件以及传入的值,通过sprintf来查看传入参数,通过open打开文件后,write写入命令。

main函数中,先通过access并掺入F_OK判断目录是否存在,如果为1,说明不存在,需要导出。此时open打开export文件,write写入命令来导出gpio。

之后调用gpio_config配置direction、active_low以及value。

GPIO应用编程输入

gpio_config是一样的,这里不再赘述。

main函数也类似,通过access判断是否存在,不存在则需要导出,open打开export后write写入来导出gpio。

然后gpio_config配置,direction就需要配为in,设置active_low然后设置edge为none非中断。

最后通过open打开文件后,read读取value的电平状态。

GPIO应用编程中断

其余的操作与输入是类似的,这里只看中断特有的代码。

gpio_config中,需要配置edge的中断触发方式,这里配置为both。

读取文件,需要用struct pollfd结构体,open打开存入pfd.fd中,然后设置pfd.events为POLLPRI(只关心高优先级数据可读,中断就是高优先级数据),之后先read一次后进入死循环中:调用poll,之后先判断pfd.revents&POLLPRI,为真后进入读取,lseek先把读位置移到头部后再read读取值

开发板测试

可以选择PE13测试,也就是编号为77的引脚:

MP157的PE13

GPIO输出

执行如下命令可测试:

./testApp 77 1 #控制 PE13 输出高电平
./testApp 77 0 #控制 PE13 输出低电平

GPIO输入

可通过如下命令读取:

./testApp 77

GPIO中断

本实验选用PE4,因为PE13是悬空的,电平状态不确定。PE4的编号就是68,执行如下命令测试:

./testApp 68 # 监测 PE4 引脚中断触发情况

输入设备应用程序

对于输入设备的应用编程其主要是获取输入设备上报的数据、输入设备当前状态等,譬如获取触摸屏当前触摸点的X、Y轴位置信息以及触摸屏当前处于按下还是松开状态

输入类设备编程

input子系统

基于input子系统注册成功的输入设备,都会在/dev/input 目录下生成对应的设备节点,设备节点名称通常为eventX(X表示一个数字编号0、1、2、3等),如/dev/input/event0、/dev/input/event1、
/dev/input/event2等,通过读取这些设备节点可以获取输入设备上报的数据。

读取数据流程

需要先打开设备文件;之后发起读操作(如read),如果无数据会进入休眠(阻塞I/O);有数据则会被唤醒,督导数据返回;应用程序处理读取数据。

应用程序解析数据

read操作获取的就是struct input_event结构体数据。主要关心齐总的type、code以及value成员变量。

type就是用来描述事件的类型;code是具体的事件,每一个事件类型均有不同的对应事件;value就是code对应事件所读取到的值。

读取完成,就是通过同步事件完成的,就是EV_SYN,内核完成了所有数据上报就会上报EV_SYN同步事件。

上报的同步事件通常为SYN_REPORT,value通常为0。

读取struct input_event数据

定义好对应的结构体后,open打开文件,并在死循环中调用read读取并printf打印出来。

开发板验证

MP157有两个按键,就是典型的输入设备:

MP157用户按键

在/dev/input目录下存在按键对应的设备节点;也可以查看/proc/bus/input/devices文件查询。

可通过如下命令执行:

./testApp /dev/input/event1

按键应用编程

对于按键,按下value=1,松开则value=0,长按则value=2。

main函数中,通过open打开文件,之后进入死循环:通过read读取传入struct input_event的结构体指针in_ev中,之后通过in_ev.value成员变量判断,按键的事件为EV_KEY,然后switch判断value的大小并执行printf打印。

开发板上验证可通过如下命令:

./testApp /dev/input/event1 # 测试开发板上的 KEY0 和 KEY1

触摸屏应用编程

解析触摸屏设备上报数据

触摸屏分为多点触摸设备和单点触摸设备。单点触摸一次完整数据只有一个触摸点数据:ABS_XXX时间承载上报触摸点信息;多点触摸则可能一次包含多个触摸点信息,大多是以ABS_MT_XXX事件承载上报数据。

MT的协议在触摸屏驱动的时候已经学过了,这里就不再多记录了。大多使用Type B协议,会先生成ABS_MT_SLOT事件并生成ABS_MT_TRACKING_ID之后上报坐标值。

可通过“cat /proc/bus/input/devices”查询LCD的设备名称来找到对应事件,然后同样执行之前的命令,把事件换成触摸屏就可以了。

获取触摸屏信息

可通过ioctl()函数一区触摸屏设备信息。原型如下:

#include <sys/ioctl.h>int ioctl(int fd, unsigned long request, ...);

第一个参数fd对应文件描述符;第二个参数request与具体要操作的对象有关,没有统一值,表示向文件描述符请求相应的操作,也就是请求指令;此函数是一个可变参函数,第三个参数需要根据request参数来决定,配合request来使用。

可在input.h中查询对应事件的宏定义。

可通过下面的宏,获取触摸屏的slot:

#define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo)

例如获取触摸屏的最大触摸点数:定义struct input_absinfo结构体info,然后open打开设备文件后,通过ioctl,第二个参数为EVIOCGABS(ABS_MT_SLOT),读取之后info.maximum-info.minimum+1就是多大点数。

单点触摸应用程序

open打开设备文件,进入死循环:read读取输入事件,通过in_ev.type判断事件类型,如果是EV_ABS就是绝对位移事件,然后可通过in_ev.code判断是x还是y坐标;如果是EV_SYN同步事件,就可以通过in_ev.code判断是否为SYN_REPORT判断是什么事件,printf打印对应信息。

多点触摸应用程序

这里的区别就是定义了几个结构体来存储多个点的数据。

编写ts_read来读取数据,传入fd、max_slots以及自定义的ts_mt结构体指针mt(该结构体存储了每一个触摸点的信息)。其中定义了input_event的in_ev,static的slot以及tp_xy结构体数组xy(存储x和y坐标),之后通过memset先清空mt,然后将mt的id成员变量全部初始化为-2(-1表示触摸点删除,id>=0就是创建)。进入死循环中,read读取事件到in_ev中,通过switch判断in_ev.type,如果是EV_ABS那就通过in_ev.code判断是什么触摸屏事件并存储对应值;如果是EV_SYN那就进行数据上报,在in_ev.code是SYN_REPORT时将xy[]中数据存到mt[]中。

main函数中,open打开设备文件,然后通过ioctl获取最大触摸点数,并通过calloc初始化mt,之后进入死循环中,调用ts_read读取,并把所有数据都printf出来。

使用tslib库

这是Linux系统下,专门为触摸屏开发的应用层函数库。

tslib简介

tslib为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏struct input_event 类型数据(这是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的API接口。tslib从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将
原始的触摸屏坐标转换为相应的屏幕坐标。

tslib有一个配置文件ts.conf,该配置文件中提供了一些配置参数,可进行修改。tslib可以作为Qt触摸屏输入插件,为Qt提供输入支持。

tslib移植

这里就直接看教程就好了。

这里感觉驱动教程那边更好,直接在buildroot里面使能编译就可以了。

tslib库函数

使用tslib库函数需要在应用程序中包含tslib的头文件tslib.h,使用tslib编程其实非常简单,基本步骤如下所示:

  • 第一步打开触摸屏设备;
  • 第二步配置触摸屏设备;
  • 第三步读取触摸屏数据。

打开触摸屏设备

使用tslib提供的库函数ts_open打开触摸屏设备,其函数原型如下所示:

#include "tslib.h"struct tsdev *ts_open(const char *dev_name, int nonblock);

参数dev_name指定了触摸屏的设备节点;参数nonblock表示是否以非阻塞方式打开触摸屏设备,如果nonblock等于0表示阻塞方式,如果为非0值则表示以非阻塞方式打开。调用成功返回一个struct tsdev *指针,指向触摸屏设备句柄;如果打开设备失败,将返回NULL。

还可以使用ts_setup()函数,其函数原型如下所示:

#include "tslib.h"struct tsdev *ts_setup(const char *dev_name, int nonblock)

参数dev_name可以设置为NULL,ts_setup()函数内部会读取TSLIB_TSDEVICE环境变量,获取该环境变量的内容以得知触摸屏的设备节点。

关闭触摸屏设备使用ts_close()函数:

int ts_close(struct tsdev *);

配置触摸屏设备

调用ts_config()函数进行配置,其函数原型如下所示:

#include "tslib.h"int ts_config(struct tsdev *ts);

参数ts指向触摸屏句柄。成功返回0,失败返回-1。

读取触摸屏数据

读取触摸屏数据使用ts_read()或ts_read_mt()函数,区别在于ts_read用于读取单点触摸数据,而ts_read_mt则用于读取多点触摸数据,其函数原型如下所示:

#include "tslib.h"int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr);
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr);

参数ts指向一个触摸屏设备句柄,参数nr表示对一个触摸点的采样数,设置为1即可!

ts_read_mt()函数有一个max_slots参数,表示触摸屏支持的最大触摸点数,应用程序可以通过调用ioctl()函数来获取触摸屏支持的最大触摸点数以及触摸屏坐标的最大分辨率等信息。

ts_read()函数的samp参数是一个struct ts_sample *类型的指针,指向一个struct ts_sample对象,struct ts_sample数据结构描述了触摸点的信息;调用ts_read()函数获取到的数据会存放在samp指针所指向的内存中。

ts_read_mt()函数的samp参数是一个struct ts_sample_mt **类型的指针,多点触摸应用程序,每一个触摸点的信息使用struct ts_sample_mt数据结构来描述;一个触摸点的数据使用一个struct ts_sample_mt对象
来装载,将它们组织成一个struct ts_sample_mt数组,调用ts_read_mt()时,将数组地址赋值给samp参数。

基于tslib编写触摸屏应用程序

单点触摸

需要定义tsdev结构体指针ts,ts_sample_mt结构体指针mt_ptr,input_absinfo结构体slot。

通过ts_setup打开触摸屏,第一个参数可以直接NULL就打开环境变量中的设备。

然后进入死循环,通过ts_read读取数据,通过samp.pressure判断是否按下,而保存的上一个pressure可辅助判断移动、按下与松开。

多点触摸

这个跟前面差不多,这里只列出区别。

通过ioctl函数获取最大触摸点数,同时mt_ptr需要calloc初始化。

死循环中,通过ts_read_mt读取数据,然后通过mt_ptr[].valid,如果为真就是数据有更新,之后一样通过pressure判断触摸情况。

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

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

相关文章

Prometheus的详细部署

普罗米修斯下载网址: Download | Prometheus 准备两台机器&#xff1a; 192.168.58.152 prometheus 192.168.58.142 node_exporter 关闭防火墙和selinux&#xff1a; [rootlocalhost ~]# setenforce 0 && systemctl stop firewalld[rootlocalhost ~]# seten…

nginx https 一个端口代理多个前端项目

打包修改 &#xff01;&#xff01;&#xff01;注意&#xff1a;第一个location root 调用的项目不用修改 打包路径&#xff0c;直接用 ‘/’&#xff0c;其他项目或者需加 /mobile 路径 worker_processes 1; events {worker_connections 1024; } http {include mime.…

K8S集群中PLEG问题排查

一、背景 k8s集群排障真的很麻烦 今天集群有同事找我&#xff0c;节点报 PLEG is not healthy 集群中有的节点出现了NotReady&#xff0c;这是什么原因呢&#xff1f; 二、kubernetes源码分析 PLEG is not healthy 也是一个经常出现的问题 POD 生命周期事件生成器 先说下PL…

for for in for of 的区别

for是JavaScript中最基本的循环语句&#xff0c;它可以用于遍历数组和对象中的元素&#xff0c;语法如下&#xff1a; for (初始化; 判断条件; 增量) {// 循环体 }其中&#xff0c;初始化是循环开始前执行的语句&#xff0c;判断条件是判断循环是否可以继续的条件&#xff0c;…

Pybullet -------[ 1 ]

1. p.addUserDebugText() 这个函数允许在仿真环境中动态添加文本&#xff0c;用于调试和可视化。你可以指定文本的内容、位置、颜色、大小等属性。 p.addUserDebugText(text, textPosition, textColorRGB[1,1,1], textSize1, lifeTime0, parentObjectUniqueId0,parentLinkInde…

机器视觉双目测宽仪具体有什么优势?

双目测宽仪是机器视觉原来制造而成的智能宽度检测设备&#xff0c;广泛应用于板材类产品的宽度检测。通过测宽仪的使用&#xff0c;实时了解产品宽度品质&#xff0c;进行超差提示&#xff0c;减少废品的生产。 双目测宽仪优势 测量软件界面显示&#xff1a;产品规格、标称宽…

MATLAB算法实战应用案例精讲-【图像处理】FPGA

目录 前言 算法原理 FPGA 是什么 FPGA的定义以及和GPU的类比 FPGA 有什么用 1)通信领域

Android控件全解手册 - 任意View缩放平移工具-源码

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

详细介绍如何使用 PaddleOCR 进行光学字符识别-(含源码及讲解)

阅读巨大的文档可能会非常累并且非常耗时。您一定见过许多软件或应用程序,只需单击图片即可从文档中获取关键信息。这是通过一种称为光学字符识别 (OCR) 的技术来完成的。光学字符识别是近年来人工智能领域的重点研究之一。光学字符识别是通过理解和分析图像的基本模式来识别图…

竞赛选题 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

文章目录 0 简介1 思路简介1.1 车牌定位1.2 畸变校正 2 代码实现2.1 车牌定位2.1.1 通过颜色特征选定可疑区域2.1.2 寻找车牌外围轮廓2.1.3 车牌区域定位 2.2 畸变校正2.2.1 畸变后车牌顶点定位2.2.2 校正 7 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享…

yolov8-pose姿势估计,站立识别

系列文章目录 基于yolov8-pose的姿势估计模式,实现站姿,坐姿,伏案睡姿识别,姿态动作识别接口逻辑作参考。本文以学习交流,分享,欢迎留言讨论优化。 yoloPose-姿势动作识别 系列文章目录前言一、环境安装二、使用yolov8-pose1.导入模型,预测图像三.姿势动作识别之站立总…

unity实时保存对象的位姿,重新运行程序时用最后保存的数据给物体赋值

using UnityEngine; using System.IO; // using System.Xml.Serialization; public class SaveCoordinates : MonoBehaviour {public GameObject MainObject;//读取坐标private float x;private float y;private float z;private Quaternion quaternion;private void Start(){/…

如何使用torchrun启动单机多卡DDP并行训练

如何使用torchrun启动单机多卡DDP并行训练 这是一个最近项目中需要使用的方式&#xff0c;新近的数据集大概在40w的规模&#xff0c;而且载入的原始特征都比较大&#xff08;5&#xff5e;7M&#xff09;&#xff0c;所以准备尝试DistributedDataParallel&#xff1b; 主要目…

Qt 自定义标题栏

在Qt中&#xff0c;如果你想要自定义窗口的标题栏&#xff0c;你可以通过覆盖窗口的windowTitleChanged信号来实现。然而&#xff0c;直接修改Qt的标题栏可能会带来一些问题&#xff0c;因为Qt的设计是尽量使窗口系统的行为标准化。 以下是一个基本的示例&#xff0c;如何在Qt…

Java中的集合

Java中的集合 java.util 包中的集合 Java 集合框架提供了各种集合类&#xff0c;用于存储和管理对象。以下是 Java 集合框架中常见的集合类&#xff1a; List 接口表示一个有序的集合&#xff0c;其中的元素可以重复。List 接口有以下实现类&#xff1a; ArrayList&#xff1…

人工智能_机器学习053_支持向量机SVM目标函数推导_SVM条件_公式推导过程---人工智能工作笔记0093

然后我们再来看一下支持向量机SVM的公式推导情况 来看一下支持向量机是如何把现实问题转换成数学问题的. 首先我们来看这里的方程比如说,中间的黑线我们叫做l2 那么上边界线我们叫l1 下边界线叫做l3 如果我们假设l2的方程是上面这个方程WT.x+b = 0 那么这里 我们只要确定w和…

<Linux> 文件理解与操作

目录 前言&#xff1a; 一、关于文件的预备知识 二、C语言文件操作 1. fope 2. fclose 3. 文件写入 3.1 fprintf 3.2 snprintf 三、系统文件操作 1. open 2. close 3. write 4. read 四、C文件接口与系统文件IO的关系 五、文件描述符 1. 理解文件描述符 2. 文…

时延抖动和通信的本质

先从网络时延抖动的根源说起。 信息能否过去取决于信道容量&#xff0c;而信道利用率则取决于编码。这是香农定律决定的。 考虑到主机处理非常快&#xff0c;忽略处理时延&#xff0c;端到端时延就是信息传播时延&#xff0c;但现实中通信信道利用率非常不均匀&#xff0c;统…

一则 MongoDB 副本集迁移实操案例

文中详细阐述了通过全量 增量 Oplog 的迁移方式&#xff0c;完成一套副本集 MongoDB 迁移的全过程。 作者&#xff1a;张然&#xff0c;DBA 数据库技术爱好者~ 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。 本文约 900…

python炒股自动化(1),量化交易接口区别

要实现股票量化程序化自动化&#xff0c;就需要券商提供的API接口&#xff0c;重点是个人账户小散户可以申请开通&#xff0c;上手要简单&#xff0c;接口要足够全面&#xff0c;功能完善&#xff0c;首先&#xff0c;第一步就是要找对渠道和方法&#xff0c;这里我们不讨论量化…