linux应用提高

应用层操控硬件的两种方式
编译app模板:arm-linux-gnueabihf-gcc miscbeepApp.c -o miscbeepApp
其实设备文件便是各种硬件设备向应用层提供的一个接口,应用层通过对设备文件的 I/O 操作来操控硬件设备,所以设备文件其实是与硬件设备相互对应的。设备文件通常在/dev/目录下,我们也把/dev 目录下的文件称为设备节点。
sysfs 文件系统页可以对硬件系统进行操作,sysfs 是一个基于内存的文件系统,同 devfs、proc 文件系统一样,称为虚拟文件系统;它的作用是将内核信息以文件的方式提供给应用层使用。
sysfs 文件系统把连接在系统上的设备和总线组织成为一个分级的文件、展示设备驱动模型中各组件的层次关系,同时sysfs 文件系统挂载在/sys 目录下。sysfs 文件系统中的目录,包括 block、bus、class、dev、devices、firmware、fs、kernel、modules、power 等,系统中所有的设备(对象)都会在/sys/devices 体现出来,是 sysfs 文件系统中最重要的目录结构;而
/sys/bus、/sys/class、/sys/dev 分别将设备按照挂载的总线类型、功能分类以及设备号的形式将设备组织存放在这些目录中,这些目录下的文件都是链接到了/sys/devices 中。
设备的一些属性、数据通常会通过设备目录下的文件体现出来,也就是说设备的数据、属性会导出到用户空间,以文件形式为用户空间提供对这些数据、属性的访问支持,可以把这些文件称为属性文件;读这些属性文件就表示读取设备的属性信息,相反写属性文件就表示对设备的属性进行设置、以控制设备的状态。(这里可以通过控制设备文件,实现对硬件的操作)
总结:应用层想要对底层硬件进行操控,通常可以通过两种方式:
/dev/目录下的设备文件(设备节点);
/sys/目录下设备的属性文件;
这里通过那种方法控制主要跟设备驱动有关,一般简单地设备会使用 sysfs 方式操控,其设备驱动在实现时会将设备的一些属性导出到用户空间 sysfs 文件系统,以属性文件的形式为用户空间提供对这些数据、属性的访问支持,譬如 LED、GPIO 等。但对于一些较复杂的设备通常会使用设备节点的方式,譬如 LCD 等、触摸屏、摄像头等。(其实sys本质还是使用write等操作,但是优点就是提供了属性文件,可以直接对特定属性文件操作,所以只能简单功能的设备使用sys方法方便)
LED的sys应用程序编写:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define LED_TRIGGER "/sys/class/leds/sys-led/trigger"
#define LED_BRIGHTNESS "/sys/class/leds/sys-led/brightness"
#define USAGE() fprintf(stderr, "usage:\n" \
 " %s <on|off>\n" \
 " %s <trigger> <type>\n", argv[0], argv[0])
int main(int argc, char *argv[])//ps,这里的argc和argv是常用的传参套路,eg:./testapp on,则代表输入两个str值,argc=2,argv[0]=./testapp,argv[1]=on,
{
 int fd1, fd2;
 /* 校验传参 */
 if (2 > argc) {//如果只输入一个或不输入报错
 USAGE();
 exit(-1);
 }
  /* 打开文件 */
 fd1 = open(LED_TRIGGER, O_RDWR);
 if (0 > fd1) {
 perror("open error");
 exit(-1);
 }
 fd2 = open(LED_BRIGHTNESS, O_RDWR);
 if (0 > fd2) {
 perror("open error");
 exit(-1);
 }
 /* 根据传参控制 LED */
 if (!strcmp(argv[1], "on")) {
 write(fd1, "none", 4); //先将触发模式设置为 none
 write(fd2, "1", 1); //点亮 LED
 }
 else if (!strcmp(argv[1], "off")) {
 write(fd1, "none", 4); //先将触发模式设置为 none
 write(fd2, "0", 1); //LED 灭
 }
 else if (!strcmp(argv[1], "trigger")) {//字符串对比实现具体功能
 if (3 != argc) {
 USAGE();
 exit(-1);
 }
 if (0 > write(fd1, argv[2], strlen(argv[2])))
 perror("write error");
 }
 else
 USAGE();
 exit(0);
}

ps:sprint(a,“bb-%s”,c[1])作用是将bb-c[1]赋值到a,发送格式化输出到 a所指向的字符串。
打开文件标准参考代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
static char gpio_path[100];
static int gpio_config(const char *attr, const char *val)
{
 char file_path[100];
 int len;
 int fd;
 sprintf(file_path, "%s/%s", gpio_path, attr);//将传入的相对地址跟绝对地址结合,相对地址是传参,绝对地址是全局变量,这个是重点
 if (0 > (fd = open(file_path, O_WRONLY))) {
 perror("open error");
 return fd;
 }
 len = strlen(val);
 if (len != write(fd, val, len)) {
 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]);
 exit(-1);
 }
 /* 判断指定编号的 GPIO 是否导出 */
 sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
 if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出。确定文件或文件夹的访问权限,R_OK 只判断是否有读权限W_OK 只判断是否有写权限X_OK 判断是否有执行权限F_OK 只判断是否存在,
 int fd;
 int len;
 if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) {
 perror("open error");
 exit(-1);
 }
 len = strlen(argv[1]);
 if (len != write(fd, argv[1], len)) {//导出 gpio
 perror("write error");
 close(fd);
 exit(-1);
 }
 close(fd); //关闭文件
 }
 /* 配置为输出模式 */
 if (gpio_config("direction", "out"))
 exit(-1);
 /* 极性设置 */
 if (gpio_config("active_low", "0"))
 exit(-1);
 /* 控制 GPIO 输出高低电平 */
 if (gpio_config("value", argv[2]))
 exit(-1);
 /* 退出程序 */
 exit(0);
}
	**常见头文件及其作用:**

主目录的13 个.h 头文件:
<errno.h>:错误号头文件,包含系统中各种出错号。(Linux 从 minix 中引进的)。
<fcntl.h>:文件控制头文件,用于文件及其描述符的操作控制常数符号的定义。fd_ctr
<signal.h>:信号头文件,定义信号符号常量,信号结构以及信号操作函数原型。
<string.h>:字符串头文件,主要定义了一些有关字符串操作的嵌入函数。
<termios.h>:终端输入输出函数头文件,主要定义控制异步通信口的终端接口。
<time.h>:时间类型头文件,主要定义了 tm 结构和一些有关时间的函数原形。
<unistd.h>:Linux 标准头文件,定义了各种符号常数和类型,并声明了各种函数。
如,定义了__LIBRARY__,则还包括系统调用号和内嵌汇编_syscall0()等。
<ctype.h>:字符类型头文件,定义了一些有关字符类型判断和转换的宏。
<stdarg.h>:标准参数头文件,以宏的形式定义变量参数列表。主要说明了一个类型 (va_list)和 3 个宏(va_start,va_arg 和 va_end),用于 vsprintf、 vprintf、vfprintf 函数。
<stddef.h>:标准定义头文件,定义了 NULL, offsetof(TYPE, MEMBER)。
<a.out.h>:定义了 a.out 执行文件格式和一些宏。
<const.h>:常数符号头文件,目前仅定义了 i 节点中 i_mode 字段的各标志位。
<ctype.h>:字符类型头文件,定义了一些有关字符类型判断和转换的宏。
<utime.h>:用户时间头文件,定义了访问和修改时间结构以及 utime()原型。
汇编与体系结构相关的 : 4个.h 头文件 include/asm(其中asm代表的是汇编!以后看到asm理解为汇编即可,asm是一个符号连接只有在你的主makefile的ARCH 变量赋值,并且编译过一遍内核之后才会指向对应的体系结构)这些头文件主要定义了一些与 CPU 体系结构密切相关的数据结构、宏函数和变量。
<asm/io.h>:I/O 头文件,以宏的嵌入汇编程序形式定义对 I/O 端口操作的函数。
<asm/memory.h>:内存拷贝头文件,含有 memcpy()嵌入式汇编宏函数。
<asm/segment.h>:段操作头文件,定义了有关段寄存器操作的嵌入式汇编函数。
<asm/system.h>:系统头文件,定义了设置或修改描述符/中断门等的嵌入式汇编宏。
Linux 内核专用的:10 个.h 头文件 include/linux
<linux/config.h>:内核配置头文件,定义键盘语言和硬盘类型(HD_TYPE)可选项。
<linux/fdreg.h>:软驱头文件,含有软盘控制器参数的一些定义。
<linux/fs.h>:文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)。
<linux/hdreg.h>:硬盘参数头文件,定义访问硬盘寄存器端口、状态码和分区表等信息。
<linux/head.h>:head头文件,定义了段描述符的简单结构,和几个选择符常量。
<linux/kernel.h>:内核头文件,含有一些内核常用函数的原形定义。
<linux/mm.h>:内存管理头文件,含有页面大小定义和一些页面释放函数原型。
<linux/sched.h>: 调度程序头文件,定义了任务结构task_struct、初始任务0的数据,以及一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
<linux/sys.h>:系统调用头文件,含有72个系统调用C函数处理程序,以"sys_"开头。
<linux/tty.h>:tty头文件,定义了有关tty_io,串行通信方面的参数、常数。
系统专用数据结构的:5 个.h 头文件 include/sys
<sys/stat.h>: 文件状态头文件,含有文件或文件系统状态结构stat{}和常量。
<sys/times.h>:定义了进程中运行时间结构tms以及times()函数原型。
<sys/types.h>:类型头文件,定义了基本的系统数据类型。
<sys/utsname.h>:系统名称结构头文件。
<sys/wait.h>:等待调用头文件,定义系统调用wait()和waitpid()及相关常数符号
Linux 驱动头文件说明 (驱动程序)
#include <linux/.h> 是在linux-2.6.29/include/linux下面寻找源文件。
#include <asm/
.h> 是在linux-2.6.29/arch/arm/include/asm下面寻找源文件。
#include <mach/***.h> 是在linux-2.6.29/arch/arm/mach-s3c2410/include/mach下面寻找源文件。
#include <plat/regs-adc.h>是在linux-2.6.31_TX2440A20100510\linux-2.6.31_TX2440A\arch\arm\plats3c\include\plat 下面寻找源文件。
与Linux驱动函数头文件相关
#include <linux/module.h> :最基本的文件,支持动态添加和卸载模块。Hello World驱动只需要这一个文件就可以了。
#include <linux/fs.h> :包含了文件操作相关struct的定义,例如大名鼎鼎的struct file_operations ,包含了struct inode 的定义,MINOR、MAJOR的头文件。
#include <linux/errno.h> :包含了对返回值的宏定义,这样用户程序可以用 perror输出错误信息。
#include <linux/types.h> :对一些特殊类型的定义,例如dev_t, off_t, pid_t.其实这些类型大部分都是unsigned int型通过一连串的typedef变过来的,只 是为了方便阅读。
#include <linux/cdev.h> :对字符设备结构cdev以及一系列的操作函数的定义。 包含了cdev 结构及相关函数的定义。
#include <linux/wait.h> :等代队列相关头文件//内核等待队列,它包含了自旋锁的头文件。
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h> :包含了kcalloc、kzalloc内存分配函数的定义。
#include <linux/uaccess.h> :包含了copy_to_user、copy_from_user等内核访问用户进程内存地址 的函数定义。
#include <linux/device.h> :包含了device、class 等结构的定义
#include <linux/io.h> :包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/miscdevice.h> :包含了miscdevice结构的定义及相关的操作函数。
#include <linux/interrupt.h> :使用中断必须的头文件
#include <mach/irqs.h> :使用中断必须的头文件
#include <asm/bitops.h> :包含set_bit等位操作函数,实现Input子系统时可用。
#include <linux/semaphore.h> :使用信号量必须的头文件
#include <linux/spinlock.h> :自旋锁
#include <linux/sched.h> :内核等待队列中要使用的TASK_NORMAL、TASK_INTERRUPTIBLE包含 在这个头文件
#include <linux/kfifo.h> :fifo环形队列
#include <linux/timer.h> :内核定时器
#include <linux/input.h> :中断处理
常见变量缩写
cfg:配置config缩写,info:information信息,msg:message 消息,sig:signal信号,
dev:device设备,exec:execute执行,arg:argument实参,param:parameter参数(形参),ins:instance实例,var:variable变量,ret:return返回,temp或tmp:temporary临时,src:source源头,ans:answer回应,cur:current当前的,pre:previous之前的,stat:status状态,val:value值,ptr:pointer指针,col:column列,row:row行,horizontal水平的,vertical垂直的,dec:decrease减少,inc:increase增加,avg:average平均,cntr:container容器,
lnk:link链接,arr:array数组合集,div:division除法,mul:multiplication乘法,sub:subtraction减法或者附属的,

串口应用:

串口在 Linux 系统中是一种终端设备,并且在我们的开发板上,其设备节点为/dev/ttyGS0(UART1),实串口的应用编程也很简单,无非就是通过 ioctl()对串口进行配置,调用 read()读取串口的数据、调用 write()向串口写入数据。但是一般不需要直接调用,linux为上层用户做了一层封
装,将这些 ioctl()操作封装成了一套标准的 API,称为 termios API,这些 API 其实是 C 库函数。这一套接口并不是针对串口开发的,而是针对所有的终端设备,串口是一种终端设备,计算机系统本地连接的鼠标、键盘也是终端设备,通过 ssh 远程登录连接的伪终端也是终端设备,需要在我们的应用程序中包含 termios.h 头文件。
termios:
对于终端来说,其应用编程内容无非包括两个方面的内容:配置和读写;其中struct termios 结构体描述了终端的配置信息,这些参数能够控制、影响终端的行为、特性。
该结构体下面的几个常用的成员,输入模式:c_iflag控制输入数据(终端驱动程序从串口或键盘接收到的字符数据)在被传递给应用程序之前的处理方式(如对接收到的数据执行奇偶校验,当检测到输入终止条件时发送 SIGINT 信号)。
输出模式:c_oflag:输出模式控制输出字符的处理方式,即由应用程序发送出去的字符数据在传递到串口或屏幕之前是如何处理的(如将输出字符中的大写字符转换成小写字符,不输出回车符)。
控制模式:c_cflag:控制模式控制终端设备的硬件特性,譬如对于串口来说,该字段比较重要,可设置串口波特率、数据位、校验位、停止位等硬件特性(B115200 115200 波特率,CS8 8 个数据位,PARENB 使能奇偶校验,默认是一个停止位)。
本地模式:c_lflag:本地模式用于控制终端的本地数据处理和工作模式。如若收到信号字符(INTR、QUIT 等),则会产生相应的信号。
ps:这些成员变量赋值的问题:对于这些变量尽量不要直接对其初始化,而要将其通过“按位与”、“按位或”等操作添加标志或清除某个标志。
不能这样赋值:

struct termios ter;
ter.c_iflag = IGNBRK | BRKINT | PARMRK;

而是通过位操作赋值:

ter.c_iflag |= (IGNBRK | BRKINT | PARMRK | ISTRIP);

终端有三种工作模式,分别为规范模式(canonical mode)、非规范模式(non-canonical mode)和原始模式(raw mode)。在规范模式下,所有的输入是基于行进行处理的。在用户输入一个行结束符(回车符、EOF 等)之前,系统调用 read()函数是读不到用户输入的任何字符的。在规范模式中,行编辑是可行的,而且一次 read()调用最多只能读取一行数据。如果在 read()函数中被请求读取的数据字节数小于当前行可读取的字节数,则 read()函数只会读取被请求的字节数,剩下的字节下次再被读取。
在非规范模式下,所有的输入是即时有效的,不需要用户另外输入行结束符,而且不可进行行编辑。
原始模式是一种特殊的非规范模式。在原始模式下,所有的输入数据以字节为单位被处理。串口就是使用的原始模式意味着通过串口传输的数据不应进行任何特殊处理、不应将其解析成 ASCII 字符。
输入设备input:(event事件类结构)
如鼠标键盘遥控器触摸屏,用户通过输入设备与系统进行交互。基于 input 子系统注册成功的输入设备,都会在/dev/input 目录下生成对应的设备节点(设备文件),设备节点名称通常为 eventX(X 表示一个数字编号 0、1、2、3 等),譬如/dev/input/event0。
读取设备流程,因为这种设备有一个特点,就是需要待机,等到有信号才响应。流程如下:①、应用程序打开/dev/input/event0 设备文件;
②、应用程序发起读操作(调用 read),如果没有数据可读则会进入休眠(阻塞 I/O 情况下);
③、当有数据可读时,应用程序会被唤醒,读操作获取到数据返回;
④、应用程序对读取到的数据进行解析。
其中在读取设备得到的是一个struct input_event 结构体类型数据,其中有四个成员,time 成员变量是内核记录事件其发生的时间,type成员用于描述发生了哪一种类型的事件(对事件的分类,如按键类事件,相对位移事件(如鼠标,记录位置),绝对位移事件(如触摸屏))。code 表示该类事件中的哪一个具体事件,通过定义宏来确定具体事件如#define KEY_1 2 //数字 1 键。value:内核每次上报事件都会向应用层发送一个数据 value,对 value 值的解释随着 code 的变化而变化。
触摸屏的tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库,tslib 从触摸屏中获得原始的坐标数据,并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将原始的触摸屏坐标转换为相应的屏幕坐标。
v4l2摄像头:
V4L2 是 Video for linux two 的简称,是 Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范,使用 V4L2 设备驱动框架注册的设备会在 Linux 系统/dev/目录下生成对应的设备节点文件,设备节点的名称通常为 videoX。在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值