|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在 linux 驱动中字符设备驱动是必须掌握的,本章主要介绍字符设备应用的程序,无论是学习了后面的知识自己写的字符驱动,还是已有的字符驱动,都需要能够写一些简单的应用程序。即使从事 Linux 驱动方面的工作,Linux 驱动写出来之后,也需要由驱动程序员编写简单的应用程序来进行测试的。- F# ?. h$ {& |7 \0 o6 C
另外,关于驱动部分,迅为电子有专门的驱动实验教程提供给大家学习,大家有了这些基础之后再去学习底层的知识就会很容易了。
2 t4 k' X$ H0 Z6 c8 n在本手册的 10.22 章节,大家可以看到这些 C 程序也是可以在 Android 下面运行的,只不过没有图形界面。
0 B# Q: F l6 ~1 x ~本章配套视频为:. @0 A4 @# S* y+ o0 N0 U
“视频 06_01 字符设备控制之 main 函数传参数”: v3 L, |, s) p7 V
“视频 06_02 字符设备控制之 led 灯”9 x! k: y" y$ i; N5 k' d" r9 _
“视频 06_03 字符设备控制之 buzzer 蜂鸣器”
" m& J2 U' A8 a! n“视频 06_04 字符设备控制之 ADC 模数转换”
# \2 X* w B9 y17.1 入口 main 函数的参数- t2 q! _) X* Z$ F3 W( n: \) C) C5 n0 |
在和用户交流的过程中,虽然所有人都学习过 C 语言,但是对 C 语言中的 main 函数的用法并不是很清楚。由于后面的实验需要用到这部分知识,这里就占用一个小节,先简单介绍一下 main 函数。% }( F( G& Z3 y/ M) ^6 o
main 函数简介
+ R% I* `) q& H$ I# Z5 }main 函数作为应用程序的入口,在头文件“#include ”中。main 函数需要传参数的时候完整的定义为
* a$ K/ d1 I$ K1 D3 cint main(int argc,char **argv)
0 c6 X' Y% f5 `8 M+ l/ _参数 argc,表示参数的个数
1 {- I/ r. H. l参数**argv,存储输入字符的数组,argv[0]表示程序名称,argv[1]——argv[n]输入的参数
9 H9 x: e9 f, F( }+ R$ a+ O0 Q不传参数的时候定义为8 w6 Z1 f; V: X% C `0 `
int main(void)! s; U9 B" N0 A5 F
函数 main 的返回值为类型为 int,用于判断程序执行成功还是失败
. a1 a o% s5 t- o7 vmain 函数例程
' p- p# q. m( G" l编写简单的 argvc.c 文件测试 main 函数。
; e+ S! n7 d6 J$ K3 b# A . | ^2 U1 i F9 c' v
如上图所示,将输入的参数第一个和第二个转换成 int 类型,赋值给 i 和 j,最后输出打印。
c6 O! U: J% o" N) d( z9 U其中 argv[0]为程序名称,这里就是后面要编译的目标文件“argvc”。
' l* e5 O, j" T" Z9 ~" Q# m* T0 U
/ h* o# v3 U" G/ @9 c" g5 h* Z编译运行测试
4 f0 P" v8 F5 }" N% i4 T7 z在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录“/home/linuxsystEMCode/”,使用命令“mkdircharcontrol”新建 charcontrol 文件夹,将源码 argvc.c 拷贝进去,进入新建的文件夹 charcontrol,如下图所示。
. v* z8 I$ Y2 G( k% N$ ? - H, ?0 }1 w8 r' s
使用命令“ARM-none-linux-gnueabi-gcc -o argvc argvc.c -static”编译 argvc 文件, 如下图所示,使用命令“ls”可以看到生成了 argvc 可执行文件。
3 j$ W5 ~* M2 z- f+ n* n0 n![]()
- m5 T: E+ s4 F3 f. Q3 y& j5 X这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考 10.3.5 小节。 将编译成的可执行文件 argvc,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。
) ^) G! T, x3 Y0 \![]()
9 }" C/ A" Z! V+ O如上图所示,程序成功运行,打印:
1 A$ F# ]: t& u( c) N0 i( p. Tthe Program name is ./mnt/udisk/argvc。7 A6 z8 |3 t3 \
因为运行的程序就是“./mnt/udisk/argvc”,这是第一个参数
2 O& D5 K$ R$ [: q. Q4 ?/ ZThe command line has 2 argument:
- g# ?1 C. |5 V0 m4 Y/ A }10,11。- u4 Q7 \% v: p3 R
- L# y+ l7 l _" s0 O0 B
输入的参数是 10 和 11,对应 argv[2]和 argv[2]。
. M8 `: P5 G" G" W' m" @* l17.2 字符类 led 灯
. W0 f% W& U8 p! P! N在前面介绍 open 函数的时候,已经提到过如何打开字符类设备,获得句柄的方法和一般文件都是一样。7 P' L3 f6 Q# {, O# S
Led 灯的设备节点在/dev 目录下,如下图所示,在超级终端可以使用 ls 命令查找。
; |1 I/ n. ?9 r; } m& ` . y6 v. n' e- H- w
由于涉及到硬件知识,这里简单介绍一下硬件原理,如下图所示,led 小灯的硬件原理很简单。
* i! h) R: a$ [5 `/ @$ R! c$ } $ V5 v/ ^- @ P& {
如上图所示,给 KP_COL0 和 VDD50_EN 网络高电平,三极管 L9014 就会导通,电源 VSYS 就会将电压加到电阻 R 和 led 小灯上,小灯就会亮。
, M5 H Z" i9 ^/ a* F v: o: g' C给 KP_COL0 和 VDD50_EN 网络低电平,三极管 L9014 就会截止,形成断路,小灯灭。 E/ T* H/ E# V% S2 K
在前面介绍过,如果要给文件进行写操作,那么使用的是 write 函数。对于 led 小灯的操作,使用写函数,理论上也是可以的。但是对于 IO 口(这里的 IO 口指的是硬件上的 IO 口, 不是指 IO 文件)的- Q" m: m" V: C/ b N w- i$ J
( R* _5 N+ V: W9 k
操作,Linux 专门设计了一个高效的函数 ioctl。! G. N3 D" Y7 @2 k
这个函数在头文件#include中。2 |9 ^6 V4 N0 c6 \
int ioctl( int fd, int request, int cmd);
1 R* V8 I. U% D4 ^2 t参数 fd,函数 open 返回的句柄。
1 Z( k, H5 S% [: ]: t! r参数 request 和参数 cmd,由内核驱动决定具体操作,例如 request 可以代表那个 IO 口,cmd 代表对 IO 进行什么样的操作,也可以反过来。具体的含义由驱动工程师在驱动中 switch 决定。3 F/ u/ [4 l! D5 W% F
返回值:返回 0 成功;返回-1,出错。8 R$ r8 S' A2 m+ G- z% g
+ \: G. J* L% {# ?; i% k: W小灯测试例程
: F. ]- w4 B/ k( H" S% z" Y& g编写简单的 leds.c 文件测试小灯。首先添加头文件,如下图所示。
- u! A% ?) E h; D: |2 b1 {通过 main 参数传过来的参数是 char 字符格式的,如果要传递给 ioctl 函数,需要用到数值转化函数atoi,添加了头文件#include 。
e" v$ i5 K+ r) R: R$ m$ ]# l+ M! T2 _接着由于小灯的数量和命令都是 2,所以对小灯数量和操作数进行宏定义* }; |! C2 W9 f* k
#define LED_NUM 2
0 K- X) x$ H6 q9 ?" ~#define LED_C 2。9 R% T" g/ q. Q: Q5 \, A7 G- g ]7 j
![]()
3 w+ H+ q. z2 O6 M8 h然后 main 函数如下图所示。6 C) Y$ N) p2 w7 Y
![]()
1 o9 m5 O6 ]& i% T9 n: L如上图所示。
+ H1 X' y2 n# m. {: i: A6 |! y第 33 行,调用了 ioctl 函数,将 main 函数的第一个和第二个参数传入驱动。
" ^) ~" q3 k( q& w) U# B第 19 行,解释那个参数具体代表什么含义,"argv1 is cmd;argv2 is io”,参数 1 对应命令,参数 2 对应具体那个 led 灯。
+ e7 O {" p1 o+ [# @% N第 36 行,将打开的设备节点"/dev/leds"关闭。
: P) q6 Z4 Z/ j, P. J# T. P t) e
8 _! ^1 @/ u$ z a& M- F$ `编译运行测试8 |( Y' M, f3 e+ t' P1 W t
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录“/home/linuxsystemcode/charcontrol”,将源码 leds.c 拷贝进去,如下图所示。
' M2 }9 m7 h" Z( C9 p![]()
: |/ g/ D( f6 y使用命令“arm-none-linux-gnueabi-gcc -o leds leds.c -static”编译 leds 文件,如下图所示,使用命令“ls”可以看到生成了 leds 可执行文件。
* b5 Y) t" c6 b6 O, x 9 y# p4 r4 r! W& i$ h+ r" e8 [( A
这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考实验 02" O. h6 U- N7 k6 G6 }6 X7 x2 V) m
将编译成的可执行文件 open,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。
, p% y# u/ [" K如下图所示,如果不加参数会有提示,然后报错。2 C/ A5 c* Z$ H- I3 }
![]()
9 w) @, a3 ^6 V0 x0 w' ^如下图所示,使用命令“./mnt/udisk/leds 0 0”运行,可以看到靠近蜂鸣器的小灯灭了。
$ K3 _. f6 W5 `# A! Z# b % s9 K% u6 Q( r7 }0 }
所有参数对小灯的控制如下:
8 @2 A4 a5 N. C/ v' H0 0 靠近蜂鸣器的小灯灭;6 H' C4 ^" T/ {3 c# T; I2 u
0 1 靠近按键的小灯灭;. ~4 ^! }7 n2 N
1 0 靠近蜂鸣器的小灯亮;# I0 |6 Z- f; c9 ^, l( j5 s4 K1 I
1 1 靠近按键的小灯亮。用户可以自行测试一下。/ d4 l0 p4 \$ J* H, e
![]()
0 s) c0 x' A, I: L T |
|