一、应用层操控设备的两种方式
应用层如何操控底层硬件,同样也是通过文件 I/O 的方式来实现,设备文件便是各种硬件设备向应用层提供的一个接口,应用层通过对设备文件的 I/O 操作来操控硬件设备,譬如 LCD 显示屏、串口、按键、摄像头等等,所以设备文件其实是与硬件设备相互对应的。设备文件通常在/dev/目录下,我们也把/dev 目录下的文件称为设备节点。
设备节点并不是操控硬件设备的唯一途径,除此之外,我们还可以通过 sysfs 文件系统对硬件设备进行操控。
1、sysfs 文件系统
sysfs 是一个基于内存的文件系统,同 devfs、proc 文件系统一样,称为虚拟文件系统;它的作用是将内核信息以文件的方式提供给应用层使用。
2、sysfs 与/sys
sysfs 文件系统挂载在/sys 目录下,启动 ALPHA 开发板,进入 Linux 系统(开发板出厂系统)之后,我们进入到/sys 目录下查看,如下所示
上图显示的便是 sysfs 文件系统中的目录,包括 block、bus、class、dev、devices、firmware、fs、kernel、modules、power 等
系统中所有的设备(对象)都会在/sys/devices 体现出来,是 sysfs 文件系统中最重要的目录结构;而
/sys/bus、/sys/class、/sys/dev 分别将设备按照挂载的总线类型、功能分类以及设备号的形式将设备组织存放在这些目录中,这些目录下的文件都是链接到了/sys/devices 中。
设备的一些属性、数据通常会通过设备目录下的文件体现出来,也就是说设备的数据、属性会导出到用户空间,以文件形式为用户空间提供对这些数据、属性的访问支持,可以把这些文件称为属性文件;读这些属性文件就表示读取设备的属性信息,相反写属性文件就表示对设备的属性进行设置、以控制设备的状态。
3、总结
这里给大家进行一个总结,应用层想要对底层硬件进行操控,通常可以通过两种方式:
- /dev/目录下的设备文件(设备节点);
- /sys/目录下设备的属性文件。
具体使用哪种方式需要根据不同功能类型设备进行选择,有些设备只能通过设备节点进行操控,而有些设备只能通过 sysfs 方式进行操控;当然跟设备驱动具体的实现方式有关,通常情况下,一般简单地设备会使用 sysfs 方式操控,其设备驱动在实现时会将设备的一些属性导出到用户空间 sysfs 文件系统,以属性文件的形式为用户空间提供对这些数据、属性的访问支持,譬如 LED、GPIO 等。但对于一些较复杂的设备通常会使用设备节点的方式,譬如 LCD 等、触摸屏、摄像头等。
二、LED 硬件控制方式
对于 ALPHA开发板出厂系统来说,此 LED 设备使用的是 Linux 内核标准 LED 驱动框架注册而成,在/dev 目录下并没有其对应的设备节点,其实现使用 sysfs 方式控制。进入到/sys/class/leds 目录下,如下所示:
上小节介绍了/sys/class 目录,系统中的所有设备根据其功能分类组织到了/sys/class 目录下,所以/sys/class/leds 目录下便存放了所有的 LED 类设备。从上图可以看到该目录下有一个 sys-led 文件夹,这个便是底板上的用户 LED 设备文件夹。
这里我们主要关注便是 brightness、max_brightness 以及 trigger 三个文件,这三个文件都是 LED 设备的属性文件:
trigger:触发模式,该属性文件可读可写,读表示获取 LED 当前的触发模式,写表示设置 LED 的触发模式。不同的触发模式其触发条件不同,LED 设备会根据不同的触发条件自动控制其亮、灭状态,通过 cat 命令查看该属性文件,可获取 LED 支持的所有触发模式以及 LED 当前被设置的触发模式:
方括号([heartbeat])括起来的表示当前 LED 对应的触发模式,none 表示无触发,常用的触发模式包括none(无触发)、mmc0(当对 mmc0 设备发起读写操作的时候 LED 会闪烁)、timer(LED 会有规律的一亮一灭,被定时器控制住)、heartbeat(心跳呼吸模式,LED 模仿人的心跳呼吸那样亮灭变化)。
通常系统启动之后,会将板子上的一颗 LED 设置为 heartbeat 触发模式,将其作为系统正常运行的指示灯,譬如 ALPHA 开发板系统启动之后,底板上的用户 LED 就会处于心跳呼吸模式。
通过上面的介绍,已经知道如何去控制 ALPHA开发板底板上的用户 LED 了,譬如通过echo 命令进行控制:
echo timer > trigger //将 LED 触发模式设置为 timer
echo none > trigger //将 LED 触发模式设置为 none
echo 1 > brightness //点亮 LED echo 0 > brightness//熄灭 LED
使用 echo 或 cat 命令进行测试、控制 LED 状态;除了使用 echo 或 cat 命令之后,同样我们编写应用程序,使用 write()、read()函数对这些属性文件进行 I/O 操作以达到控制 LED 的效果。
三、编写 LED 应用程序
1、例程源码
#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[])
{
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);
}
程序中定义了两个宏,LED_TRIGGER 和 LED_BRIGHTNESS,分别对应/sys/class/leds/sys-led/trigger 和/sys/class/leds/sys-led/brightness 属性文件;程序首先会调用 open()函数打开这两个属性文件,之后判断传入参数指向相应的动作,传入"on"表示点亮 LED,先调用 write()将"none"
写入到 trigger 属性文件中,也就是设置为无触发,接着再向 brightness 属性文件中写入"1"点亮 LED;传入"off"表示熄灭 LED,同样也是先调用 write()将"none"写入到 trigger 属性文件设置 LED 为无触发,接着再向brightness 属性文件中写入"0"熄灭 LED;传入"trigger"表示设置 LED 的触发模式,则需要传入第二个参数,第二个参数表示需要设置的模式。
2、开发板上测试
启动开发板进入 Linux 系统,将上小节编译得到的可执行文件 testApp 拷贝到开发板根文件系统中,譬如拷贝到开发板 Linux 系统的家目录下,如下图所示:
拷贝方法很多,介绍下 scp 命令:
1、将开发板用网线连接在与虚拟机同一个路由器下,查看下开发板的IP地址。
例如,在 home 目录下有个 test 文件我们要把这个 test 文件传到开发板的/home/root 目录(我们一般传文件都是传文件到家目录(/home/root)的)。指令格式如下
scp 文件 用户名@ip 地址:路径
例:scp testApp root@192.168.5.14:/home/root
指令格式分析:
testApp 要传输的文件
root 为用户名,开发板默认的就是 root 用户,拥有最高权限
@ 一个符号
192.168.5.14 开发板 ip
: 这里要加一个英文字符的“:”,不要忘记了!
/home/root 要传输到开发板的路径
在开发板/home/root 可以看到 testApp 文件已经通过网络传输到开发板了。
2、用WiFi驱动,同样是获取IP地址
首先插上USBWiFi,进入终端
cd /home/root/shell/wifi
ls
使用station上网模式
wpa_cli -i wlan0 scan_result // 此指令需要启动进入后再插 WIFI 才生效,需要 wpa_supplicant 在运行。
source ./alientek_usb_wifi_setup.sh -m station -i ALIENTEK-YF -p 1590202**** -d wlan0
参数解释:
-m station :设置成 station 模式
-i ALIENTEK-YF :无线网络名称(ssid)。
-p 1590202**** :无线网络密码(psk)。
-d wlan0 :USB WIFI 节点
看到下图已经获取到 ip 信息就代表连接无线网络成功,如果没有获取到 ip 信息,请检查密码是否正确或者重试指令。
若未能正常获取 ip,等待 RTL871X: set group key camid:5 这句话出现后输入 udhcpc -i wlan0 指令重新获取 ip。若没有这句话,请检查无线网络信息是否正确。
测试是否能上网,使用 ping 指令 ping 百度,可按 ctrl+c 终止执行指令
ping www.baidu.com -I wlan0
接下来执行 testApp 程序测试:
./testApp on # 点亮 LED
./testApp off # 熄灭 LED
./testApp trigger timer # LED 会有规律的一亮一灭,被定时器控制住
结果与执行的一致。