韦东山嵌入式Liunx入门应用开发二(含课后作业、代码详解)

本文详细介绍了Linux系统中的输入系统编程,包括输入设备管理、API操作如非阻塞和POLL机制,以及电阻屏和电容屏的工作原理。同时提到了网络编程的基础概念和多线程编程的简要提及。
摘要由CSDN通过智能技术生成


本人学习完韦老师的视频,因此来复习巩固,写以笔记记之。
看完视频复习的同学观看最佳!
基于 IMX6ULL-PRO
参考视频 Linux快速入门到精通视频
参考资料:01_嵌入式Linux应用开发完全手册V5.1_IMX6ULL_Pro开发板.pdf

一、输入系统应用编程

1-1 输入系统简介

输入设备:常见的有键盘、鼠标、遥控杆、书写板、触摸屏等。用户通过这些输入设备与 Linux 系统进行数据交换。
输入系统:Linux系统为了统一管理这些输入设备,实现了一套能兼容所有输入设备的框架。框架:输入系统。驱动开发人员基于这套框架开发出程序,应用开发人员就可以使用统一的API去使用设备。
输入系统框架如下图所示:
在这里插入图片描述
/dev/input/event0 是Linux操作系统的一个输入设备,代表了计算机上的第一个物理输入设备,通常是键盘、鼠标、触摸屏。所有输入设备都被视为文件,以便于使用文件I/O操作来读写或写入输入设备的数据。

假设用户程序直接访问dev/input/event0 设备节点,数据的流程为:
APP发起读操作,若无数据则休眠;
② 用户操作设备,硬件上产生中断;
③ 输入系统驱动层对应的驱动程序处理中断:读取到数据,转换为标准的输入事件,向核心层汇报。输入事件即“struct input_event ”结构体。
④ 核心层可以决定把输入事件转发给上面哪个 handler来处理:从handler 的名字来看,它就是用来处输入操作的。有多种 handler ,比如:evdev_handler 、kbd_handler 、joydev_handler 等等。

内核中使用input_dev 结构体来表示输入设备,部分内容如下图所示

struct input_dev {
	const char *name;//输入设备的名字
	const char *phys;//在系统层次结构中设备的物理路径;
	const char *uniq;
	struct input_id id;//输入设备id,包含总线类型,制造商id,产品id和版本号等;
 
	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];//设备属性和怪异位图
 
	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//设备支持的事件类型;EV_KEY, EV_REL;
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//设备具有的按键(能够报告的)
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//设备相对轴
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//设备支持的杂项设备事件
	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//设备存在的led位图
	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//设备支持的声音效果
	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];//设备支持的力反馈效果位图
	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];//设备存在的开关位图

APP可以从struct input_event 结构体获取一系列输入事件

struct input_event {
	struct timeval time;
	__u16 type;  		/*哪类事件*/
	__u16 code;			/*哪个事件*/
	__s32 value;		/*事件值*/
};

① type:表示哪类事件
比如EV_KEY表示按键类、 EV_REL表示相对位移(比如鼠标)、 EV_ABS 表示绝对位置(比如触摸屏)

/* Event type */
#define EV_SYN			0x00
#define EV_KEY			0x01
#define EV_REL			0x02
#define EV_ABS			0x03
#define EV_MSC			0x04
#define EV_SW			0x05
#define EV_LED			0x11
#define EV_SND			0x12
#define EV_REP			0x14
#define EV_FF			0x15
#define EV_PWR			0x16
#define EV_FF_STATUS		0x17
#define EV_MAX			0x1f
#define EV_CNT			(EV_MAX+1)

② code: :表示该类事件下的哪一个事件
比如对于EV_KEY(按键)类事件,它表示键盘。键盘上有很多按键,比如数字键
1、2、3,字母键A、B、C

#define KEY_RESERVED		0
#define KEY_ESC			1  /*键盘数字1*/
#define KEY_1			2
#define KEY_2			3
#define KEY_3			4
#define KEY_4			5
#define KEY_5			6
#define KEY_6			7
#define KEY_7			8
#define KEY_8			9
#define KEY_9			10
#define KEY_0			11
#define KEY_MINUS		12
#define KEY_EQUAL		13
#define KEY_BACKSPACE		14
#define KEY_TAB			15
#define KEY_Q			16     /*键盘数字Q*/
#define KEY_W			17
#define KEY_E			18
#define KEY_R			19
#define KEY_T			20
#define KEY_Y			21

③ value:表示事件值
对于按键,它的value 可以是0表示按键被按下 ;1表示按键被松开;2表示长按
对于触摸屏,它的value就是坐标值、压力值。
④ 事件之间的界线
驱动程序上报完一系列的数据后,会上报一个“同步事件”,表示数据上报完
毕。 APP读到“同步事件”时,就知道已经读完了当前的数据。同步事件也是一个input_event 结构体,它的 type 、code 、value 三项都是0 。

1-2 设备信息
(1) 列出输入设备的节点
ls -l /dev/input/*

在这里插入图片描述
/dev/input/event0:一个字符设备文件,通常代表一个输入事件设备;event0表示第一个输入事件设备,event1表示第二个输入事件设备,mice表示鼠标设备。
/dev/input/by-path: 这是一个目录,包含了通过路径链接到输入设备的符号链接。在这里,有一个符号链接 platform-20cc000.snvs:snvs-powerkey-event,可能与与电源按键相关的输入事件设备有关。

(2) 查看设备节点对应的硬件
cat /proc/bus/input/devices

在这里插入图片描述

经测试,SNVS对应event0的是板子上的开关机的按键,正对屏幕的右上角,挨着电源开关的按键;触摸屏对应event1事件
① I:id of the device(设备 ID)
该参数由结构体struct input_id来进行描述

struct input_id {
	__u16 bustype;
	__u16 vendor;
	__u16 product;
	__u16 version;
};

② N:name of the device 设备名称
③ P:physical path to the device in the system hierarchy 系统层次结构中设备的物理路径
④ S:sysfs path 位于sys文件系统的路径
⑤ U:unique identification code for the device 设备的唯一标识码
⑥ H:list of input handles associated with the device. 与设备关联的输入句柄列表
⑦ B:bitmaps(位图 )
PROP:设备属性
EV: 设备支持的事件类型
KEY: 此设备具有的键按钮
MSC: 设备支持的其他事件
LED: 设备上的指示灯

使用命令读取触摸屏上的数据

hexdump /dev/input/event1

在这里插入图片描述

type为3 ,对应EV_ABS;code为0x35 对应ABS_MT_POSITION_X;code 为0x36对应 ABS_MT_POSITION_ Y。2 个同步事件:它的type 、code 、value 都为 0。表示电容屏上报了 2 次完整的数据。

1-3 系统支持完整的API操作

获取设备信息编写的程序,放于poll机制中一同讲解

(1) 查询方式

APP调用open函数时,传入“O_NONBLOCK ”表示非阻塞
APP调用read函数读取数据时,如果驱动程序中有数据,那么 AP 的read函数会返回数据,否则也会立刻返回错误。

(2) 休眠-唤醒方式

APP 调用open函数时,不带参数,默认为阻塞状态。
APP 调用read函数读取数据时,如果驱动程序中有数据,那么APP的read函数会返回数据;否则APP就会在内核态休眠,当有数据时驱动程序会把APP唤醒,read函数恢复执行并返回数据给APP 。

(3) POLL机制

POLL函数就是“定个闹钟”:在调用poll、select 函数时可以传入“超时时间”。在这段时间内,条件合适时(比如有数据可读、有空间可写)就会立刻返回,否则等到“超时时间”结束时返回错误。
APP可以根据POLL函数的返回值判断返回原因,有无数据或等待超时,然后就调用read函数读取数据,这时就会立刻获得数据。

代码详解

err = ioctl(fd, EVIOCGID, &id);   /*用于获取输入设备的信息,放于input_id类型的id结构体中*/

在这里插入图片描述

len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);

这个有点麻烦,个人理解,仅供参考
在evdev_do_ioctl函数中,
在这里插入图片描述

在这里插入图片描述

(3.1) poll函数监测多个输入设备

前文有提及,板子上有两个输入设备;event0代表右上角那个按键,event1代表触摸屏,本人在usb串口外接一个鼠标,系统显示为event2,以此来做测试。
插入鼠标后,查看硬件信息中,板子信息中会多出一个event2(鼠标)
在这里插入图片描述
代码部分


/* ./01_get_input_info /dev/input/event1  /dev/input/event2*/
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int ret;
	int i;
	int j;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	struct input_event event;
	
	struct pollfd fds[2];
	nfds_t nfds = 2;

	/*指针数组*/
	char *ev_names[] = {   /*事件类型*/
		/*为节省空间,按课程中poll源代码复制即可,头文件也一样*/
		};
	
	if (argc != 3){
		printf("Usage: %s <dev> <dev> \n", argv[0]);
		return -1;
	}

	for(i = 0; i< nfds; i++){
		fd = open(argv[1 + i], O_RDWR | O_NONBLOCK); /*非阻塞方式打开文件*/
		//fd = open(argv[1 + i], O_RDWR ); 
		if (fd < 0){
			printf("open %s err\n", argv[1]);
			return -1;
		}

		err = ioctl(fd, EVIOCGID, &id);   /*用于获取输入设备的信息,放于input_id类型的id结构体中*/
		if (err == 0){
			printf("bustype = 0x%x\n", id.bustype );
			printf("vendor	= 0x%x\n", id.vendor  );
			printf("product = 0x%x\n", id.product );
			printf("version = 0x%x\n", id.version );
		}

		len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit); /*evbit中的每一位对应每一种事件类型*/
		if (len > 0 && len <= sizeof(evbit))  /*成功读到数据*/
		{
			printf("support ev type: ");
			for (j = 0; j < len; j++){
				byte = ((unsigned char *)evbit)[j];
				for (bit = 0; bit < 8; bit++){			/*逐位判断*/
					if (byte & (1<<bit)) {  		/*该位为1 则证明支持某一事件类型*/
						printf("%s ", ev_names[j*8 + bit]);   /*打印为哪一类事件 键盘、触摸屏、按键*/
					}
				}
			}
			printf("\n");
		}
		fds[i].fd = fd;
		fds[i].events = POLLIN;
		fds[i].revents = 0;
		
	}
	
	while (1)
	{		
		ret = poll(fds, nfds, 5000);
		if(ret > 0){
			printf("ret = %d\n",ret);
			for(i = 0; i < nfds; i++){
				if(fds[i].revents = POLLIN){
					while (read(fds[i].fd, &event, sizeof(event) == sizeof(event))) {
						printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
						fds[i].revents = 0;  /*实测无效,poll机制是不断轮询,会一直打印*/
					}
				}
			}
		}
		else if(ret == 0){
			printf("time out\n");
		}
		else {
			printf("poll err");
		}
	}
	return 0;
}

结果展示,无论是动鼠标还是动触摸屏都会打印对应事件的信息。
在这里插入图片描述
在这里插入图片描述

(3.2) select函数实现同样的功能

源代码文件中有,不多加赘述。

(4) 异步通知方式

异步通知,就是APP可以忙自己的事,当驱动程序用数据时它会主动给APP发信号,这会导致APP执行信号处理函数。
驱动程序通知APP时,它会发出SIGIO这个信号,表示有IO事件要处理。
就APP而言。你想处理SIGIO信息,那么需要提供信号处理函数跟SIGIO挂钩。
① 编写信号处理函数:

void my_sig_handler(int sig)
{
	struct input_event event;
	while(read(fd, &event, sizeof(event)) == sizeof(event)){
		printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
	}
}

② 注册信号处理函数:

signal(SIGIO, my_sig_handler);

③ 打开设备文件

fd = open(argv[1], O_RDWR);

④ 把进程 ID告诉驱动:

fcntl(fd, F_SETOWN, getpid()); //fcntl 获取文件信息

⑤ 使能异步通知:

flags = fcntl(fd, F_GETFL);  //F_GETFL获取文件状态
fcntl(fd, F_SETFL, flags | FASYNC);  //F_SETFL设置文件状态
1-4 电阻屏和电容屏

触摸屏分为电阻屏、电容屏。电阻屏结构简单;电容屏支持多点触摸,现在的手机基本都是使用电容屏。LCD是输出设备,触摸屏是输入设备。

(1) 原理

电阻屏原理:电阻屏就是基于欧姆定律制作的,它有上下两层薄膜,这两层薄膜就是两个电阻。平时上下两层薄膜无触触,当点击触摸屏时,上下两层薄膜接触;这时就可以测量触点电压。
在x两端施加电压,来测x坐标;y坐标同理。
Linux 驱动程序中,会上报触点的X 、Y数据,注意:这不是LCD的坐标值,需要APP再次处理才能转换为LCD坐标值。
对应的 input_event 结构体中,type、code、value如下
按下时

EV_ KEY 	BTN_TOUCH 		1 			/* 按下
EV_ABS 		ABS_PRESSURE 	1 			/* 压力值,可以上报,也可以不报,可以是其他压力值
EV_ABS 		ABS_X 			x_value 	/* X 坐标
EV_ABS 		ABS_ Y 			y _value 	/* Y 坐标
EV_ SYNC 	0 				0 			/* 同步事件

松开时

EV_ KEY 		BTN_TOUCH 		0 		/* 松开
EV_ABS 			ABS_PRESSURE 	0 		/* 压力值,可以上报,也可以不报
EV_ SYNC 		0 				0 		/* 同步事件

电容屏原理:电容屏中有一个控制芯片 ,它会周期性产生驱动信号,接收电极接收到信号,并可测量电荷大小。当电容屏被按下时,相当于引入了新的电容,从而影响了接收电极接收到的电荷大小。主控芯片根据电荷大小即可计算出触点位置。
触摸屏能分辨是哪一个触点,上报数据时会先上报触点ID ,再上报数据。

1-5 tslib

tslib是一个触摸屏的开源库,可以使用它来访问触摸屏设备,可以给输入
设备添加各种filter(过滤器,就是各种处理 )。
编译tslib后,可以得到相应的库和各种工具:较准工具、测试工具。
略(有需要后面可补齐)

二、网络通信

2-1 网络编程

所有的数据传输,都有三个要素:源、目的、长度。
两个浏览器访问同一个网站,网站对应的服务器根据源端口来区别用一个IP下的两个连接。
而两个软件需要访问的服务器IP相同,而服务不同,则需要指定目的端口;一般的,80端口代表http服务,22端口代表ssh服务。

三、多线程编程

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很抱歉,我并不能完全理解您的问题。如果您是想了解关于嵌入式Linux机器人的开发,这需要考虑到硬件和软件两个方面。一般情况下,我们需要选择合适的硬件平台,并在其上安装嵌入式Linux系统,然后使用编程语言(如C/C++、Python等)编写机器人的控制程序。 以下是一个简单的示例代码,使用Python语言实现了一个基于嵌入式Linux机器人的前进和后退功能: ``` python import RPi.GPIO as GPIO import time # 设置GPIO口 GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.setup(13, GPIO.OUT) GPIO.setup(15, GPIO.OUT) GPIO.setup(19, GPIO.OUT) # 向前 def forward(): GPIO.output(11, GPIO.HIGH) GPIO.output(13, GPIO.LOW) GPIO.output(15, GPIO.HIGH) GPIO.output(19, GPIO.LOW) # 向后 def backward(): GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.HIGH) GPIO.output(15, GPIO.LOW) GPIO.output(19, GPIO.HIGH) # 停止 def stop(): GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.LOW) GPIO.output(15, GPIO.LOW) GPIO.output(19, GPIO.LOW) # 主函数 if __name__ == '__main__': try: while True: forward() time.sleep(2) stop() time.sleep(1) backward() time.sleep(2) stop() time.sleep(1) except KeyboardInterrupt: GPIO.cleanup() ``` 上述代码使用了树莓派(Raspberry Pi)的GPIO库,通过设置GPIO口的电平来控制机器人的前进和后退。当然,这只是一个简单的示例代码,实际的机器人开发还需要考虑到很多其他的因素,比如传感器、图像识别等等。 ### 回答2: 嵌入式Linux机器人的实现源代码可以分为几个关键方面: 1.硬件驱动程序:为了使机器人能够与外部设备进行通信和控制,需要编写硬件驱动程序。这些驱动程序可以包括控制底盘的驱动程序、传感器驱动程序、摄像头驱动程序等。 2.操作系统和内核定制:嵌入式Linux机器人需要一个适合嵌入式设备的操作系统和内核。在源代码中,我们需要对操作系统和内核进行定制,以满足机器人的需求。例如,可以选择定制轻量级的Linux发行版,去除不必要的组件和功能,加入对机器人硬件的支持等。 3.通信和控制程序:机器人要能够与其他设备、服务器或人机界面进行通信和控制。因此,我们需要编写通信和控制程序,以实现远程控制、信息传输等功能。这些程序可以使用网络协议、串口通信或其他通信方式。 4.感知和决策算法:机器人需要能够感知周围环境并做出相应决策。在源代码中,我们需要实现各种感知算法,如图像识别、目标跟踪、距离测量等,并结合决策算法,使机器人能够做出适当的行动。 5.用户界面和应用程序:为了更好地与机器人进行交互,我们可以编写用户界面和应用程序。这些程序可以包括机器人状态显示、地图生成、路径规划等。用户界面可以是图形化界面或命令行界面,根据实际需求进行选择。 总之,嵌入式Linux机器人的实现源代码是一个综合性的工程,需要涉及到硬件驱动、操作系统定制、通信和控制程序、感知和决策算法以及用户界面和应用程序等方面的编写和集成。这些源代码的编写需要深入理解嵌入式Linux系统的原理和机器人的工作原理,并结合实际的硬件和应用需求进行开发。 ### 回答3: 嵌入式Linux机器人的源代码实现可以包括以下几个方面: 1. 系统初始化:源代码需要实现嵌入式Linux系统的初始化,包括启动引导程序、加载内核镜像、初始化硬件设备、加载文件系统等。 2. 传感器驱动:机器人通常需要使用各种传感器来感知环境。源代码需要实现传感器驱动程序,以便与传感器进行通信和数据交换,如摄像头驱动、红外传感器驱动、超声波传感器驱动等。 3. 运动控制:机器人的运动控制涉及到电机、舵机等设备的驱动。源代码需要实现运动控制的相关算法,如PID控制算法、路径规划算法等,并与电机和舵机进行通信,控制机器人的运动。 4. 环境感知:源代码需要实现对机器人周围环境的感知和分析,以便进行智能决策。例如,图像处理算法可以用于识别目标物体,声音处理算法可以用于语音识别等。 5. 通信模块:机器人通常需要与其他系统进行通信,如与远程控制台进行通信、与其他机器人进行协作等。源代码需要实现通信模块,以便机器人能够与其他系统进行数据传输和指令交互。 6. 高级功能:根据机器人的具体应用场景,源代码还可以实现一些高级功能,如人脸识别、目标跟踪、自主导航等。这些功能需要依赖于相应的算法和模型,源代码需要与这些算法和模型进行集成。 总之,基于嵌入式Linux实现机器人的源代码了系统初始化、传感器驱动、运动控制、环境感知、通信模块以及各种高级功能的实现。这些源代码的编写需要结合具体的硬件平台和应用需求,并进行充分的调试和测试,确保机器人的正常运行和实现预期的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值