|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在 linux 驱动中字符设备驱动是必须掌握的,本章主要介绍字符设备应用的程序,无论是学习了后面的知识自己写的字符驱动,还是已有的字符驱动,都需要能够写一些简单的应用程序。即使从事 Linux 驱动方面的工作,Linux 驱动写出来之后,也需要由驱动程序员编写简单的应用程序来进行测试的。; c6 N" z' k9 N# D4 l
另外,关于驱动部分,迅为电子有专门的驱动实验教程提供给大家学习,大家有了这些基础之后再去学习底层的知识就会很容易了。
8 g: r0 t( c) v在本手册的 10.22 章节,大家可以看到这些 C 程序也是可以在 Android 下面运行的,只不过没有图形界面。
6 Q+ a& X0 Q- B. j' X8 u, h本章配套视频为:
: \. ~& d# B# Y: w* F“视频 06_01 字符设备控制之 main 函数传参数”# Y5 A' m5 S* S8 t8 |) C
“视频 06_02 字符设备控制之 led 灯”" {/ r- M' e8 l, d( N0 z& q" V* Q
“视频 06_03 字符设备控制之 buzzer 蜂鸣器”. g+ ~: h' d9 _4 F4 j+ z- I$ _
“视频 06_04 字符设备控制之 ADC 模数转换”
# R: Q2 `$ D- V+ y1 n! i17.1 入口 main 函数的参数
I; B2 V+ T$ Z0 r# y在和用户交流的过程中,虽然所有人都学习过 C 语言,但是对 C 语言中的 main 函数的用法并不是很清楚。由于后面的实验需要用到这部分知识,这里就占用一个小节,先简单介绍一下 main 函数。: k5 h" ]$ Y4 N% O. c7 C2 b
main 函数简介
" k" {; g7 I. r* M2 _9 @% I5 L+ omain 函数作为应用程序的入口,在头文件“#include ”中。main 函数需要传参数的时候完整的定义为
* \" A6 ]5 \2 |$ k" W9 fint main(int argc,char **argv)3 S* h3 b) K# u9 U' Z8 h) I; @
参数 argc,表示参数的个数
) x( k5 x3 \2 j参数**argv,存储输入字符的数组,argv[0]表示程序名称,argv[1]——argv[n]输入的参数
$ \6 Y; g/ V. k不传参数的时候定义为
2 O/ g1 H. B# Y T. @3 u) Kint main(void)
; T1 N: V+ r4 V函数 main 的返回值为类型为 int,用于判断程序执行成功还是失败
7 |$ o3 ^( w/ |" _- ?+ Mmain 函数例程8 N7 {9 H+ [5 @/ c2 K( d
编写简单的 argvc.c 文件测试 main 函数。1 z+ f3 y$ _& p1 O* g( M: y
4 a" I8 [- F9 L) Q/ H }3 L- U
如上图所示,将输入的参数第一个和第二个转换成 int 类型,赋值给 i 和 j,最后输出打印。9 \$ {, B5 e- v; ?& o3 V& B. ` {
其中 argv[0]为程序名称,这里就是后面要编译的目标文件“argvc”。! J8 m$ Q- o' W+ y: e6 ^' |! m
" D3 r% A( G- D
编译运行测试9 L8 }7 M. K. u" L5 P
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录“/home/linuxsystEMCode/”,使用命令“mkdircharcontrol”新建 charcontrol 文件夹,将源码 argvc.c 拷贝进去,进入新建的文件夹 charcontrol,如下图所示。
K) S$ l8 X; S, m1 Z6 N![]()
]& m% S3 Q* V+ f$ k使用命令“ARM-none-linux-gnueabi-gcc -o argvc argvc.c -static”编译 argvc 文件, 如下图所示,使用命令“ls”可以看到生成了 argvc 可执行文件。
7 W( L4 `! I8 |/ ]7 c![]()
3 d k$ ?6 u) J; _# _: H ?这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考 10.3.5 小节。 将编译成的可执行文件 argvc,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。6 a" ?0 c8 i1 ?
![]()
5 [! i7 Q$ t7 p2 V如上图所示,程序成功运行,打印:8 W) Z. k0 L* j- U/ B2 U
the Program name is ./mnt/udisk/argvc。8 `/ ^) Q' T1 Y- F) a. b- s! G9 N
因为运行的程序就是“./mnt/udisk/argvc”,这是第一个参数5 b% N4 E$ e) |3 E. z, s" r
The command line has 2 argument:( n, Q4 O7 J3 o+ g) b
10,11。
( T7 v: `4 C9 q
3 L$ D+ Y [0 k4 J7 A输入的参数是 10 和 11,对应 argv[2]和 argv[2]。
9 x, X& t" G3 L( A0 r# Q- p17.2 字符类 led 灯3 V2 Q' Q1 H) `
在前面介绍 open 函数的时候,已经提到过如何打开字符类设备,获得句柄的方法和一般文件都是一样。# y& V; s7 Q6 B! v* e
Led 灯的设备节点在/dev 目录下,如下图所示,在超级终端可以使用 ls 命令查找。
; R* Z; r4 c B+ O/ N / z2 O: s" W. Z7 A6 k) H# i
由于涉及到硬件知识,这里简单介绍一下硬件原理,如下图所示,led 小灯的硬件原理很简单。$ _! }% p1 t# M, G" {
![]()
& E0 b5 C Z ]$ Q( z如上图所示,给 KP_COL0 和 VDD50_EN 网络高电平,三极管 L9014 就会导通,电源 VSYS 就会将电压加到电阻 R 和 led 小灯上,小灯就会亮。8 D5 R7 L8 R v# O: \
给 KP_COL0 和 VDD50_EN 网络低电平,三极管 L9014 就会截止,形成断路,小灯灭。
% G4 x9 T M# ^在前面介绍过,如果要给文件进行写操作,那么使用的是 write 函数。对于 led 小灯的操作,使用写函数,理论上也是可以的。但是对于 IO 口(这里的 IO 口指的是硬件上的 IO 口, 不是指 IO 文件)的+ R3 V0 ^2 \- W, p
4 n5 L+ R1 b) }+ `7 N6 ]; D
操作,Linux 专门设计了一个高效的函数 ioctl。
+ c8 Z+ t$ o8 Y, g% g* [5 S7 z这个函数在头文件#include中。& S: d+ O7 L% K: b
int ioctl( int fd, int request, int cmd);7 J# T8 m0 y& p; |5 z
参数 fd,函数 open 返回的句柄。5 @. L3 n3 K. N: [8 o
参数 request 和参数 cmd,由内核驱动决定具体操作,例如 request 可以代表那个 IO 口,cmd 代表对 IO 进行什么样的操作,也可以反过来。具体的含义由驱动工程师在驱动中 switch 决定。
. g( a" `+ |2 ~7 U4 J返回值:返回 0 成功;返回-1,出错。
3 P- Z3 g! J$ J; R
7 O7 T; N( J( u4 i+ `' X% z6 y- z小灯测试例程
' z" [" p U; `0 E9 G9 V2 k编写简单的 leds.c 文件测试小灯。首先添加头文件,如下图所示。+ w. f0 A# {+ J; P
通过 main 参数传过来的参数是 char 字符格式的,如果要传递给 ioctl 函数,需要用到数值转化函数atoi,添加了头文件#include 。7 P6 M' P$ o5 b A. y0 u
接着由于小灯的数量和命令都是 2,所以对小灯数量和操作数进行宏定义6 @. d: f) f% J: k3 J7 e1 O$ L
#define LED_NUM 2
. Y9 f- \ I) X; h$ `#define LED_C 2。
* L0 k( o3 t2 w" E: n![]()
& W' Q2 B5 Q) t9 P4 |7 E然后 main 函数如下图所示。 D# ]1 q5 i5 @* p8 p' A
8 ]& H" L( G9 h0 z0 F
如上图所示。
4 M; D( C- @$ t7 }( l) E Y6 l3 _第 33 行,调用了 ioctl 函数,将 main 函数的第一个和第二个参数传入驱动。: t4 s! a `% z/ v; B
第 19 行,解释那个参数具体代表什么含义,"argv1 is cmd;argv2 is io”,参数 1 对应命令,参数 2 对应具体那个 led 灯。; s; M2 l, j% S& n) Q9 k2 I0 j) q
第 36 行,将打开的设备节点"/dev/leds"关闭。
9 K2 c/ ~6 {$ i1 |( d3 O
& h+ g l3 T& i) ^9 j4 y编译运行测试3 G; g7 Y/ @6 G! h. [8 p& b8 u
在 Ubuntu 系统下,如下图所示,进入前面实验创建的目录“/home/linuxsystemcode/charcontrol”,将源码 leds.c 拷贝进去,如下图所示。7 T1 |/ t8 G( z b, ~5 Y
( Q5 M8 v4 d" r( ?4 m! P
使用命令“arm-none-linux-gnueabi-gcc -o leds leds.c -static”编译 leds 文件,如下图所示,使用命令“ls”可以看到生成了 leds 可执行文件。
7 L' y. I2 ?" W/ p# a g$ {9 i![]()
, [: X7 C' f2 A4 u$ Z% j6 e这里介绍 U 盘拷贝代码的方法,也可以编译进文件系统,具体方法参考实验 02' q1 s1 Y( c+ N/ Y
将编译成的可执行文件 open,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。, d, H1 @5 n. x( S
如下图所示,如果不加参数会有提示,然后报错。
. O* Q% W# q% k. w ! U% ?; }3 Q9 d) t6 d
如下图所示,使用命令“./mnt/udisk/leds 0 0”运行,可以看到靠近蜂鸣器的小灯灭了。' L2 c8 X4 K% P y; v- n7 F/ d* }
![]()
% O9 H8 C' C" n- q! q% d* w所有参数对小灯的控制如下:
5 Y& C# l8 v: `9 J6 f! k6 `$ b0 0 靠近蜂鸣器的小灯灭;* k1 X5 x* L# C9 r$ W* {, D
0 1 靠近按键的小灯灭;
8 ^) E$ y# e! C6 J1 0 靠近蜂鸣器的小灯亮;
% s+ T% }4 x, ~1 1 靠近按键的小灯亮。用户可以自行测试一下。9 E0 o- ?2 o$ q7 q# z
![]()
$ k& e: Z- `' X# Z |
|