一、 led驱动的实现原理:

通过GPC0_3和GPC0_4引脚的高低电平来控制三极管的导通性,从而控制LED灯亮灭。

 

尽管 Linux 驱动直接与硬件打交道,但并不是 Linux 驱动直接向硬件中的内存写数据, 而是与本机的I0内存(I/O Memory,位于内核空间〉进行交互。所谓 1/0 内存是通过各种接口( PCI、 USB、蓝牙、以太网口等〉连接到主机( PC、手机〉的硬件〈网卡、声卡、摄像头等〉在主机内存中的映射。

二、 编写led驱动

1>首先我们需要感受一下led驱动的结果:

测试 LED 驱动之前需要用 USB 数据线连接 S3C 开发板,然后打开 S3C 开发板的电源开关。成功启动后,执行 build.sh脚本文件编译和安装 LED 驱动。如果一切顺利,build. sh脚本文件会自动将 s3c6410_leds.ko 文件上传到 S3C 开发板并安装.

可以用下面的命令控制开发板上的 LED:

 adb shell ”echo ‘1’ > /dev/s3c6.410_leds" #打开第 1 个 LEO,其他的 LED 都关闭

adb shell ” echo’ 1010 ’> /dev/s3c6410_leds" #第 1 个和第 3 个 LED 打开,第2个和 第 4 个 LED 关闭

2>其次:创建 LED 驱动的设备文件,步骤如下:

第1步:使用 cdev_init 函数初始化 cdev:

描述设备文件需要一个 cdev 结构体.cdev 的大多数成员变量并不需要我们自己去初始化, 只要调用 cdev init 函数就可以初始化大 部分 cdev 的成员变量。

第 2 步:指定设备号

Linux 设备文件的设备号分为主设备号和次设备号。用 1 个 int类型(devt)表示。其中前 12 位表示主设备号,后 20 位表示次设备号。设备号有如下两种指定方法:

1、直接在代码中指定(硬编码〉

2、动态分配。

i.nt  alloc_c hrdev_region(dev_t *dev, unsigned baseminor, unsigned  count, const char *name) 其中,dev 表示设备号指针〈就是个int 指针), .alloc_chrdev_region函数会随机分配-个未使用的主设备号,并根据baseminor参数值分配次设备号。

如果直接指定设备号,需要使用register_chrdev_region函数注册字符设备区域。

一般采用分别指定主设备号和次设备号的方式指定设备号,因此需要 MKDEV宏将主设备号和次设备号组合成设备号,代码如下:

// major 表示主设备号, minor表示次设备号

 int dev_number =MKDEV(m:ajor, minor) ;

可以分别使用 MAJOR和 MiNOR宏从设备号中获取主设备号和次设备号,代码如下:

// 获取主设备号

int major=MAJOR(dev_number);

 // 获取次设备号

 int minor= MAJOR(dev number);

第 3步:使用 cdev_add函数将字符设备添加到内核中的字符设备数组中

cdev_add函数用于将字符设备添加到 probes数组〈保存己建立的字符设备〉中。

第 4 步: 使用 class_create宏创建 struct class

struct class 包含了一些与设备文件有关的变量以及一些回调函数指针变量。

第 5 步:使用 device_create 函数创建设备文件

使用下面的代码调甩device_create 函数创建设备文件。

 device create(leds class, NULL, dev number, NULL, DEVICE NAME);

其中 leds class 表示 struct class, dev number表示设备号,DEVICE NAME 表示设备文件的名称。

3>卸载 LED 驱动的设备文件

卸载 Linux 驱动的设备文件稍微简单-些,需要依次调用 device_destroy、class_destroy 和 unregister_chrdev_region 方法。

4>设置寄存器与初始化 LED 驱动

ARM 处理器有多个寄存器,通过设置不同寄存器的值。可以设置 LED 引脚的状态、打开或禁止上拉(pull-up)电路以及控制 LED 的亮和灭。

注意:

  1. LED 有两个引脚: GPBO 和 GPBI 。其中一个引脚连接到了 ARM 处理器的 GPIO 端口,另 一个引脚经过一个限流电阻连接到电源: VCC3 上。。当 GPIO 端口为低电平时, LED 两端产生电压差,这时 LED 有电流通过并发光;反之当 GPIO 端口为高电平时, LED 中没有电流通过, LED 将 熄灭。
  2. 控制 LED 需要通过 3个寄存器来完成。这 3个寄存器是GPMCON (端口配置寄存器〉、 GPMDAT (端口数据寄存器)和 GPMPUD (端口上拉电路寄存器〉。
  3. 每一个寄存器可以使用 4个字节,也就是一个int型类型数据占用的空间。
  4. 使用 GPMCON 寄存器的低 16 位将 LED 的两个端口( GPBO 和 GPB l )的属性,设为 Output。
  5. 使用 GPMDAT 寄存器的低 4 位控制 4 个 LED 的亮、灭。
  6. 使用 GPMPUD 寄存糕的低 8 位分别打开 4 个 LED 的上拉( pull-up)电路。

一般需要在LED驱动加载时初始化以上3个寄存器。先编写一个初始化函数--à在 leds_init 函数中调用leds_init_gpm 函数就可以完成寄存器的初始化。

5>控制 LED

LED 驱动可以使用如下两种方式控制 LED:1、通过字符串控制 LE D; 2、通过 l/0命令控制 LED ;

其次:LED 驱动必须要接收相应的数据。 如果通过字符串控制 LED。需要使用. fif.e_operations.write函数。如果通过 I/0命令控制 LED, 需要使用 file__operations.iocdl函数。

6> LED 驱动的模块参数

使用一个模块参数:Linux 驱动指定一个模块参数需要使用 module_param Cname, type, perm)宏。其中 name 表示参数名, type 表示参数类型, perm表示读/写权限。

module_param 支持的参数类型包括 byte、 short、 ushort (无符号 short)、 int、 uint (无符号 int〕、 long、 ulong (无符号 long)、 charp (字符指针)、 bool 和 invbool (布尔的反)。

因此:修改 LED 驱动的代码,为 LED 驱动添加一个模块参数:

首先要定义一个保存模块参数值的变量(leds_state),--àmodule_param 宏指定模块参数的相关信息--à修改 leds_init 函数的代码;将leds_init_gpm 函数的参数值改成leds_state (因为模块参数值和LED 驱动控制lED的规则正好相反)。

扩展:

使用下面的命令可以测试 LED 驱动的模块参数:

# adb shell insmod  /data/1ocal/s3c6410_leds.ko leds_state=3 点亮靠近电池的两个 LED

使用下面的命令可以看到leds_state文件的内容为3 :

# adb shell cat /sys/module/s3c6410_leds/parameters/leds_state

使用下面的命令可以将 leds_state文件的内容改为 5

 # adb shel l ’echo 5 > /sys/module/s3c6410_leds/parameters/leds_ state’修改 leds_state文件的内容后。在LED驱动代码中的leds_state值会变成5.

使用模块参数我们需要注意:

1. 通过module_param_array宏的第3个参数指定数组长度时使用指针类型的数据;

2. 如果linux驱动含有多个模块参数,需要将这些参数用单引号或双引号括起来;

3. 指定数组类型的参数值,逗号前后不能空格。

7>led灯完整代码的编写。

三、 测试led驱动

在本章学习过程中,前面已经提到过测试方法,此处为更复杂的方法:可以向 LED 设备文件发送字符串,还可以发送 I/0 控制命令。其测试方式包括可执行程序测试、 NDK 测试和 Java 测试。

1> 编写测试 I/O 控制命令的通用程序

LED 驱动有两种与设备文件交互的方式:

分别为:直接写入字符串数据和I/O控制命令。

其中直接向设备文件写入数据可以使用命令行方式来完成,也可以通过 write 函数来完成。但 I/0 控制命令只能通过 iocll 函数发出。

注意:编译android源代码有很多方法,例如:使用独立的交叉编译器、AndroidADK等。如果利用android源代码编译,要保证本机上有Android源代码,并且在<Android混代码>/development目录为ioctl 目录建立一个符号链接,符号链接名必须是ioctl。

1.在ioctl 目录有一个 build_android.sh脚本文件,读者可以直接运行这个脚本文件利用Adroid 源代码编译 ioctl【前提:按照build_android.sh文件中的路径确定android源代码的位置,或者修改build_android.sh文件中的Android源代码路径】-----à先编译 test_ioctl文件----à将生成的ioctl文件上传到开发板的“/data/local”目录,最后执行ioctl命令。

注意:ioctl 的命令行参数必须至少有3 个 (多余的被忽略〉,如果命令行参数小于 3 个,会输出ioctl 命令的用法提示。

2.在开发板上运行的可执行文件同样可以在 eclipse集成环境中开发。由于ioctl是通用的,因此,应该为每一个需要测试的程序单独建立1个 Eclipse 工程,当我们测试一个程序时,例如:led。我们需要建立一个eclipse目录,用于存放eclipse工程的相关内容。另外,还需要为每一个测试程序单独建立1个脚本文件,其最后一条命令为 ioctl命令传递了3个参数,该条命令如下:

adb shell ‘/data/loctal/ioctl  /dev/s3c6410_leds 1 .2'//将第三个led点亮。

2> 使用 NDK 测试 LED 驱动

NDK 程序测试和可执行程序测试差不多。只不过 NDK Library 可以被 Java 程序调用,而在 Android 系统中的可执行程序在非 root 状态下是无法直接被调用的。所以如果想在 Android 应用程序中测试 Linux 驱动, 最直接的方法就是使用 NDK。

3> 使用 Java 测试 LED 驱动

由于 JDK 未提供发送I/O命令的 API,因此使用 Java 只能通过发送控制字符串测试 LED 驱动。

四、 Led驱动移植

Android 系统能否正常运行,在很大程度上 取决于 Android 能否识别当前设备的硬件,例如,显示屏、 Wi-Fi、 GPS、 GSM、蓝牙、各种传感器等,识别这些设备的工作主要由 Linux 驱动完成。而 Linux 驱动必须要在当前使用的 Linux 内核下编译才可以安装在当前的 Linux 或 Android 系统中。LED 驱动最简单的移植就是将其在不同 Linux内核版本下进行编译。

例如:以led驱动为例:

LED 驱动是通过设置 GPMDAT 寄存器来控制 LED 的亮灭的。 GPMDAT 寄存器的低 4位分别控制 4个 LED 。 0 表示打开 LED, 1 表示关闭 LED。这是在 OK64 1 0 开发板上的设置。如果要将 LED 驱动移植到另外基于 S3C6410 的开发版上,而这个目标开发板上恰好与 OK6410 开发板的 GPMDA1、寄存器的低 4 位表示的含义相反,也就是说, 1 表示打开 LED,0 表示关闭 LED, 那么 LED 驱动的代码就要做相应的改变。首先要改变的就是 leds_init 函数。在 LED 驱动装载后,会 用 OxE 设置 4个 LED 的默认状态. 而在新的开发板上,就要将 OxE 变成 Oxl.

 

我们还需要修改控制 LED 的代码,例如,下面的代码用于发送控制字符串。需要将生成开关 LED 的代码互换位置。

注意:移植linux有一条非常重要,就是在修改Liunx驱动的源代码时应尽量不修改linux驱动接口。

 

posted on 2016-06-05 20:37  Y-Beatrice  阅读(427)  评论(0编辑  收藏  举报