Linux字符设备驱动-毕业论文_第1页
Linux字符设备驱动-毕业论文_第2页
Linux字符设备驱动-毕业论文_第3页
Linux字符设备驱动-毕业论文_第4页
Linux字符设备驱动-毕业论文_第5页
免费预览已结束,剩余37页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

本科毕业论文(科研训练、毕业设计)题 目:Linux字符设备驱动姓 名:学 院:软件学院系:专 业:软件工程年 级: 学 号: 指导教师(校内): 职称: 指导教师(校外): 职称: 年 月摘 要驱动程序在 Linux 内核里扮演着特殊的角色,它们完全隐藏了设备工作的细节。用户通过一套标准化的调用来操作设备,这些调用是与特定的驱动相互独立的;设备驱动的任务就是将这些调用映射到作用于实际硬件的相关设备操作上。这个编程接口使得驱动可以与内核的其他部分分开建立,并在需要的时候“插入”。这种模块化的方法使得 Linux 驱动易写,易于修改。本文通过在嵌入式Linux上实现一个模块化的RTC设备驱动实例,分析了Linux内核中字符设备驱动的运行机制,并着重介绍了Linux字符设备驱动的关键过程,包括基本字符设备驱动,I2C总线驱动,设备底层操作等。同时也展示了通过交叉编译来开发和调试的全过程。关键词:字符设备;设备驱动;I2C;实时时钟;交叉编译。AbstractDevice drivers take on a special role in the Linux kernel; they hide completely the details of how the device works. User activities are performed by means of a set of standardized calls that are independent of the specific driver; mapping those calls to device-specific operations that act on real hardware is then the role of the device driver. This programming interface is such that drivers can be built separately from the rest of the kernel and plugged in at runtime when needed. This modularity makes Linux drivers easy to write, easy to modify.Through an instance of realizing a modularized driver of the RTC device in the embedded Linux, this paper analyzes the function mechanism of the char device driver in detail, and pay more attention on the key process during the development of the Linux char device drivers, including the basic char device driver, the i2c bus driver, the device bottom operation and so on. The paper also presents the whole debug and development process with the cross compile method Key words: char device; device driver; I2C; RTC; cross compile.目 录第1章 引 言11.1 Linux 简介11.2 设备驱动11.3 Linux驱动21.4 选题背景3第2章 字符设备驱动框架42.1 注册设备文件42.1.1 设备号42.1.2 注册设备号42.1.3 释放设备号52.1.4 创建设备节点52.2 内核设备注册62.3 设备操作索引62.4 设备操作函数72.4.1 Open()72.4.2 Release()72.4.3 Read()72.4.4 Write()82.4.5 ioctl()8第3章 设计与实现93.1 设计思路93.1.1 设计目标93.1.2 解决方案93.1.3 最终方案93.1.4 细节103.1.5 数据流图103.2 I2C总线驱动113.2.1 I2C背景113.2.2 传输格式113.2.3 标志位 START & STOP123.2.4 响应133.2.5 传输流图133.2.6 引脚地址143.2.7 电平设置143.2.8 引脚初始化153.2.9 I2C写数据153.2.10 写入从机地址163.2.11 I2C读数据163.3 RTC设备驱动173.3.1 从机地址173.3.2 设备数据地址183.3.3 数据编码183.3.4 读寄存器数据193.3.5 写寄存器数据193.3.6 读取时间193.3.7 设置时间20第4章 编译调试214.1 交叉编译214.2 模块Makefile设计214.3 测试设计234.3.1 test.c设计234.3.2 Makefile设计244.4 调试脚本设计254.5 调试流程274.6 测试结果28结 论29致 谢30参考文献31ContentsChapter 1 Introduction11.1 Introduction to Linux11.2 Device driver11.3 Linux driver21.4 Background3Chapter 2 Farmwork of char device driver42.1 Register device files42.1.1 Device numbers42.1.2 Register device numbers42.1.3 Free device numbers52.1.4 create device node52.2 Register device in the kernel62.3 Index of device operation62.4 Device operation function72.4.1 Open()72.4.2 Release()72.4.3 Read()72.4.4 Write()82.4.5 ioctl()8Chapter 3 Design and implement93.1 Design thought93.1.1 Goal of design93.1.2 Solution93.1.3 Adopted solution93.1.4 Details103.1.5 Data flow chart103.2 I2C bus driver113.2.1 Background113.2.2 Transport format113.2.3 Flag bit START & STOP123.2.4 ACK133.2.5 Transport flow chart133.2.6 Address of pins143.2.7 Set High()143.2.8 Initialize pins153.2.9 Write()153.2.10 Write slave address163.2.11 Read()163.3 RTC device driver173.3.1 Slave address173.3.2 Device register address183.3.3 Data coding183.3.4 Read from registers193.3.5 Write to registers193.3.6 Read time193.3.7 Write time20Chapter 4 Compile and test214.1 Cross compile214.2 Module Makefile214.3 Testing design234.3.1 design of test.c234.3.2 Test Makefile244.4 Shell design254.5 Flow of test274.6 Testing result28Conclusion29Appreciations30References31第1章 引 言1.1 Linux 简介Linux 操作系统是UNIX 操作系统的一种克隆系统,是一个免费发行的UNIX操作系统在微机上的实现。它诞生于1991 年的10 月5 日(这是第一次正式向外公布的时间),由芬兰赫尔辛基大学的Linus Torvalds完成。Linux的开发得到了遍布于Internet上的许多UNIX程序员和爱好者的帮助。借助于Internet 网络,任何懂得如果去做并有能力,有兴趣的人都可以参与Linux的开发,经过全世界各地计算机爱好者的共同努力,Linux现在已成为世界上使用最多的一种UNIX 类操作系统,并且使用人数还在迅猛增长。从最初公布的Linux 内核0.01 版,到目前最新的2.6.20稳定版本,Linux在全世界hacker 的帮助下逐步完善。Linux操作系统也因为其高效、安全、可动态加载及源代码开放等特点,深受设备驱动程序开发人员的喜爱。41.2 设备驱动系统调用是操作系统内核和应用程序之间的接口,而设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,用户无需关心硬件问题,只需要通过一些独立于底层硬件运行的系统调用,像操作普通文件一样对硬件设备进行open, read, write, iotcl 等操作。设备驱动程序的主要任务就是将这些系统调用映射到作用于实际硬件设备的特定操作上,该编程接口能够使得驱动程序独立于内核的其他部分来搭建,在需要时才动态加载到内核。这种模块化的特点,使得Linux设备驱动程序的编写过程变得清晰简单。每块可以在运行时添加到内核的代码,被称为一个模块。Linux 内核提供了对许多模块类型的支持,包括但不限于设备驱动。每个模块由目标代码组成( 没有连接成一个完整的可执行文件 ), 可以动态连接到运行中的内核中, 通过 insmod 程序加载,通过 rmmod 程序卸载。5设备驱动程序作为模块,也是内核的一部分,它需要完成以下功能:1对设备的初始化和释放。2把数据从内核传送到硬件和从硬件读取数据。3读取应用程序传送给设备文件的数据和回送应用程序请求的数据。4.检测和处理设备出现的错误。1.3 Linux驱动Linux下驱动程序主要分为以下几类1. 字符设备 (char device)2. 块设备 (block device)3. 网络接口 (network device)4. 其他设备,比如cypher coprocessor, realtime extend hardware等。字符( char )设备是一种可以当作一个字节流来存取的设备( 如同一个文件 );一个字符驱动负责实现这种行为。这样的驱动常常至少实现 open,close,read,和 write 系统调用。字符设备通过文件系统结点来存取,可以映射到/dev目录下,例如/dev/console。有的字符设备如同流一样,只能向前读取。有的字符设备则可以通过lseek来进行定位读取。块设备也可以映射到/dev目录下,但是块设备比之字符设备要复杂的多,内核实现上同字符设备也是完全不同的。一个块设备(例如一个磁盘)应该是可以驻有一个文件系统的。Linux系统允许应用程序像读写一个字符设备一样读写一个块设备,不同的是它允许一次传送任意数目的字节。块和字符设备的区别仅仅在与内核在内部管理数据的方式上, 并且因此在内核/驱动的软件接口上不同,它们之间的区别对用户是透明的。网络设备,网络设备不能映射到文件系统中,系统通过一个唯一的名字来访问网络设备,例如eth0。在内核网络子系统的驱动下, 不必知道单个事务是如何映射到实际的被发送的报文上的。很多网络连接( 特别那些使用 TCP 的)是面向流的,但是网络设备却常常设计成处理报文的发送和接收。一个网络驱动对单个连接一无所知,它只处理报文。21.4 选题背景以 Linux 为代表的自由操作系统的很多优点之一,是它们的内部是开放的。 操作系统,曾经是一个神秘的领域,它的代码只局限于少数的程序员才能观看,而现在,任何具备必要技能的人都能来检查, 理解以及修改。Linux 内核保留有大量的复杂的代码,但是,想要成为内核 hacker,首先需要一个入口点,设备驱动就提供了这样的门路,了解了设备驱动,就可以很快地进入内核代码中, 不会被代码的复杂性所压倒。1在实习期间,我接触了公司目前正在开发的一块开发板,需要在这个嵌入式系统上驱动一个实时时钟设备(RTC),这个课题有一定的工程应用背景和项目效益,因此我选择它作为我的毕业设计课题。以解决实际工程问题为宗旨,在实际的工程环境下,我选择了Linux平台设计驱动程序,一方面在Linux下设计驱动程序,思路简洁,操作方便,功能也能强大,另一方面也能让我在综合应用相关领域的理论、方法和技术手段的同时,加深对Linux内核的理解。- 29 -第2章 字符设备驱动框架2.1 注册设备文件字符设备通过文件系统中的某些特殊文件来存取,可以称这些特殊文件为设备文件,它们通常位于 /dev 目录。字符驱动的特殊文件可以由使用 ls -l 输出的第一列的c标识。块设备也出现在 /dev 中,但是它们由b标识。编写驱动需要先为设备注册一个设备文件,并将它们连接起来。12.1.1 设备号所有的设备文件都有各自的设备编号,设备编号又分为主设备号和次设备号。主设备号标识设备对应的驱动程序,内核利用主设备号将设备与相应的驱动程序对应起来。次设备号由内核使用,用于正确标识设备文件所指的设备。可以通过次设备号获得一个指向内核设备的直接指针,也可将次设备号当作设备本地数组的索引,不过,内核本身基本上不关心关于次设备号的任何其他消息。Linux2.6内核中,类型dev_t (定义在中)用来保存设备号。2.4及之前的版本将dev_t定义为16位整数,但现实中经常需要超过256个次设备号,因此在2.6中,dev_t被定义成了32位整数,其中12位用来标识主设备号,20位用来标识次设备号。2.1.2 注册设备号在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用,如果确定你需要的设备号,可以通过函数register_chrdev_region () 实现: int register_chrdev_region(dev_t first, unsigned int count, char *name);这里,first 是你要分配的起始设备编号。first 的次编号部分通常是 0。count 是你请求的连续设备的总数。name 是应当连接到这个编号范围的设备的名字,它会出现在 /proc/devices 和 sysfs 中。如果不确定所要的设备号,则通过下面函数获得int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)这里,dev 是一个输出参数, 它在函数成功完成时持有你的设备号。firstminor 是请求的第一个要用的次编号,通常设定为 0。count 是你请求的连续设备的总数。name 是应当连接到这个编号范围的设备的名字。2.1.3 释放设备号当从系统中卸载设备时,应该释放相应的主设备号,通过如下函数完成:void unregister_chrdev_region(dev_t first, unsigned int count);2.1.4 创建设备节点如上所述,当不确定设备的设备编号时,将动态分配设备号,但是由于每次分配的主设备号不能保证一致,因此无法预先知道将分配的设备号从而创建节点。为使用动态编号加载一个设备驱动,用一个脚本替换对insmod的调用,它通过读取/proc/devices来创建节点,因为新分配的动态设备号总会注册到/proc/devices中。脚本可以利用awk这类工具从/proc/devices中获取信息,主要代码如下:module=” mypcf8563”device=” mypcf8563”major=$(awk $2= =$module print $1 /proc/devices)在/dev中创建文件,代码如下:mknod /dev/$device0 c $major 02.2 内核设备注册在Linux2.6内核中,注册分配完设备节点,还应该注册字符设备。内核使用类型struct cdev (include ) 来描述字符设备。在内核调用设备操作之前,必须先分配并定义一个或多个这种结构体,代码如下:pcf8563_devices = kmalloc(mypcf8563_nr_devs * sizeof(struct pcf8563_dev),GFP_KERNEL);cdev_init(&pcf8563_devices-cdev, &pcf8563_fops);pcf8563_devices-cdev.ops = &pcf8563_fops;cdev_add (&pcf8563_devices-cdev, dev, 1);其中cdev_init 函数是注册cdev 结构体的关键,原型如下:void cdev_init(struct cdev *cdev, struct file_operations *fops);这里,dev 是 cdev 结构,fops就是设备的操作索引。一旦 cdev 结构建立, 最后的步骤是把它告诉内核, 调用:int cdev_add(struct cdev *dev, dev_t num, unsigned int count);这里,dev 是 cdev 结构,num 是这个设备响应的第一个设备号,count 是应当关联到设备的设备号的数目,通常是1。2.3 设备操作索引一旦驱动程序被注册到内核表中,它的操作就和指定的主设备号对应起来,这时候在与主设备号对应的字符设备文件上进行某个操作时,内核就能从file_operations结构中找到并调用正确的函数。这些函数则是整个设备操作的核心。struct file_operations mypcf8563_fops = .owner=THIS_MODULE,.read=mypcf8563_read,.write=mypcf8563_write,.ioctl=mypcf8563_ioctl,.open=mypcf8563_open,.release=mypcf8563_release,;2.4 设备操作函数2.4.1 Open()对设备文件进行Open()系统调用时,调用驱动程序的Open()函数。完成一些初始化操作。Open()的主要任务是a) 检查设备特定的错误b) 如果设备是首次打开,对其进行初始化c) 如果有必要,更新f_op指针d) 分配并填写被置于filp-private_data里的数据结构部分代码如下: dev = container_of(inode-i_cdev, struct scull_dev, cdev); filp-private_data = dev; /* for other methods */2.4.2 Release()当最后一个打开设备的用户进程执行Close()系统调用时 ,内核将调用驱动程序的Release()函数。Release()的主要任务是a) 释放由open分配的,保存在filp-private_data中的所有内容b) 在最后一次关闭操作时关闭设备2.4.3 Read()当对设备特殊文件进行Read()系统调用时,将调用驱动程序read()函数。read()函数的主要功能是从硬件设备或内核内存中读取或复制 count 个字节到 buf 指定的缓冲区中。设备在Read()时状态较多,因此设计不同的返回值代表相应含义是Read()设计的重要内容。可以考虑返回正数,0,和代表各种错误号的负值。本驱动中Read()将读取时间,是驱动的关键函数之一。2.4.4 Write()当设备特殊文件进行Write()系统调用时,将调用驱动程序的Write()函数。Write()函数的主要功能是将参数buf 指定的缓冲区中的count 个字节内容复制到硬件或内核内存中。Write()设备和Read()设备相似,同样需要设计不同的返回值,同样考虑返回正数,0,和代表各种错误号的负值。本驱动中Write()将设置时间,是驱动的关键函数之一。2.4.5 ioctl()ioctl是特殊的控制函数,可以通过它向设备传递控制信息或从设备取得状态信息。ioctl通常是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。也经常在这里实现设备的读写。调用如下: int ioctl(int fd, ind cmd, );本驱动中ioctl()将通过cmd读取或设置时间,是驱动的关键函数之一。第3章 设计与实现3.1 设计思路3.1.1 设计目标在Linux开发编译环境下设计RTC设备(PCF8563)在MPC8272平台上的驱动程序。主要完成I2C协议/RTC原理分析,实现基于I2C总线驱动的RTC设备的初始化,同时提供时间设置、查询接口。这个字符设备驱动程序应该是完整的模块化的,符合POSIX通用Linux设备标准,能够正确地初始化设备,实现char设备的基本操作open / release / read / write / ioctl,正确地实现内核与设备间的数据交换,同时还要解决冲突等异常情况。3.1.2 解决方案本设计要实现的是基于I2C总线的PCF8563(RTC)设备驱动,有下列解决方案:1. 应用Linux系统内核对I2C设备的支持,通过I2C总线驱动I2C core,I2C控制器驱动I2C adapter,I2C设备驱动I2C driver的系列模型为PCF8563提供I2C总线的接口,直接读取设备,设置设备。2. 编写基本I2C总线规范,根据规则直接对PCF8563实现I2C规则的读写。3.1.3 最终方案方案1的实现相对简单,但是在目标文件中会产生大量冗余代码,与嵌入式开发追求高效的原则不符。而且本设计只针对一个I2C设备,完全可以只针对本设备提供需要的I2C接口,一方面大大减小了接口的复杂度,另一方面也能加深对I2C总线的理解和应用。尽管采用方案2,本设计仍然通过命名(i2c_func,pcf8563_func )将i2c驱动与pcf8563驱动在逻辑上分隔开,使其支持重用。3.1.4 细节PCF8563属于RTC实时时钟设备。RTC在内核中通常注册为misc设备,即主设备号10,次设备号235(RTC_ MINOR),设备写入/proc/misc。由于本设计偏实验研究性质,为了避免与内核RTC设备冲突,采用普通字符设备的动态注册。即动态分配主设备号,次设备号。设备写入/proc/devices。设备的底层操作通过通用I/O口实现。3.1.5 数据流图图 31 PCF8563数据流图3.2 I2C总线驱动3.2.1 I2C背景I2C(IntelIntegrated Circuit)总线是荷兰的Philips公司于八十年代初推出的一种芯片间串行总线扩展技术。它用两根线(数据线SDA、时钟线SCL)完成总线上主机与器件的全双工同步数据传送,可方便地构建多主机系统和外围器件扩展系统。I2C总线支持所有NMOS、CMOS、TTL等工艺制造的器件,其上所有的节点都连到同名的SDA、SCL上,所有数据传送都有相同的操作模式,所有I2C的外围器件都有应答能力,而且具有简单、高效,占用空间小,互联成本低等特点。因此广泛适用于嵌入式产品。I2C 总线在连接到总线的器件间传递信息时,每个器件都有各自唯一的地址识别。根据数据传输时的功能不同,把器件分为主机和从机。主机是初始化总线的数据传输并产生允许传输的时钟信号的器件,通常是微控制器。此时,任何被寻址的器件都被认为是从机,例如LCD驱动器、E2PROM等。 3.2.2 传输格式PCF8563驱动主要基于I2C总线驱动实现,因此首先应遵循I2C总线标准。I2C总线协议规定,各设备进行通信时都要有起始、结束、发送数据和应答信号。这些信号都是通信过程中的基本单元。图 32 I2C总线标准如图3-2所示,SDA线上传输的每一个字节都必须是8-bits长,启动总线后的第1个字节的高7位是对从机的寻址地址,第8位为方向位(“0”表示主机对从机的写操作;“1”表示主机对从机的读操作),其余的字节为操作数据。每一次所传输的字节数不限,传输的数据都是高位优先传输(MSB)的。每传输完一个字节,必须跟随一个ack位(ack 方向与数据传输方向相反)。因为本RTC驱动仅有一个主机,因此不考虑多主机需要仲裁的情况。3.2.3 标志位 START & STOPI2C总线规定,当SCL 线的时钟信号是高电平时,SDA线的状态代表数据的传输,即如果使SCL保持低电平,主机将进入等待状态。SDA数据的改变只能在SCL为低电平的时候进行。当SCL线是高电平时,SDA线相应的2个改变状态唯一代表了相应的2个标志。1. START当SCL 线是高电平时,SDA 线从高电平向低电平切换,这个状态代表一个START标志。相应伪代码实现如下:static void i2c_start (void)SET_LOW(I2C_SCL);SET_HIGH(I2C_SDA);SET_HIGH(I2C_SCL);SET_LOW(I2C_SDA);2. STOP当SCL 线是高电平时,SDA 线从低电平向高电平切换,这个状态代表一个STOP标志。相应伪代码实现如下:static void i2c_stop (void)SET_LOW(I2C_SCL);SET_LOW(I2C_SDA);SET_HIGH(I2C_SCL);SET_HIGH(I2C_SDA);udelay (5);3.2.4 响应数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出低电平脉冲,表示已经收到数据。1. 读响应相应伪代码实现如下:static int i2c_read_ack (void)SET_LOW(I2C_SCL);while (not receive ack)if( timeout ) printk(I2C not ACKn);return -1;/* ack timeout */udelay (5);SET_HIGH(I2C_SCL);SET_LOW(I2C_SCL);SET_HIGH(I2C_SDA);return 0;2. 发送响应相应伪代码实现如下:static void i2c_write_ack (int ack)SET_LOW(I2C_SCL);if (ack = I2C_ACK)SET_LOW(I2C_SDA);else/ noackSET_HIGH(I2C_SDA);SET_HIGH(I2C_SCL);udelay (5);SET_LOW(I2C_SCL);3.2.5 传输流图如图3-3为一次完整的I2C传输:图33 I2C数据传输3.2.6 引脚地址本I2C总线架构在MPC8272上的CPM2上,因此IO口SDA,SCL等引脚设置都应参照MPC8272上CPM2的说明。表格 31 MPC8272 Data Sheet因此SDA和SCL引脚分别为PD15,PD14。宏定义如下:/* I2C Pins */#defineI2C_SCL 0x00020000/* PD 14 0000 0000 0000 0010 0000 0000 0000 0000 */#define I2C_SDA 0x00010000/* PD 15 0000 0000 0000 0001 0000 0000 0000 0000 */3.2.7 电平设置知道了SDA,SCL pins的位置,就可以设置其电平。宏定义如下:#ifndef CPM_MAP_ADDR#define CPM_MAP_ADDR (uint)0xF0000000)#endif#defineSET_HIGH(bit)do volatile iop_cpm2_t *io;volatile cpm2_map_t* immap = (cpm2_map_t*)CPM_MAP_ADDR;io = (iop_cpm2_t *) & (immap-im_ioport);io-iop_pdird |= bit;/* set 1 for output */io-iop_pdatd |= bit;/* set 1 for high */udelay (5); while (0)通过cpm2_map_t结构体获得引脚的地址映射,借用之前定义的SDA,SCL掩码对其进行相应的 |= ,&= 位操作,即可实现高电平,低电平设置。3.2.8 引脚初始化参看MPC8272的CPM说明书,对PD15,PD14进行的操作要映射到串行线SDA,SCL上,还需要初始化一些引脚设置,代码如下:io-iop_ppard &= (I2C_SCL | I2C_SDA);/* set 0 for general - purpose IO */io-iop_podrd |= (I2C_SCL | I2C_SDA);/* set 1 for Open Drain Output */初始化同时需要发送一个STOP信号,中止之前可能正在进行的传输。i2c_stop ();/* stop first */udelay(25);3.2.9 I2C写数据正确初始化好设备,并定义了正确的I2C信号函数之后,就可以通过I2C标准写入数据。1 写入位在SCL低电平时根据要写入的位改变SDA电平,然后将SCL拉高,通过采样SDA线获得写入的位。代码如下:static void i2c_write_bit (int bit)SET_LOW(I2C_SCL);if (bit)SET_HIGH(I2C_SDA);elseSET_LOW(I2C_SDA);SET_HIGH(I2C_SCL);2 写入字节I2C传输的数据都是高位优先传输(MSB)的,因此传输字节时,分别剥离出高位,依次传送,最后等待接收端回送一个ack信号。代码如下:static int i2c_write_byte (unsigned char byte)int i;for (i = 0; i 8; +i) if (byte & 0x80) i2c_write_bit (1); else i2c_write_bit (0);byte = 1;return i2c_read_ack();3.2.10 写入从机地址I2C标准规定传输的第一个字节是7位的从机地址加1位的读写位。因此,伪代码如下:addr = (addr 1) | ( 读 ? 0x01: 0x00);i2c_write_byte (addr);3.2.11 I2C读数据当写入正确的7位从机地址加1位读标志位后,就可以通过I2C标准读取数据。1 读入位先拉低SCL线,延时一小段时间,这时设备将改变SDA电平,拉高SCL线,采样SDA线则可获得要读取的位。伪代码如下:static int i2c_read_bit (void)int bit;SET_LOW(I2C_SCL);SET_HIGH(I2C_SCL);bit = SDA线高电平 ? 1 : 0;return (bit);2读入字节I2C传输的数据都是高位优先传输(MSB)的,因此接收字节时,分别得到高位,次高位,最后回送一个ack或noack信号。static unsigned char i2c_read_byte (int last)unsigned char byte = 0;int i;设置SDA线为输入模式for (i = 0; i 8; +i) byte = (byte 4) & 0x0F) * 10) + (n & 0x0F);static unsigned int bin2bcd (unsigned int n)return (n / 10) tm_sec= bcd2bin (sec & 0x7f);tmp-tm_min= bcd2bin (min & 0x7f);tmp-tm_hour= bcd2bin (hour & 0x3F);/* 24 Hour */tmp-tm_wday= bcd2bin (wday & 0x07);tmp-tm_mday= bcd2bin (mday & 0x3f);tmp-tm_mon= bcd2bin (mon & 0x1f);tmp-tm_year= bcd2bin (year);/* 0-99 1970-2069*/tmp-tm_yday= 0;tmp-tm_isdst= 0;3.3.7 设置时间同上,转换成BCD码后写入寄存器。year = bin2bcd (tmp-tm_year % 100);pcf8563_write_reg (0x08, year);pcf8563_write_reg (0x07, mon);pcf8563_write_reg (0x06, wday);pcf8563_write_reg (0x05, mday);pcf8563_write_reg (0x04, hour); /* 24 Hour */pcf8563_write_reg (0x03, min);pcf8563_write_reg (0x02, sec);第4章 编译调试4.1 交叉编译编写驱动时,如果驱动的是PC机上的设备,我们只要用Linux本身提供的强大的编译器(如gcc)直接编译就可以了。而一般的嵌入式设备所用的CPU和PC机上的CPU有着不同的架构,而且嵌入式设备上未必有gcc等编译器,这样就要求我们在PC机上编译出适用于嵌入式设备CPU架构的软件。在一个体系结构上生成另一个体系结构上的可执行代码,这就是我们所说的交叉编译。当我们在嵌入式Linux开发时,一般都要经过交叉编译这一步,本驱动也不例外。要进行交叉编译,我们需要先在主机平台上安装对应的交叉编译工具链(cross compilation tool chain),然后用这个交叉编译工具链编译我们的源代码,最终生成可在目标平台上运行的代码。 本驱动的开发环境是i386的Linux,应用在ppc体系结构的MPC8272平台上,需要的交叉编译链路径如下:/home/guys/starwave-ppc/staging_dir_powerpc/bin/powerpc-linux-uclibc-gcc4.2 模块Makefile设计本驱动是模块化开发,因此在编译时需要添加编译模块相应的选项,通过设置Makefile实现驱动模块的编译。Makefile代码如下:CROSS_COMPILE=/home/guys/starwave-ppc/staging_dir_powerpc/bin/powerpc-linux-uclibc-CC = $(CROSS_COMPILE)gccLD = $(CROSS_COMPILE)ldRM = rmKMOD = mypcf8563INC = myi2cLINUX = /home/guys/starwave-ppc/build_powerpc/linuxSYS_INC = /home/guys/starwave-ppc/staging_dir_powerpc/lib/gcc/powerpc- linux-uclibc/3.4.6/includeCINCS = -I . -I include -I inc

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论