iTOP-4412全能版采用四核Cortex-A9,主频为1.4GHz-1.6GHz,配备S5M8767 电源管理,集成USB HUB,选用高品质板对板连接器稳定可靠,大厂生产,做工精良。接口一应俱全,开发更简单,搭载全网通4G、支持WIFI、蓝牙、陀螺仪、CAN总线、RS485总线、500万摄像头等模块,稳定运行Android 4.0.3/Android 4.4操作,系统通用Linux-3.0.15+Qt操作系统(QT支持5.7版本),Ubuntu版本:12.04,接口智能分配 方便好用。
第十七章 Linux字符设备控制
在 linux 驱动中字符设备驱动是必须掌握的,本章主要介绍字符设备应用的程序,无论是学习了后面的知识自己写的字符驱动,还是已有的字符驱动,都需要能够写一些简单的应用程序。
即使从事 Linux 驱动方面的工作,Linux 驱动写出来之后,也需要由驱动程序员编写简单的应用程序来进行测试的。
另外,关于驱动部分,迅为电子有专门的驱动实验教程提供给大家学习,大家有了这些基础之后再去学习底层的知识就会很容易了。
在本手册的 10.22 章节,大家可以看到这些 C 程序也是可以在 Android 下面运行的,只不过没有图形界面。
本章配套视频为:
"视频 06_01 字符设备控制之 main 函数传参数"
"视频 06_02 字符设备控制之 led 灯"
"视频 06_03 字符设备控制之 buzzer 蜂鸣器"
"视频 06_04 字符设备控制之 ADC 模数转换"
17.1 入口main函数的参数
在和用户交流的过程中,虽然所有人都学习过 C 语言,但是对 C 语言中的 main 函数的用法并不是很清楚。
由于后面的实验需要用到这部分知识,这里就占用一个小节,先简单介绍一下 main 函数。
main 函数简介
main 函数作为应用程序的入口,在头文件"#include<stdio.h> "中。main 函数需要传参数的时候完整的定义为
int main(int argc,char **argv)
参数 argc,表示参数的个数
参数**argv,存储输入字符的数组,argv[0]表示程序名称,argv[1]------argv[n]输入的参数
不传参数的时候定义为
int main(void)
函数 main 的返回值为类型为 int,用于判断程序执行成功还是失败
main 函数例程
编写简单的 argvc.c 文件测试 main 函数。
如上图所示,将输入的参数第一个和第二个转换成 int 类型,赋值给 i 和 j,最后输出打印。
其中 argv[0]为程序名称,这里就是后面要编译的目标文件"argvc"。
编译运行测试
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录"/home/linuxsystemcode/",使用命令"mkdir charcontrol"新建 charcontrol 文件夹,将源码 argvc.c 拷贝进去,进入新建的文件夹 charcontrol,如下图所示。
使用命令"arm-none-linux-gnueabi-gcc -o argvc argvc.c -static"编译 argvc 文件, 如下图所示,使用命令"ls"可以看到生成了 argvc 可执行文件。
这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考 10.3.5 小节。 将编译成的可执行文件 argvc,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。
如上图所示,程序成功运行,打印:
the Program name is ./mnt/udisk/argvc。
因为运行的程序就是"./mnt/udisk/argvc",这是第一个参数
The command line has 2 argument:
10,11。
输入的参数是 10 和 11,对应 argv[2]和 argv[2]。
17 . 2 字符类led灯
在前面介绍 open 函数的时候,已经提到过如何打开字符类设备,获得句柄的方法和一般文件都是一样。
Led 灯的设备节点在/dev 目录下,如下图所示,在超级终端可以使用 ls 命令查找。
由于涉及到硬件知识,这里简单介绍一下硬件原理,如下图所示,led 小灯的硬件原理很简单。
如上图所示,给 KP_COL0 和 VDD50_EN 网络高电平,三极管 L9014 就会导通,电源VSYS 就会将电压加到电阻 R 和 led 小灯上,小灯就会亮。
给 KP_COL0 和 VDD50_EN 网络低电平,三极管 L9014 就会截止,形成断路,小灯灭。
在前面介绍过,如果要给文件进行写操作,那么使用的是 write 函数。对于 led 小灯的操作,使用写函数,理论上也是可以的。但是对于 IO 口(这里的 IO 口指的是硬件上的 IO 口, 不是指 IO 文件)的操作,Linux 专门设计了一个高效的函数 ioctl。
这个函数在头文件#include<unistd.h>中。
int ioctl( int fd, int request, int cmd);
参数 fd,函数 open 返回的句柄。
参数 request 和参数 cmd,由内核驱动决定具体操作,例如 request 可以代表那个 IO 口,cmd 代表对 IO 进行什么样的操作,也可以反过来。具体的含义由驱动工程师在驱动中switch 决定。
返回值:返回 0 成功;返回-1,出错。
小灯测试例程
编写简单的 leds.c 文件测试小灯。首先添加头文件,如下图所示。
通过 main 参数传过来的参数是 char 字符格式的,如果要传递给 ioctl 函数,需要用到数值转化函数 atoi,添加了头文件#include <stdlib.h>。
接着由于小灯的数量和命令都是 2,所以对小灯数量和操作数进行宏定义
#define LED_NUM 2
#define LED_C 2。
然后 main 函数如下图所示。
如上图所示。
第 33 行,调用了 ioctl 函数,将 main 函数的第一个和第二个参数传入驱动。
第 19 行,解释那个参数具体代表什么含义,"argv1 is cmd;argv2 is io",参数 1 对应命令,参数 2 对应具体那个 led 灯。
第 36 行,将打开的设备节点"/dev/leds"关闭。
编译运行测试
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录"/home/linuxsystemcode/charcontrol",将源码 leds.c 拷贝进去,如下图所示。
使用命令"arm-none-linux-gnueabi-gcc -o leds leds.c -static"编译 leds 文件,如下图所示,使用命令"ls"可以看到生成了 leds 可执行文件。
这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考实验 02
将编译成的可执行文件 open,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。
如下图所示,如果不加参数会有提示,然后报错。
如下图所示,使用命令"./mnt/udisk/leds 0 0"运行,可以看到靠近蜂鸣器的小灯灭了。
所有参数对小灯的控制如下:
0 0 靠近蜂鸣器的小灯灭;
0 1 靠近按键的小灯灭;
1 0 靠近蜂鸣器的小灯亮;
1 1 靠近按键的小灯亮。用户可以自行测试一下。
17.3 字符类Buzzer蜂鸣器
和 led 灯类似,蜂鸣器的设备节点也是在/dev 目录下,如下图所示。
蜂鸣器的硬件和 led 灯类似,如下图所示。
如上图所示。
原理图很容易理解,如果网络 MOTOR_PWM 为高电平,则 L9014 导通,蜂鸣器响,如果网络 MOTOR_PWM 为低电平,则 L9014 截止,蜂鸣器则不响。
操作方式和 led 小灯类似。
蜂鸣器测试例程
编写简单的 buzzertest.c 文件测试蜂鸣器。
首先添加头文件,如下图所示,下面新加了几个库文件,一般常用的就是下面几个,写代码的时候,为了方便,可以直接都添加上。
然后 main 函数如下图所示。
如上图代码所示。由于只有一个 IO,底层没有做第三个参数的判断,所以无效
第 16-19 行,对参数 argv[1]有个简单的判断,命令只能是 0 或者 1。
第 21-24 行,open 函数打开蜂鸣器设备节点
第 26 行,使用 ioctl 函数操作蜂鸣器。
第 27 行,使用 close 函数关闭设备节点。
编译运行测试
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录"/home/linuxsystemcode/charcontrol",将源码 buzzertest.c 拷贝进去,如下图所示。
使用命令"arm-none-linux-gnueabi-gcc -o buzzertest buzzertest.c -static"编译buzzertest 文件,如下图所示,使用命令"ls"可以看到生成了 buzzertest 可执行文件。
这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统。
将编译成的可执行文件 buzzertest,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘, 运行程序如下。
使用参数 1 和 0,蜂鸣器会响。第二个参数实际上并不起作用。
如下图所示,使用参数 0 和 0,蜂鸣器会停止响。
17 . 4 字符类ADC模数转换
和 led 灯类似,数模转换的设备节点也是在/dev 目录下,如下图所示。
模数转换的硬件部分如下图所示。
如上图所示。
XadcAIN0 网络可以读取到当前输入电压,滑动变阻器 R 移动的时候,1 和 2 之间的电阻R12 改变,滑动变阻器最大电阻为 R13,然后电压 Vadc=R12*VDD1V8_EXT/R13
上面公式中 Vadc 可以通过 4412 读取出来,VDD1V8 和 R13 已知,那么就很容易求出R12 的电阻。如下图所示,在 4412datasheet 中 ADC 章节中有真实的电阻和电压曲线图。
这里将数值做一个简单的换算,
1.8V 对应的是 10K 欧姆,对应的寄存器数值为 0xfff;
0V 对应的是 0 欧姆,对应的寄存器数值为 0x0。
这样做一个简单公式,将读取的数值 r 转化为电阻值 R。
R = r*10000/0xfff,即 R = r*10000/4095。
这个小公式在后面的代码中将会使用到。
模数转换例程
编写简单的 ADC.c 文件测试 adc 的驱动。首先添加头文件,如下图所示。
然后 main 函数如下图所示。
如上图代码所示。
第 14 行,设备节点为 char *adc = "/dev/adc"。
第 21 行,打开设备节点文件。
第 26 行,使用 read 函数,将读取数字赋予 buffer。
第 30 和 31 行,做个简单地换算,将读取的数值转化为电阻值。
编译运行测试
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录"/home/linuxsystemcode/charcontrol/"将源码 ADC.c 拷贝进去,如下图所示。
使用命令"arm-none-linux-gnueabi-gcc -o ADC ADC.c -static"编译 ADC 文件,如下图所示,使用命令"ls"可以看到生成了 ADC 可执行文件。
这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考 10.3.5 小节。将编译成的可执行文件 open,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序。
如下图所示,使用命令"./mnt/udisk/ADC"即可检测当前电阻值,中间的大段打印参数是多次打印寄存器的数值,在驱动实验中再去介绍。
调整滑动电阻器之后,再次使用测试程序,如下图所示,输出数值会有变化。
滑动变阻器向顺时针旋转,阻值会减小,最小为 0;
滑动变阻器向逆时针旋转,阻值会增大,最大为 10K。