|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在 linux 驱动中字符设备驱动是必须掌握的,本章主要介绍字符设备应用的程序,无论是学习了后面的知识自己写的字符驱动,还是已有的字符驱动,都需要能够写一些简单的应用程序。即使从事 Linux 驱动方面的工作,Linux 驱动写出来之后,也需要由驱动程序员编写简单的应用程序来进行测试的。
& f. v1 ~+ Z3 w, r/ Y3 U2 g1 F另外,关于驱动部分,迅为电子有专门的驱动实验教程提供给大家学习,大家有了这些基础之后再去学习底层的知识就会很容易了。
: M# m2 i! X2 ^6 m; s- E在本手册的 10.22 章节,大家可以看到这些 C 程序也是可以在 Android 下面运行的,只不过没有图形界面。
8 w% l3 K7 ]; M. ?本章配套视频为:
- u) v* W( P3 E- n# A7 W* R/ [ E0 M1 _“视频 06_01 字符设备控制之 main 函数传参数”6 ~7 Z: u$ c: ?, V6 o( x: j3 j
“视频 06_02 字符设备控制之 led 灯”. u& y- Q$ P, e$ a& i/ t
“视频 06_03 字符设备控制之 buzzer 蜂鸣器”
4 E" D K, M6 w2 x, B7 H“视频 06_04 字符设备控制之 ADC 模数转换”
; g2 E; h E) w [0 p5 J9 B3 j0 S$ C17.1 入口 main 函数的参数% R& Y5 I! [4 [% w
在和用户交流的过程中,虽然所有人都学习过 C 语言,但是对 C 语言中的 main 函数的用法并不是很清楚。由于后面的实验需要用到这部分知识,这里就占用一个小节,先简单介绍一下 main 函数。$ Y* h( T" m2 L. B7 B2 e
main 函数简介
) P( j3 X% ?" ymain 函数作为应用程序的入口,在头文件“#include ”中。main 函数需要传参数的时候完整的定义为+ Q! o7 I" @) Y2 q1 W
int main(int argc,char **argv)
5 g! r- ]8 e5 T参数 argc,表示参数的个数5 I+ M _+ _: j/ d0 l
参数**argv,存储输入字符的数组,argv[0]表示程序名称,argv[1]——argv[n]输入的参数7 P0 e3 v9 m5 o/ W# g
不传参数的时候定义为
: H" p6 q+ C5 gint main(void)
+ t. N* @% r( i" o$ v1 Y' h- r& {函数 main 的返回值为类型为 int,用于判断程序执行成功还是失败2 L" G! T" d0 i# _& |" s" N
main 函数例程. B( H1 h" V2 g# `* O% K" J0 i7 Q' C
编写简单的 argvc.c 文件测试 main 函数。
: p/ S5 [# I* J* X7 @$ `/ Z % ~9 C: u D: q9 `& g2 ^! c) i5 e% k
如上图所示,将输入的参数第一个和第二个转换成 int 类型,赋值给 i 和 j,最后输出打印。
- n+ {6 P4 K' L( e其中 argv[0]为程序名称,这里就是后面要编译的目标文件“argvc”。
) u" T4 Y8 v0 X, W; T! J
7 j, w+ w; v/ o: K. D0 g# N% h! r编译运行测试
: a' w, l8 A# {/ h5 K; X8 l在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录“/home/linuxsystEMCode/”,使用命令“mkdircharcontrol”新建 charcontrol 文件夹,将源码 argvc.c 拷贝进去,进入新建的文件夹 charcontrol,如下图所示。
3 N4 \( E& s. e( d( R 8 i$ h: R5 Z1 X# L& N
使用命令“ARM-none-linux-gnueabi-gcc -o argvc argvc.c -static”编译 argvc 文件, 如下图所示,使用命令“ls”可以看到生成了 argvc 可执行文件。9 S$ [" Q7 x& X8 j% N* K
![]()
) n, U, E- m/ r4 E& h! k0 }这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考 10.3.5 小节。 将编译成的可执行文件 argvc,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。
3 q2 M; A) ?; B; d9 M2 h![]()
2 N6 I' L8 R" [* J( S如上图所示,程序成功运行,打印:
6 ~" _: d0 c- l) L" Bthe Program name is ./mnt/udisk/argvc。% V& H2 Z7 ], e. x+ b5 V
因为运行的程序就是“./mnt/udisk/argvc”,这是第一个参数: n- ?$ q8 x; L# C1 q
The command line has 2 argument:
. D* Q# m2 u0 f10,11。
- a: k! S9 Z, J2 H; T' W N' _9 ?7 z5 S5 C6 H1 B, J1 ?
输入的参数是 10 和 11,对应 argv[2]和 argv[2]。
" `' d2 ?4 Z3 r17.2 字符类 led 灯0 g; P8 ^6 Q% z
在前面介绍 open 函数的时候,已经提到过如何打开字符类设备,获得句柄的方法和一般文件都是一样。
& w2 v5 M7 R' w' N& ]+ ~Led 灯的设备节点在/dev 目录下,如下图所示,在超级终端可以使用 ls 命令查找。
7 L9 F% f( o/ j: X![]()
: C3 i# r a0 q7 i3 D4 b$ Z( I, H由于涉及到硬件知识,这里简单介绍一下硬件原理,如下图所示,led 小灯的硬件原理很简单。4 J9 f0 B9 E E3 |2 [
" i1 u; O5 k9 p* P. i
如上图所示,给 KP_COL0 和 VDD50_EN 网络高电平,三极管 L9014 就会导通,电源 VSYS 就会将电压加到电阻 R 和 led 小灯上,小灯就会亮。
5 x' m2 m" X/ N3 E给 KP_COL0 和 VDD50_EN 网络低电平,三极管 L9014 就会截止,形成断路,小灯灭。, G; O! b+ g' S' N. X) |. q
在前面介绍过,如果要给文件进行写操作,那么使用的是 write 函数。对于 led 小灯的操作,使用写函数,理论上也是可以的。但是对于 IO 口(这里的 IO 口指的是硬件上的 IO 口, 不是指 IO 文件)的$ M5 b. R7 y* |$ w
. j0 o: ~ h5 d! H% {操作,Linux 专门设计了一个高效的函数 ioctl。% j6 R' n4 E9 Z1 b, \6 D, Z
这个函数在头文件#include中。" n4 \1 Q2 V5 c, k9 U7 y/ R
int ioctl( int fd, int request, int cmd);# r: m" y ]) r* Y" v H( G8 ]
参数 fd,函数 open 返回的句柄。8 Q4 Y! f3 Y0 h x, b& L/ c5 Z
参数 request 和参数 cmd,由内核驱动决定具体操作,例如 request 可以代表那个 IO 口,cmd 代表对 IO 进行什么样的操作,也可以反过来。具体的含义由驱动工程师在驱动中 switch 决定。1 t+ I% y- r- ?$ m8 f3 S" \
返回值:返回 0 成功;返回-1,出错。
" u2 g4 b+ Q; z& |( }) G: Z6 ?* i! F$ n* W# @/ S) J
小灯测试例程. `- F, ]1 g/ B( K7 E7 Q; g
编写简单的 leds.c 文件测试小灯。首先添加头文件,如下图所示。# j3 D& q, X8 l6 L, S2 d
通过 main 参数传过来的参数是 char 字符格式的,如果要传递给 ioctl 函数,需要用到数值转化函数atoi,添加了头文件#include 。
. l& M* I: p4 N& ~% a9 Z接着由于小灯的数量和命令都是 2,所以对小灯数量和操作数进行宏定义
4 C- e, e2 g' t6 k4 R0 s#define LED_NUM 2& o" {4 S4 J; x8 E0 A7 E, |/ y
#define LED_C 2。
1 s4 o7 M+ d, {![]()
: @# u6 `' [1 U5 e' i( R6 n5 @然后 main 函数如下图所示。& B9 g9 b+ p+ N
![]()
8 E) K5 Q) } ^) T4 |9 S- ^如上图所示。
7 Q7 y2 j* Z- r& l第 33 行,调用了 ioctl 函数,将 main 函数的第一个和第二个参数传入驱动。
* x3 g9 F. F) [) e5 k( f第 19 行,解释那个参数具体代表什么含义,"argv1 is cmd;argv2 is io”,参数 1 对应命令,参数 2 对应具体那个 led 灯。
. F* ~ J7 z7 H- b! q$ @& u5 M第 36 行,将打开的设备节点"/dev/leds"关闭。
; N5 f# |2 m- i/ P- M( Y% f
) X5 `8 ^. z3 _编译运行测试
6 V- p9 W8 W; S; _% n$ f4 z6 h* u在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录“/home/linuxsystemcode/charcontrol”,将源码 leds.c 拷贝进去,如下图所示。4 L1 G, ]" y) _+ h% t* d/ H6 r
0 U! b6 r u0 y; ~5 H" x
使用命令“arm-none-linux-gnueabi-gcc -o leds leds.c -static”编译 leds 文件,如下图所示,使用命令“ls”可以看到生成了 leds 可执行文件。
; o/ v8 ^# \( [- h% ~ / I5 `9 N$ [% i H
这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考实验 026 d! T6 P& \% }) d8 p( Q1 o- y
将编译成的可执行文件 open,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。5 x* n! d, n8 p5 A, Z8 F& h6 P: s
如下图所示,如果不加参数会有提示,然后报错。
1 B0 M, X# O2 L" U+ m8 C3 Z![]()
2 c& P$ V* R& n: h如下图所示,使用命令“./mnt/udisk/leds 0 0”运行,可以看到靠近蜂鸣器的小灯灭了。
6 Y7 s+ P6 ]* z6 L9 w$ b0 M/ Z![]()
9 C) J% E6 G6 y- T4 S+ m所有参数对小灯的控制如下:& h: p4 B/ p Y; f6 d5 v9 s1 r( T0 c
0 0 靠近蜂鸣器的小灯灭;
3 ~, e; U, T, a: A1 l, J4 G1 O0 1 靠近按键的小灯灭;" L- z* q, d! a' ~2 m9 B7 Q! s
1 0 靠近蜂鸣器的小灯亮;6 ^; Y1 D6 n6 L, G& T- v
1 1 靠近按键的小灯亮。用户可以自行测试一下。
. `5 M6 s' V1 S4 q' O 0 n8 Z) L6 i) t% j, s6 b
|
|