该Linux驱动用来控制开发板上的4个LED灯,即通过向Linux驱动发送数据可以控制LED灯的开关。LED驱动提供两种交互方式:命令和读写设备文件。

测试LED驱动之前需用USB线连接开发板,然后打开开发板。成功启动后,执行build.sh脚本文件编译和安装LED驱动。build.sh脚本文件会自动将s3c6410_leds.ko文件上传到开发板并安装。LED驱动只能在开发板上安装,build.sh执行了build_s3c6410.sh脚本文件进行编译和安装。LED驱动会建立一个/dev/s3c6410_leds设备文件,该Linux驱动可控制4个LED,通过向设备文件发送长度为1到4的字符串可以控制这4个LED的开关。1表示开,0表示关。字符串长度不足4个,相当于后面补0。执行命令

“# adb shell "echo '1'> /dev/s3c6410_leds" #打开第一个LED,其他的都关闭

# adb shell "echo '1010'> /dev/s3c6410_leds" #第一个和第三个LED打开,第二个和第四个关闭

# adb shell "echo '1111'> /dev/s3c6410_leds" #打开所有的LED”可控制开发板上的LED。可使用命令“# sh ~/drivers/s3c6410_leds/test_leds.sh”执行test_leds.sh脚本文件测试LED。执行脚本文件后,开发板上的4个LED会根据0到15的二进制形式控制LED,第一个为最低位。脚本文件使用的是标准的Bash Shell,如果在Ubuntu下无法成功执行,是因为其将dash作为默认的脚本解析器。可使用命令“# dpkg-reconfigure dash”将默认脚本解析器改成Bash,出现设置界面时,选择“否”,再回车即可。

创建LED驱动的设备文件,步骤如下;1.描述设备文件需要使用一个cdev结构体,该结构体在<Linux内核源代码>/include/linux/cdev.h文件中定义。其中的大多数成员变量只需调用cdev_init()就可被初始化,该函数在<Linux内核源代码>/fs/char_dev.c文件中。若要在一个Linux驱动中建立多个设备文件,cdev.count变量的值就是要建立的设备文件数。这些设备文件对应的cdev结构体就通过cdev.list.prev和cdev.list.next指针变量连接,从而形成一个双向链表。cdev.owner变量未在cdev.init函数中初始化,使用语句“leds_cdev.owner=THIS_MODULE;”来初始化2.Linux设备文件的设备号分为主设备号和次设备号。用一个int类型表示,其中前12位表示主设备号,后20位表示次设备号。设备号有两种指定方法:直接在代码中指定和动态分配。第一种方法虽比较直观,但如果主设备号和次设备号已存在,建立设备文件就会失败。以防万一,可使用alloc_chrdev_region()自动分配一个未使用的主设备号。习惯上将次设备号设为0。函数原型为int alloc_chrdev_region(dev_t*dev,unsigned baseminor,unsigned count,const char *name),其中dev表示设备号指针,函数会随机分配一个未使用的主设备号,根据baseminor参数值分配次设备号。count表示分配的次设备号范围。name表示设备文件名称。多个Linux设备文件可拥有同一个主设备号,但两个设备的主设备号和次设备号不能都一样。在使用函数自动分配设备号时,baseminor和count参数不要设太大,否则次设备号会溢出,且主设备号会进位,从而变成下一个主设备号。若要直接指定设备号,需使用register_chrdev_region()注册字符设备区域,该函数在<Linux内核源代码>/fs/char_dev.c文件中实现,原型为:int register_chrdev_region(dev_t from,unsigned count,const char *name),from表示设备号,count表示次设备号范围,name表示设备文件名称。一般采用分别指定主设备号和次设备号的方式指定设备号,需要MKDEV宏将主设备号和次设备号组合成设备号-“int dev_number=MKDEV(major,minor);”。也可分别使用MAJOR和MINOR宏从设备号中获取主设备号和次设备号,代码为:“int major=MAJOR(dev_number); int minor=MAJOR(dev_number);”3.cdev_add()用于将字符设备添加到probes数组中。函数在<Linux内核源代码>/fs/char_dev.c文件中实现,原型为

“int cdev_add(struct cdev *p,dev_t dev,unsigned count){

p->dev=dev;

p->count=count;

return kobj_map(cdev_map,dev,count,NULL,exact_match,exact_lock,p);

}”,调用该函数需指定设备文件指针p、设备号dev和设备文件数量count。在该函数还调用了一个重要的函数kobj_map,此函数负责将设备文件的相关信息添加到保存已建立的设备文件的probes数组中。kobj_map()和probes数组都在<Linux内核源代码>/drivers/base/map.c文件中4.struct class包含一些与设备文件有关的变量及一些回调函数指针变量,使用class_create宏创建struct class,代码为

“struct class *leds_class=NULL;

leds_class=class_create(THIS_MODULE,"dev_name");”,dev_name是设备文件名称。class_create宏实际上使用了_class_create()创建struct class。该函数在<Linux内核源代码>/drivers/base/class.c文件中实现5.device_create()用于创建设备文件,该函数在<Linux内核源代码>/include/linux/device.h文件中定义,在<Linux内核源代码>/drivers/base/core.c文件中实现。可使用代码“device_create(leds_class,NULL,dev_number,NULL,DEVICE_NAME);”调用device_create()创建设备文件,其中leds_class表示struct class,dev_number表示设备号,DEVICE_NAME表示设备文件的名称。编写leds_create_device()时应了解:①DEVICE_COUNT表示建立设备文件的个数②alloc_chrdev_region()的第二个参数表示分配的起始次设备号。如果第三个参数的值大于1,函数会依次分配次设备号③采用自动分配设备号的方式创建设备文件,建议使用MAJOR和MINOR宏获取主设备号和次设备号,并分别保存在major和minor变量中,以备之后使用到④LED驱动的设备号保存在dev_number变量中,要将leds_cdev.dev变量的值赋给dev_number变量。leds_init()是LED驱动的初始化函数,在函数中直接调用leds_create_device()即可。若将S3C6410_LEDS_MAJOR设为0,系统会自动分配一个未使用的主设备号,次设备号仍是10.在每次装载LED驱动时主设备号可能会不一样,但次设备号总是10。

卸载LED驱动的设备文件:卸载操作会稍简单一些,需依次调用device_destroy、class_destroy和unregister_chrdev_region()。leds_destroy_device()用于卸载LED驱动的设备文件,leds_exit()是LED驱动的卸载函数,它通过调用leds_destroy_device()来完成卸载LED驱动设备文件的工作。

设置寄存器与初始化LED驱动:ARM处理器有多个寄存器,通过设置不同寄存器的值。可以设置LED引脚的状态、打开或禁止上拉电路以及控制LED的亮和灭。我们必须知道的有:①LED有两个引脚:GPB0和GPB1,其中一个引脚连接到了ARM处理器的GPI0端口,另一个引脚经过一个限流电阻连接到电源VCC3上。当GPI0端口为低电平时,LED两端产生电压差,LED有电流通过发光;反之当GPI0端口为高电平时,LED中没有电流通过,灯熄灭。高低电平之间切换非常快,LED亮灭之间有一定的延迟②控制LED需要通过3个寄存器完成,GPMCON端口配置寄存器、GPMDAT端口数据寄存器和GPMPUD端口上拉电路寄存器③每一个寄存器可以使用4个字节,即一个int类型数据占用的空间④使用GPMCON寄存器的低16位将LED的两个端口GPB0、GPB1的属性设为Output。每4位设置一个LED,共4个LED。output的值是0001,若使用十六进制表示,寄存器的低16位的值是0x1111⑤使用GPMDAT寄存器的低4位控制4个LED的亮、灭。每一位控制一个LED,最低位控制离电池最近的LED。0表示亮、1表示灭⑥使用GPMPUD寄存器的低8位分别打开4个LED的上拉电路。每两位控制一个LED的上拉电路。10为打开上拉电路。使用十六进制的话,GPMPUD寄存器的低8位是0xAA,才能同时打开4个LED的上拉电路。以上3个寄存器在内存中都有一个虚拟地址。向这些地址写入数据后,ARM处理器会使用一套算法将虚拟地址映射成物理地址,并根据物理地址将数据写入相应的硬件端口。ARM处理器中的GPMCOM、GPMDAT和GPMPUD的虚拟地址在Linux内核中都使用了宏定义。为了跟踪这些宏,需再加两个include路径:/root/kernel/linux_kernel_2.6.36/arch/arm/mach-s3c64xx/include和/root/kernel/linux_kernel_2.6.36/arch/arm/plat-samsung/include。这三个寄存器的虚拟地址对应的宏分别为S3C64XX_GPMCON、S3C64XX_GPMPUD、S3C64XX_GPMDAT。这三个宏涉及了4个头文件共9个宏。可推出S3C64XX_GPM_BASE的值是0xF04500820,GPMCON、GPMDAT和GPMPUD寄存器的虚拟地址分别为0xF04500820、0xF04500824和0xF04500828,这三个虚拟地址是固定的,可向这三个地址写数据。更好的是使用S3C64XX_GPMCON、S3C64XX_GPMPUD、S3C64XX_GPMDAT来操作这3个地址。一般需在LED驱动装载时初始化上述3个寄存器。只要在leds_init()中调用leds_init_gpm()就可完成寄存器的初始化。

控制LED:LED驱动可使用两种方式控制LED:通过字符串控制LED和通过I/O命令控制LED。要使用以上两种方式控制LED,驱动必须接收相应的数据。若通过字符串控制LED,需使用file_operations.write(),可接收向设备文件写入的数据。若通过I/O命令控制,需使用file_operations.ioctl(),可接收向字符设备发送的命令和参数。s3c6410_leds_write()用于接收向LED驱动的设备文件写入控制LED的数据,在实现其功能编写代码时需了解:①4个LED的亮灭用一个长度为4的mem数组。1表示点亮LED,0表示熄灭LED。与GPMDAT寄存器的低4位表示的含义正好相反②若写入的字符串长度小于等于4,直接写入这些字符串。若长度大于4,则只写入前4个字符串。s3c6410_leds_write()要按传入该函数的字符串长度返回,否则系统会调用多次该函数写入字符串③事先mem数组已被清零,若要写入的字符串长度小于4,则相当于后面的字符都是④向GPMDAT寄存器写入数据之前最好先读取GPMDAT寄存器的当前值,并通过位与、或等操作保留与本次操作无关的值⑤ioread32、iowrite32用于读写虚拟地址中的32位数据。使用命令

“# adb shell 'echo 1101 > /dev/s3c6410_leds'

# adb shell 'echo 1 > /dev/s3c6410_leds'”可通过字符串控制LED的亮、灭。I/O命令无法使用命令行方式进行测试。

LED驱动的模块参数:若想在装载LED驱动时指定默认状态值,就要使用模块参数。为Linux驱动指定一个模块参数需使用module_param(name,type,perm)宏。name表示参数名,type表示参数类型,perm表示读/写权限。module_param支持的参数类型包括byte、short、ushort、int、uint、long、charp、bool和invbool。使用module_param宏指定模块参数时,会在/sys/module目录下生成和驱动设备文件同名的目录。若在装载Linux驱动时未指定某个参数,则参数文件的内容是该参数在Linux驱动源代码中指定的默认值。通过module_param宏可指定参数文件的访问权限。S_IRUGO表示所有的用户都可访问该参数文件中的内容,但不能修改。S_IRUGO|S_IWUSR表示允许所有用户读,以及创建文件的用户写。Linux内核还提供了更多的定义访问权限的宏。S_IRWXUGO表示所有用户可对文件读、写和执行。IWUGO表示所有用户对文件只有写权限。需要修改LED驱动的代码,为LED驱动添加一个模块参数,该参数存储了4个LED的初始状态,参数类型为int。参数值的范围是0到15.参数值控制LED的规则与GPMDAT寄存器低4位控制LED的规则相同。为LED驱动添加模块参数首先要定义一个保存模块参数值的变量,然后使用module_param宏指定模块参数的相关信息。最后修改leds_init()代码,将leds_init_gpm()的参数值改成~leds_state。使用命令“# adb shell insmod /data/local/s3c6410_leds.ko leds_state=3”可测试LED驱动的模块参数。执行完命令后,会在/sys/module/s3c6410_leds/parameters目录下生成一个leds_state文件,使用命令

“# adb shell cat /sys/module/s3c6410_leds/parameters/leds_state”可看到文件内容为3。使用命令“# adb shell 'echo 5 > /sys/module/s3c6410_leds/parameters/leds_state'”可将文件内容改为5。修改leds_state文件内容后,在LED驱动代码中的leds_state变量值会变成5。Linux驱动在装载时会将指定的参数值写入参数文件,若未指定参数值,Linux驱动会将参数的默认值写入参数文件。在Linux驱动工作的过程中,参数值会与参数文件中的内容同步。使用module_param_array(name,type,nump,perm)宏可为Linux驱动指定数组形式的模块参数。nump表示存储数组长度的变量的指针,perm表示参数文件的访问权限。通过命令“# adb shell insmod /data/local/s3c6410_leds.ko 'leds_state=11 param=str1,str2,str3'”可指定params参数值。如果params参数指定的值的个数少于数组长度,后面的数组元素使用默认值。如果大于数组长度,LED驱动装载失败,并在日志中输出信息。使用模块参数要注意:①通过module_param_array宏的第3个参数指定数组长度时要使用指针类型的数据②如果Linux驱动含有多个模块参数,需将这些参数用单引号或双引号括起来③指定数组类型的参数值时,逗号前不能有空格。

第七章 LED将为我闪烁:控制发光二极管的更多相关文章

  1. 第七章 LED 将为我闪烁:控制发光二级管

    在上一章中了解到驱动程序的开发步骤,并一个实列来演示如何开发一个完整的驱动.但这个驱动只是简单的演示了实现步骤.真正的驱动需要与硬件直接进行相互交互.这节完整的演示驱动程序,控制开发板上的4个led灯 ...

  2. 第七章 LED将为我闪烁:控制发光二级管

    LED驱动开发实验 如图所示,LED1-LED2 分别与GPC0_3.GPC0_4 相连,通过GPC0_3.GPC0_4 引脚的高低电平来控制三极管的导通性,从而控制LED 的亮灭. 根据三极管的特性 ...

  3. 第七章 LED将为我们闪烁:控制发光二极管

     第七章 LED将为我们闪烁:控制发光二极管 本章我们将会看到一个完整的linux驱动程序,通过linux驱动程序控制LED的四个小灯,通俗的说就是通过向linux驱动程序来控制LED小灯的开关.用到 ...

  4. 第7章 LED将为我闪烁:控制发光二极管

    所谓I/O内存是通过各种接口连接到主机的硬件在主机内存的映射.LED驱动还提供了两种交互方式:命令和读写设备文件. 创建设备文件的步骤: 第1步:使用cdev_init函数初始化cdev 第2步:指定 ...

  5. 第七章:LED将为我闪烁:控制发光二极管

        在之前章节了解到Linux驱动程序可以控制软硬件,可以实现软硬件之间的交互.在这章我们学习LED驱动的实现原理.Linux内核提供了多个与I/O内存交互的函数可以实现控制硬件.    编写LE ...

  6. LED将为我闪烁:控制发光二极管

      一个完整的linux驱动主要由内部处理和与硬件交互两部分组成.其中内部处理主要是指linux驱动的装载.卸载.与设备文件相关的动作处理以及业务逻辑等:与硬件交互主要是指通过iowrite32.io ...

  7. Android系统移植与驱动开发——第七章——LED驱动

    LED驱动的实现原理 编写LED驱动: 测试LED驱动之前需要用USB数据线连接开发板,然后打开电源,成功启动之后,执行build.sh脚本文件编译和安装LED驱动,顺利则会自动连接 如果有多个设备文 ...

  8. 第七章Bulk设备

    小川工作室编写,本书为LM3S的USB芯片编写,上传的均为草稿,还有没修改,可能还有很多地方不足,希望各位网友原谅! QQ:2609828265 TEL:15882446438 E-mail:paul ...

  9. 精通Web Analytics 2.0 (9) 第七章:失败更快:爆发测试与实验的能量

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第七章:失败更快:爆发测试与实验的能量 欢迎来到实验和测试这个棒极了的世界! 如果Web拥有一个超越所有其他渠道的巨大优势,它就 ...

随机推荐

  1. App开发三种模式

    APP开发三种模式 现在App开发的模式包含以下三种: Native App 原生开发AppWeb App 网页AppHybrid App 混合原生和Web技术开发的App 详细介绍: http:// ...

  2. 【安全测试】WebGoat安装

    参考资料:http://wenku.baidu.com/link?url=Qg8GOqw6-CK92-3Dgrm608TlJjDtKMLU9ZlC73Js9LD2FZFgqdHEfJ2sTIRCae_ ...

  3. Synchronized快

    /* JAVA对于多线程的安全问题提供了专业的解决方式 就是同步代码块 synchronized(对象)//这个对象可以为任意对象 { 需要被同步的代码 } 对象如同锁,持有锁的线程可以在同步中执行 ...

  4. 神奇的margin之豆瓣豆瓣么么哒

    在经过周末的豆瓣主页和这周的豆瓣电影,表示网页什么的已经被我玩坏了. 老师在周末布置豆瓣主页,对于只学了四天的css和html的我,表示鸭梨山大. 最开始的两个小时只能做出一个连自己都看不下去的导航栏 ...

  5. synchronized和ReentrantLock

    一.什么是sychronized sychronized是java中最基本同步互斥的手段,可以修饰代码块,方法,类. 在修饰代码块的时候需要一个reference对象作为锁的对象. 在修饰方法的时候默 ...

  6. META-INF文件夹是干啥的,META-INF文件夹的作用, META-INF文件夹能删吗

    今天有人问到 META-INF文件夹是干啥的,META-INF文件夹的作用, META-INF文件夹能删吗,还有项目的META-INF下面一般会有个MANIFEST.MF 文件,都是干啥的. 百度搜了 ...

  7. git clone带用户名和密码的方式

    git clone http://username:password@127.0.0.1/res/res.git

  8. DataTable数据导出CSV文件

    public static void SaveAsExcel(DataTable dt1) { //System.Windows.Forms.SaveFileDialog sfd = new Syst ...

  9. ADO总结测试数据库

    create database ADO测试 go use ADO测试 go create table Student ( Code ) not null primary key,--学生编号,主键 N ...

  10. Scala 学习笔记(五)

    def main(args : Array[String]): Unit = { def add(x:Int,y:Int):Int = { return x+y; } def subtract:(In ...