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

news2024/11/24 20:22:28

在之前的入门篇学习中,都是直接在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.coloradmin.cn/o/1263138.html

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

相关文章

uniapp使用vue3和ts开发小程序获取用户城市定位

这个组件的功能&#xff1a;可以重新定位获取到用户的具体位置&#xff0c;这个是通过getLocation这个api和高德地图的api获取到的&#xff0c;getLocation这个api需要在微信公众平台后台>开发管理> 接口管理里面申请才能使用的&#xff0c;不然无法使用哦&#xff0c;这…

数学老师怎么和家长沟通

作为一名数学老师&#xff0c;与家长建立良好的沟通关系是非常重要的。以下是我个人认为可以帮助与家长有效沟通的建议&#xff1a; 建立良好的第一印象 第一次与家长接触时&#xff0c;要尽可能展现出你的专业素养和热情。在交流中&#xff0c;要表达出你对孩子的关心和重视&…

Android 编译的配置文件:android.mk 和android.bp

Android.bp文件首先是Android系统的一种编译配置文件&#xff0c;是用来代替原来的Android.mk文件的。在Android7.0以前&#xff0c;Android都是使用make来组织各模块的编译&#xff0c;对应的编译配置文件就是Android.mk。在Android7.0开始&#xff0c;Google引入了ninja和kat…

接口文档自动生成工具:详细教程和实用技巧

本篇文章详细教你如何使用 Apifox 的 IDEA 插件实现自动生成接口代码。好处简单总结有以下几点&#xff1a; 自动生成接口文档&#xff1a; 不用手写&#xff0c;一键点击就可以自动 生成文档&#xff0c;当有更新时&#xff0c;点击一下就可以自动同步接口文档&#xff1b;代…

小程序静默授权获取unionid

文章目录 导文文章重点 导文 小程序静默授权获取unionid 文章重点 用wx.login(Object object)放到app.js里面 wx.login({success (res) {console.log(123);if (res.code) {//发起网络请求// wx.request({// url: https://example.com/onLogin,// data: {// code: res.…

ERRO报错

无法下载nginx 如下解决&#xff1a; 查看是否有epel 源 安装epel源 安装第三方 yum -y install epel-release.noarch NGINX端口被占用 解决&#xff1a; 编译安装的NGINX配置文件在/usr/local/ngin/conf 修改端口

AI - Navmesh 寻路

用cocos2dx引擎简单实现了一下navmesh的多边形划分&#xff0c;然后基于划分多边形的a*寻路。以及路径拐点优化算法 用cocos主要是方便使用一些渲染接口和定时器。重点是实现的原理。 首先画了一个带有孔洞的多边形 //多边形的顶点数据Vec2(100, 100),Vec2(300, 200),Vec2(50…

程序员的软件开发帮手,低代码当仁不让

目录 一、低代码是什么&#xff1f; 二、低代码的能力表现 1.提供可视化开发 2.预构建的组件和模板 3.集成的开发和测试工具 4.跨平台兼容性 5.可伸缩性和可扩展性&#xff1a; 跟随互联网信息技术快速发展的脚步&#xff0c;各行各业都在积极拥抱数字化转型。在这个过程中&…

详解STL库—map和set

目录 一、关联式容器 二、键值对 SGI-STL中关于键值对的定义&#xff1a; 三、set 3.1 set的介绍 3.2 set的使用 1.set的模板参数列表​编辑 2. set的构造 3. set的迭代器 4. set的容量 5. set修改操作 6. set的使用举例 四、map 4.1map的介绍 4.2 map的使用 1…

国产操作系统-银河麒麟V10

一、介绍 银河麒麟操作系统隶属于麒麟软件&#xff0c;麒麟软件是专业从事国产操作系统研发和产业化的企业&#xff0c;面向通用和专用领域打造安全创新的国产操作系统产品和相应解决方案&#xff0c;旗下拥有银河麒麟、中标麒麟、星光麒麟三大产品品牌。 麒麟软件官方网站地…

【攻防世界-misc】glance-50

1.得到一个动图 2.使用GIF动态图片分解&#xff0c;多帧动态图分解成多张静态图片_图片工具网页版&#xff0c;将图片定格组合&#xff0c; 由此得到flag值&#xff0c;拼写提交。

卡码网语言基础课 | 15. 链表的基础操作Ⅲ

目录 一、 插入链表的过程 二、 删除链表的过程 三、 打印链表 3.1 判断节点是否处于链尾 3.2 打印链表 3.3 循环体结束&#xff0c;遍历打印 题目&#xff1a; 请编写一个程序&#xff0c;实现以下链表操作&#xff1a;构建一个单向链表&#xff0c;链表中包含一组整数…

c++没有返回值的返回值

上面的函数search没有返回值,因为a不等于1,但是输出的时候会输出6.这恰巧是x的值,如果我们希望a不等于1时返回x,那么这种结果反而是正确的.有时候这种错误的代码可能产生正确的结果反而会加大debug难度 int search(int n) { 00007FF66DB723E0 mov dword ptr [rsp8],e…

【Linux系统编程】进程概念详解(什么是进程?如何查看进程?)

目录 一、前言 二、 什么是进程&#xff1f; &#x1f4a6;引出进程 &#x1f4a6;进程的基本概念 &#x1f4a6;理解进程 ⭐描述进程--PCB&#xff08;进程控制块&#xff09; ⭐组织进程 三、查看进程 &#x1f4a6; 通过 ps 命令查看进程 &#x1f4a6; 通过 l…

事件代理?

1.什么是事件代理&#xff1f; 事件代理也叫事件委托&#xff0c;只指定一个事件处理程序&#xff0c;就可以管理某一类型得事件。 可以简单理解为&#xff0c;事件代理就是将本应该绑定子元素事件绑定给父元素代理。它的优点就是&#xff1a;减少事件得执行&#xff0c;减少浏…

2023/11/28JAVAweb学习

查找哪个进程占用了该端口号 跳过某一个阶段

欧拉公式推导

欧拉恒等式 函数推导过程(幂级数展开的方式近似&#xff0c;后面用到了三角函数展开的方式) 从导数中推导的方程&#xff0c;对于该函数当x0时为1即初值,导数为自身&#xff1b; 设,当x 0时&#xff0c; 因为函数是收敛的所以会越来越精确&#xff08;引用自MIT公开课&#xf…

激光器温度,波长变化

940&#xff0c;波长变化0.3nm/C

小白必知:AIGC 和 ChatGPT 的区别

原文 &#xff1a; https://openaigptguide.com/chatgpt-aigc-difference/ AIGC 和 ChatGPT 都是人工智能技术&#xff0c;但它们的功能和应用场景不同。 AIGC&#xff08;AI-GeneratedContent&#xff0c;人工智能自动生成内容&#xff09;是人工智能、计算机图形学和深度学…

强烈推荐:零售行业升级教程,现学现用

新零售模式是随着科技的迅猛发展而崭露头角的一种零售业态。在这个数字化时代&#xff0c;消费者的购物习惯和期望正在发生根本性的变化&#xff0c;推动着传统零售业寻找创新的方式来满足不断变化的市场需求。 自动售货机作为新零售模式的一部分&#xff0c;以其高效、便捷、智…