《嵌入式Linux开发入门》课件第6章_第1页
《嵌入式Linux开发入门》课件第6章_第2页
《嵌入式Linux开发入门》课件第6章_第3页
《嵌入式Linux开发入门》课件第6章_第4页
《嵌入式Linux开发入门》课件第6章_第5页
已阅读5页,还剩196页未读 继续免费阅读

下载本文档

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

文档简介

第6章嵌入式Linux设备驱动程序的设计与实现

6.1嵌入式Linux驱动程序简介6.2嵌入式Linux驱动程序框架6.3嵌入式Linux模块化驱动程序设计6.4嵌入式Linux的CAN总线通信6.5嵌入式Linux的IIC总线通信6.6嵌入式Linux的D/A转换6.7嵌入式Linux的A/D转换6.8嵌入式Linux的PS/2键盘6.9嵌入式Linux的8255驱动6.10嵌入式Linux的PWM驱动6.11嵌入式Linux的串口驱动6.12LCD测试6.13矩阵键盘6.1嵌入式Linux驱动程序简介设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件细节,对应用程序来说硬件是透明的。在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。

Linux系统的设备分为字符设备(chardevice)、块设备(blockdevice)、网络设备(networkdevice)和其他设备。字符设备是不需要缓冲而直接读/写的设备,如串口、键盘和鼠标等;块设备的访问通常需要缓冲来支持,以数据块为单位来读/写,如磁盘设备等;网络设备是通过套接字来访问的特殊设备。用户进程通过设备文件实现与硬件的交流。每个设备文件都有其文件属性,表示是字符设备还是块设备。另外每个文件都有两个设备号:第一个是主设备号,标识驱动程序;第二个是从设备号,标识使用同一个设备驱动程序的不同硬件设备,比如有两个软盘,就可以用从设备号来区分它们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。

Linux的设备驱动程序可以分为三个主要组成部分:

(1)自动配置和初始化子程序,负责检测所要驱动的硬件设备是否存在和是否能正常工作。如果该设备正常,则对这个设备及相关设备驱动程序需要的软件状态进行初始化。这部分驱动程序仅在初始化的时候被调用一次。

(2)服务于I/O请求子程序,又称为驱动程序的上半部分。调用这部分是由于系统调用的结果。这部分程序在执行的时候,系统仍认为是和进行调用的进程属于同一个进程,只是由用户态变成了核心态,并具有此系统调用的用户程序的运行环境,所以可以在其中调用sleep()等与进程运行环境有关的函数。

(3)中断服务子程序,又称为驱动程序的下半部分。在Linux环境中,它与UNIX一样并不是直接从中断向量表中调用设备驱动程序的中断服务子程序,而是由Linux系统来接受硬件中断,再由系统调用中断服务子程序。中断可以在任何一个进程运行的时候产生,因此在中断服务程序被调用的时候,不能依赖于任何进程的状态,也就不能调用任何与进程运行环境有关的函数。因为设备驱动程序一般支持同一类型的若干设备,所以一般在系统调用中断服务子程序的时候,都带有一个或多个参数,以唯一标识服务的设备。6.2嵌入式Linux驱动程序框架根据功能划分,设备驱动程序代码有以下几个部分:

(1)驱动程序的注册与注销;

(2)设备的打开与释放;

(3)设备的读/写操作;

(4)设备的控制操作;

(5)设备的中断和轮询处理。

1.驱动程序的注册与注销设备驱动程序可以在系统启动时初始化,也可以在需要时动态加载。字符设备初始化由chr_dev_init()完成。块设备初始化由blk_dev_init()完成。每个字符设备或块设备的初始化都要通过devfs_

register_chrdev()或devfs_register_blkdev()向内核注册。在关闭字符设备或块设备时,还需要通过devfs_unregister_

chrdev()或devfs_unregister_blkdev()从内核中注销设备。

2.设备的打开与释放打开设备是由open()完成的。open主要完成如下工作:

(1)增加设备的使用计数;

(2)检查设备相关错误,如设备尚未准备好或类似的硬件问题;

(3)如果是首次打开,则初始化设备;

(4)识别次设备号,如有必要更新f_op指针;

(5)如果需要,分配且设置放在filp->private_data里的数据结构。释放设备由release()完成,包括以下几件事:

(1)释放在filp->private_data中open分配的内存;

(2)如果是最后一个释放,则关闭设备;

(3)递减设备的使用计数。

3.设备的读/写操作字符设备使用各自的read()和write()来进行数据读/写。块设备使用通用的generic_file_read()和generic_file_write()来进行数据读/写。这两个通用函数向请求表中添加读/写请求,内核可以通过ll_rw_block()优化请求顺序。由于只是对内存缓冲区而不是对设备进行操作,因而可以加快读/写请求。如果内存缓冲区内没有要读入的数据或者需要将写请求写入设备,那么就需要真正地执行数据传输。这是通过数据结构request_queue中的request_fn来完成的(include/Linux/blkdev.h)。

4.设备的控制操作除了读/写操作,有时还需要控制设备。通过设备驱动程序中的ioctl()完成。ioctl()的用法与具体设备密切相关。除了ioctl(),设备中还有其他控制函数,例如llseek()等。

5.设备的轮询和中断处理对于不支持中断的设备,读/写时需要轮询设备状态,以决定是否继续进行数据传输。如果设备支持中断,则可按照中断方式进行。6.3嵌入式Linux模块化驱动程序设计

1.模块的概念

Linux的内核是一个整体内核(MonolithicKernel),与之对应的是微内核(Microkernel),即所有的内核功能连接在一起,在同一个地址空间执行。但完全这样做会带来很多不便和浪费。如果新添加一个硬件,就需要重新编译内核;如果去掉一个硬件,那么这个硬件已经编译进内核的驱动程序就是浪费。Linux操作系统提供了一种机制,即利用内核模块来解决这个问题。可以根据需要,在不需要重新编译内核的情况下,把模块插入内核或者从内核卸载。整个Linux是一个整体式的内核结构,整个内核是一个单独且非常大的程序。它由五个子系统组成,每个子系统都提供了内部接口的函数和变量。这些函数和变量可供内核所有子系统调用。内核的另外一种形式是微内核结构,此时内核的功能块被划成独立的模块,各部分之间通过严格的通信机制进行联系,给内核增加一个新成分的配置过程非常费时。整个内核并不需要同时装入内存。应该确认,为保证系统能够正常运行,一些特定的内核必须总是驻留在内存中,例如,进程调度代码就必须常驻内存。但是内核其他部分,例如大部分的设备驱动程序就应该仅在内核需要时才加载,而在其他情况下,则无需占用内存。因此,Linux系统提供了一种全新的模块机制,可以根据用户的需求在不需对内核进行重新编译的情况下,在内核中动态加载和卸载模块。模块是内核的一部分且都是设备驱动程序,但它们并没有被编译进内核中,而是被分别编译并链接成一组目标文件。根据需要动态载入模块可以保证内核达到最小,并且有很大的灵活性。

2.模块化的优缺点内核模块的动态加载具有以下优点:将内核映像的尺寸保持在最小,并且有最大的灵活性。这便于检验新的内核代码,而不需要重新编译内核并重新引导。但是,内核模块的引入也带来如下问题:对系统性能和内存利用有负面影响。装入的内核模块与其他内核部分一样,具有相同的访问权限,因此差的内核模块会导致系统崩溃。为了使内核模块能访问所有内核资源,内核必须维护模块符号表,并在加载和卸载模块时修改这些符号表。内核必须能够在卸载模块时通知模块,并且释放分配给模块的内存和中断等资源。内核版本和模块版本的不兼容也可能导致系统崩溃,因此,严格的版本检查是必需的。尽管内核模块的引入同时也带来不少问题,但是模块机制确实是扩充内核功能的一种行之有效的方法,也是在内核级进行编程的有效途径。

3.两个重要函数模块的作用就是扩展内核的功能,运行在内核中模块化的代码。

init_module()函数在模块调入内核(insmod)时被调用,它在内核中注册一定的功能函数(如图6.1中的功能1、功能2、功能3)。在注册之后,如果有程序访问内核的某个功能,如功能1,则内核将查表获得功能1在Moudule中的位置,然后调用功能1的函数。

cleanup_moudule()函数在模块从内核中卸载时被调用,它把init_module()函数在内核中注册的功能函数完全卸载。如果没有完全卸载,在此模块下次调入时,将会因为有重名的函数而导致调入失败。

_init定义的函数将在执行一次后从内存中被删除,并且它请求的内存也将收回。_init定义的函数只在与内核一起编译时才有用,当作为模块时无效。对于_exit定义的函数,当它与内核一起编译时,根本就不会编译进内核;当作为模块编译时,_exit无效。Linux的模块调用如图6.1所示。图6.1Linux的模块调用6.4嵌入式Linux的CAN总线通信6.4.1CAN设备驱动程序设计在嵌入式Linux操作系统下使用CAN总线,必须设计嵌入式Linux上的CAN驱动程序。SJA1000CAN控制器集成了CAN协议的物理层与数据链路层,CAN控制器属于字符型设备。当SJA1000接收一个报文时,数据保存在接收缓存器中,并产生一个接收中断;发送报文时,先将数据送入SJA1000的发送缓冲器中,再将数据串行化发送到CAN总线上。

1. CAN设备驱动程序的处理流程

CAN设备驱动程序中最重要的是中断处理程序。中断处理程序首先是被来自CAN控制器的硬件中断唤起,然后中断处理程序分辨中断类型(发送或接收)。驱动程序处理流程如图6.2所示。如果中断的类型为接收数据,则中断处理例程会调用接收数据处理函数(receive)从CAN控制器读取数据到ReceiveFIFO,最后用户使用系统调用从ReceiveFIFO中读到完整的一帧数据。当中断类型为发送数据时,则中断处理例程调用数据发送处理函数(Transmit),从TransmitFIFO中读取数据送入CAN控制器。图6.2CAN设备驱动程序处理流程

2.收发缓冲区管理收发缓冲区一般都采用环形FIFO队列,使得读/写可以并发执行。每一次读/写操作都要判断队列是否可操作。依据CAN通信协议,CAN控制器每次只接收或发送一帧数据,一标准帧为11个字节,它由1个字节的帧信息(FF、RTR和DLC位)、2个字节的标识位(识别码)、8个字节的数据区组成。所以我们在每次需要内存缓冲区时,直接分配长度为11个字节的数据块,这11个字节地址是线性连续的。在向缓冲区读/写数据时,只要判断一次缓冲区可否操作,并获得块首地址,就可以一次读/写11个字节的数据,减少了重复性条件判断,提高了效率。在CAN设备驱动程序里,设立这样一个CAN_FIFO_buffer数据结构作为收发数据缓冲区:

structCAN_FIFO_buffer{

intstart;

intend;

unsignedchardatabuffer[11*MAX_NR_FRAMES];

}rd_buf;

start=end=0,表示缓冲区为空,end=11*MAX_

NR_FRAMES,表示缓冲区为满。

3.文件操作接口根据系统需求,CAN设备驱动程序实现了部分重要的设备方法,采用标记化格式声明它的file_operations结构如下:

structfile_operationsfops={

owner: THIS_MODULE,

read: can_read,

llseek: can_llseek,

write: can_write,

ioctl: can_ioctl,

open: can_open,

release: can_release,

};

can_read负责从ReceiveFIFO读取数据;can_write负责向TransmitFIFO写入数据;can_release负责关闭CAN控制器;can_ioctl负责向CAN控制器发各种操作命令;can_open负责打开CAN控制器,检查CAN控制器是否打开。

4.驱动程序初始化

1)在内核中注册设备在实现了CAN_fops结构体内的各个入口点函数后,还要编写一个CAN设备驱动程序初始化函数,在内核启动时登记这个驱动程序,并完成CAN控制器的初始化设置。函数register_chrdev是设备驱动程序登记函数,负责向系统注册字符型设备驱动程序,注册形式如下:

ret=register_chrdev(CAN_MAJOR,DEVICE_NAME,&fops);

if(ret<0)

{

printk("%sdevicefailedwith%d\n","Sorry,registeringthecharacter",ret);

returnret;

}参数CAN_MAJOR为CAN的主设备号,内核中一个驱动程序对应一个主设备号;DEVICE_NAME是CAN驱动程序的设备名称;fops是前面定义的file_operations结构体的操作指针。

2)注册中断使用函数request_irq()向系统登记,安装中断处理程序。request_irq()函数的原型如下:

ret=request_irq(IRQ_EXT1,sja1000_can_irq,SA_

INTERRUPT,"can_irq",NULL);

if(ret<0)

{

printk("%sdevicefailedwith%d\n","Sorry,registeringtheIRQ",ret);

returnret;

}其中,参数IRQ_EXT1是CAN驱动程序使用的中断号;sja1000_can_irq是要安装的CAN中断处理函数指针;SA_INTERRUPT表示快速中断;"can_irq"用来在/proc/interrupts中显示中断的拥有者。

3) SJA1000初始化

SJA1000的初始化只有在复位模式下才可以进行。 初始化主要是针对硬件的寄存器进行设置,包括工作方式的设定、接收屏蔽寄存器(AMR)和验收代码寄存器(ACR)的设置、总线时序寄存器的设置、输出模式寄存器和中断寄存器的设置等。初始化设置完成以后,SJA1000就可以进入工作状态,进行正常的通信工作。需要初始化的CAN控制寄存器有:模式寄存器、时分寄存器、接收代码寄存器、屏蔽寄存器、总线定时寄存器和输出控制寄存器等。它们均在InitializeSJA(void)函数中完成。6.4.2CAN总线通信的测试测试程序编写的shell根据输入命令的不同,执行不同的操作。两个开发板的CAN口可以进行正常通信,也可以回环测试。测试的流程如图6.3所示。图6.3CAN测试流程

shell支持的命令如下:● init:初始化SJA1000;● wsja:用于向SJA1000的寄存器写入数据;● rsja:用于读出SJA1000的寄存器的内容;● sfrm:用于发送数据;● gfrm:用于接收数据;● exit:退出程序。

1.测试操作步骤测试操作步骤如下:

~#cd/dev

/dev#mknodcanc1200

//建立DS1337设备

/dev#cd/

~#ifconfigeth072//设置网口eth0

~#tftp-g-rsja1000.o71//从服务器上下载驱动程序

~#tftp-g-rcan_test71//从服务器上下载测试程序

~#chmod777can_test//修改程序的运行权限

~#./can_test//运行实践表明,SJA1000是一种较好的CAN总线控制器件,它的抗干扰性能极好,通信速率较高,电路也比较简单。在PeliCAN模式下,其识别码可达29位,因而可满足各种应用场合。在自动化控制系统中,合理安排这29位识别码可以使许多问题得以简化。PeliCAN模式增加的各种功能,大大地方便了通信的调试。

2.举例

1) help命令测试之前,可用help命令,显示如下:

myshell>help

init--initthesja

dein--deinitthesja

rsja--readfromthesja

wsja--writetothesja

gfrm--gettheframe

sfrm--sendtheframe

conf--configthefilter

help--getcommandinformation

exit--exittheshell

quit--quitfromtheshell

2) init命令初始化SJA1000

myshell>init

initsjasucceeded

3)回环测试回环测试之前,需要设置SJA1000的模式寄存器。首先设置模式寄存器为复位模式,然后再设置为自检测模式。测试代码如下:

myshell>wsja

pleasetellmetheaddressyouwanttowriteto:

Theaddressis:0

ok,youraddressis0

pleasetellmethevalueyouwanttowrite:

Thevalueis:1

ok,yourvaluwrite_sja_reg_addr=0

eis1

write_sja_reg_value=1

ok,youwrote1into0

myshell>wsja

pleasetellmetheaddressyouwanttowriteto:

Theaddressis:0

ok,youraddressis0

pleasetellmethevalueyouwanttowrite:

Thevalueis:4

ok,yourvaluwrite_sja_reg_addr=0

eis4

write_sja_reg_value=4

ok,youwrote4into0

myshell>rsja

pleasetellmetheaddressyouwanttoreadfrom:

Theaddressis:0

enterread

sja_reg_addr=0

sja_reg_value=4

usr_sja_reg_value=4

ok,yourdatareadfrom0is4发送数据:8,0,0,1,1,1,1,1,1,1,1

myshell>sfrm

wewillsendsomefrmames

pleaseinputcommand.formatis:

8addr0addr1d0d1d2d3d4d5d6d7

8,0,0,1,1,1,1,1,1,1,1

xx--xx--xx--[0]=8xx--xx--xx

[1]=0[2]=0[3]=1[4]=1[5]=1[6]=1[7]=1[8]=1[9]=1[10]=1

xxxxxxxxxxxx

TXdata[0]=8TXdata[1]=0TXdata[2]=0TXdata[3]=1TXdata[4]=1TXdata[5]=1TXdata[6]=1TXdata[7]=1TXdata[8]=1TXdata[9]=1TXdata[10]=1[0]=8[1]=0[2]=0[3]=1[4]=1[5]=1[6]=1[7]=1[8]=1[9]=1[10]=1

myshell>gfrm

wewillgetsomefrmames

readin1frames

8--0--0--1--1--1--1--1--1--1--1–

4)两个CAN口通信用一般导线(可用双绞线、光纤)连接两个CAN口,CANH1与CANH1相连接,CANL1与CANL1相连接。SJA1000处于工作模式,一个开发板发数据,另一个开发板收数据,若相互交换收发数据正确无误,则表示通信正常。6.5嵌入式Linux的IIC总线通信6.5.1IIC总线概述

1.IIC总线特点

IIC总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10kb/s的最大传输速率支持40个组件。IIC总线的另一个优点是,它支持多主控(multimastering),其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

2.IIC总线工作原理

IIC总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率为400 kb/s。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程中,IIC总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂在同一条总线上,却彼此独立,互不相关。

IIC总线在传送数据过程中共有3种类型信号,它们分别是:开始信号、结束信号和应答信号。●开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。●结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。●应答信号:接收数据的IC在接收到8 bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,则判断受控单元出现故障。

3.IIC总线的硬件实现在嵌入式系统中,IIC总线的硬件实现有3种:

(1)利用微处理器的两个I/O口模拟SDA和SCL,用软件实现SDA和SCL上的信号;

(2)微处理器本身带有IIC总线硬件电路,只对微处理器的相应寄存器写入数据就实现IIC总线;

(3)微处理器的其他总线(如SPI或ISA)上接IIC总线专用芯片。

EP9315的IIC总线属于第一种情况,在IIC总线上接有DS1337实时时钟芯片和MAX5821数模转换芯片。6.5.2IIC总线驱动程序的设计

1.DS1337实时时钟原理图

DS1337实时时钟芯片原理图如图6.4所示。图6.4DS1337实时时钟连接图

2.文件操作接口根据系统需求,DS1337设备驱动程序实现了部分重要的设备方法,采用标记化格式声明它的file_operations结构如下:

structfile_operationsds1337_fops={

owner: THIS_MODULE,

read: ds1337_read,

write: ds1337_write,

open: ds1337_open,

release: ds1337_release,

};

ds1337_write负责对DS1337写命令控制字;DS1337_open负责打开DS1337,检查DS1337是否打开;ds1337_release负责关闭DS1337。

ds1337_write函数中的WriteI2Creg()是对IIC总线的具体操作,参看IIC总线原理。6.5.3DS1337驱动程序测试在测试DS1337时钟时,以动态模块方式加载。用Tftp把DS1337的驱动程序ds1337.c的可执行文件、测试程序iic_test.c的可执行文件下载到开发板上运行,可用下面的命令运行:

~#cd/dev

/dev#mknodds1337c1210//建立DS1337设备

/dev#cd/

~#ifconfigeth072//设置网口eth0

~#tftp-g-rds1337.o71 //从服务器上下载驱动程序

~#tftp-g-riic_test71//从服务器上下载测试程序

~#chmod777iic_test//修改程序的运行权限

~#./iic_test

//运行

StartTestDS1337

DoyouwanttoResetTime(Y/N)y//设置时间

year(00-99):07

month(1-12):1date(1-31):19week(1-7):5hour(0-23):16minute(0-59):1second(0-59):0显示信息:Yourinputstringis=711951610Nowis19-Jan-200716:1:0Friday0Nowis19-Jan-200716:1:1Friday1在IIC总线的应用中应注意的事项总结为以下几点:

(1)严格按照时序图的要求进行操作;

(2)若与口线上带内部上拉电阻的单片机接口连接,可以不外加上拉电阻;

(3)程序中为配合相应的传输速率,在对口线操作的命令后可用udelay加一定的延时。6.6嵌入式Linux的D/A转换6.6.1MAX5821驱动程序设计

1. MAX5821原理图

MAX5821连接原理图如图6.5所示。图6.5MAX5821硬件连接图

2.文件操作接口根据系统需求,D/A转换设备驱动程序实现了部分重要的设备方法,采用标记化格式声明它的file_operations结构如下:

structfile_operationsmax5821_fops={ owner: THIS_MODULE, read: max5821_read, write: max5821_write, open: max5821_open, release: max5821_release,

};

max5821_write负责对MAX5821写命令控制字;MAX5821_open负责打开MAX5821,检查MAX5821是否打开;MAX5821_release负责关闭MAX5821。因为IIC总线是用GPIO口模拟实现的,所以模拟时钟频率最高达220 kHz,而操作MAX5821,需要写3次控制字,才完成1次数据转换。完成1次的频率为7kHz左右,操作时序如图6.6所示。第1个字节为器件地址和读/写标志位;第2个字节为控制字(C3C2C1C0)和待转换数据的高四位;第3个字节为待转换数据的低六位。

C3C2C1C0分别表示以下含义:

0000:LoadDACAinputandDACregisterswithnewdata.ContentsofDACBinputregistersaretransferredtotheDACregister.Alloutputsareupdated.

0001:LoadDACBinputandDACregisterswithnewdata.ContentsofDACAinputregistersaretransferredtotheDACregister.Alloutputsareupdated.

0100:LoadDACAinputregisterwithnewdata.DACoutputsremainunchanged.

0101:LoadDACBinputregisterwithnewdata.DACoutputsremainunchanged.

1000:DatainallinputregistersistransferredtorespectiveDAC

registers.AllDACoutputsareupdatedsimultaneously.NewdataisloadedintoDACAinputregister.

1001:DatainallinputregistersistransferredtorespectiveDACregisters.AllDACoutputsareupdatedsimultaneously.NewdataisloadedintoDACBinputregister.

1100:LoadallDACswithnewdataandupdateallDACoutputssimultaneously.BothinputandDACregistersareupdatedwithnewdata.

1101:Loadallinputregisterswithnewdata.DACoutputsremainunchanged.写操作的时序如图6.6所示。需要写3次控制字,才可完成1次数据转换,IIC总线模拟操作参见IIC总线原理。图6.6写操作时序6.6.2MAX5821驱动程序测试驱动程序测试的方法如下:

(1) A口作为脉宽调制输出(由于模拟IIC的限制,频率有如下要求):Dutyfactor为占空比,Frequencymaximum为频率的最大值。

Dutyfactor=0.1****Frequencymaximum=650 Hz

Dutyfactor=0.2****Frequencymaximum=1300 Hz

Dutyfactor=0.3****Frequencymaximum=2000 Hz

Dutyfactor=0.4****Frequencymaximum=2650 Hz

Dutyfactor=0.5****Frequencymaximum=3300 Hz

(2) B口作为普通模拟输出。输入需要转换的值(0~1023),即完成转换。测试程序流程如图6.7所示。图6.7测试流程图在测试MAX5821数/模转换时,以动态模块方式加载。用Tftp把MAX5821的驱动程序max5821.c的可执行文件max5821.o、测试程序da_test.c的可执行文件da_test下载到开发板上运行,可用下面的命令运行:

~#ifconfigeth072 //设置网口eth0

~#mknod/dev/max5821c1230 //建立MAX5821设备节点

~#tftp-g-rmax5821.o71//从服务器上下载驱动程序

~#tftp-g-rda_test71 //从服务器上下载测试程序

~#chmod777da_test//修改测试程序的运行权限

~#insmodmax5821.o//加载驱动程序

~#./da_test6.7嵌入式Linux的A/D转换6.7.1MAX1261驱动程序设计

1. A/D转换驱动程序设计

1)实现MAX1261的file_operations结构

structfile_operationsmax1261_fops={

read:read_max1261,

write:write_max1261,

ioctl:ioctl_max1261,

open:open_max1261,

release:release_max1261,

};

2)中断服务子程序在中断服务子程序中,读出MAX1261转换后的数据,因为是八位数据线,且是12位的数据,所以读两次。先置低HBEN,读取低八位;后置高,读取高四位。程序代码如下:

voidmax1261_irq(intirq,void*dev_id,structpt_regs*regs) {charbuffer[2]={0};

max1261_read(buffer);

in_rd_buf(buffer);

}

voidmax1261_read(char*data)

{

intuiPADR;

uiPADR=inl(GPIO_PADR)&0xfe;//outdatais0:egpio0HBEN=0,读取低八位

outl(uiPADR,GPIO_PADR);

data[0]=readb(AD_DATA_BASE);

uiPADR=inl(GPIO_PADR)|0x01;//outdatais1:egpio0HBEN=1,读取高四位

outl(uiPADR,GPIO_PADR);

data[1]=readb(AD_DATA_BASE)&0xf;

}

3)初始化初始化时,首先设置EGPIO0为输出,EGPIO9为输入,再配置EGPIO9为低电平触发。6.7.2MAX1261驱动程序测试测试时,还需要对MAX1261进行配置,确定它的模拟输入方式。测试流程如图6.8所示。图6.8A/D测试程序流程图(1)图6.8A/D测试程序流程图(2)

1.操作步骤进行测试时运行如下命令:

~#mknod/dev/max1261c1220

//设备文件下设置结点

~#ifconfigeth072

//设置网口eth0

~#tftp-g-rmax1261.o71//从服务器上下载驱动程序

~#tftp-g-rad_test71//从服务器上下载测试程序

~#chmod777ad_test

//修改程序的运行权限

~#./ad_test

//运行

2.支持的命令● config1:用于配置MAX1261工作的模式。

0:完全掉电模式,时钟模式无影响;

1:备用掉电模式,时钟模式无影响;

2:正常工作模式,内部时钟模式;

3:正常工作模式,外部时钟模式。● config2:用于配置MAX1261的模拟输入模式。

0:伪差分模拟输入模式;

1:单端模拟输入模式。● config3:用于配置MAX1261的极性。

0:双极性;

1:单极性。● config4:用于配置MAX1261的通道。● startin:用于使MAX1261开始转换的命令,且采用内部捕捉模式。● startex:用于使MAX1261开始转换的命令,且采用外部捕捉模式。● exitadc:用于退出测试程序。

3. 举例具体A/D测试操作如下:

#ifconfigeth072

//设置网口eth0

~#tftp-g-rmax1261.o27 //从服务器上下载驱动程序

~#tftp-g-rad_test27 //从服务器上下载测试程序

~#chmod777ad_test //改变测试程序权限

~#mknod/dev/max1261c1220 //建立设备结点

~#insmodmax1261.o //加载驱动Usingmax1261.oSMCBCR3=0x2000ffe1SMCBCR3=0xffefSYSCON_CLKSET1=0x2a4e39eGPIO_PBDR=0xffcongraulation,max1261aresucGPIO_PBDR=0xff~#./ad_testDAC:max1261deviceopenmyshell>config40channel0isselected.myshell>startin10thereceivedvalueis0x1,0.000610myshell>startin100thereceivedvalueis0x0,0.000000myshell>●查看MAX1261产生的中断次数,如下所示:

~#cat/proc/interrupts

4:30927timer

17:

0ide0

31:

0graphics

39:167ep93xx_ethernet

52:836uart1

56:0usb-ohci

58:0FPU

59:110max1261_irq

Err:0

max1261_irq中断号59产生了110次中断。6.8嵌入式Linux的PS/2键盘6.8.1键盘工作原理键盘的处理器花费很长的时间来扫描或监视按键矩阵,如果它发现有键被按下、释放或按住,键盘将发送扫描码的信息到计算机。扫描码有通码和断码两种不同的类型,当一个键被按下或按住时,就发送通码;当一个键被释放时,就发送断码。每个按键被分配了唯一的通码和断码,这样主机通过查找唯一的扫描码,就可以测定是哪个按键。每个键的通断码组成了扫描码集,有3套标准的扫描码集。所有现代的键盘默认使用第2套扫描码。只要键一按下通码就会被发送,每个键都有它自己唯一的通码。它们也都有唯一的断码,在通码和断码之间存在着必然的联系。多数第二套断码有两字节长,它们的第1个字节是F0h,第2个字节是这个键的通码。部分扩展按键的断码通常有3个字节,它们的前两个字节是E0h和F0h,最后一个字节是这个按键通码的最后一个字节,见表6.1。按下按键“A”,发送到计算机的数据应该是1Ch,F0h,1Ch。如果按下按键Shift和2,输出@,发到计算机的数据应该是12h,1Eh,F0h,1Eh,F0h,12h。表6.1键码转换表6.8.2PS/2模块硬件连接本系统中,扩展PS/2口为键盘接口,支持标准键盘输入。PS/2的传输通过数据线K-DATA和时钟线K-CLK来完成,与SPI复用。对应的硬件连接图如图6.9所示。

PS/2向主机发送键值通过SPI的接收线、时钟线传送给主机,主机向PS/2发送命令通过EGPIO[14]和[12]实现。运行程序时,需要将对应4个跳线接通,保证PS/2键盘和主机通信正常,配置超级终端以观测按键值。图6.9PS/2键盘连接图6.8.3键盘驱动程序的设计程序开始运行后,等待接收PS/2键盘输入的按键值。当有按键按下时,从寄存器SSPDR中读取按键值,进行判断。如果不是Esc按键,则调用Translate函数,将对应的扫描码转换为对应的字符并输出;如果按下按键Esc,则退出程序。在Translate函数中,首先将SPI数据线接收进来的11位标准数据通过反相器还原成键盘的输入值;其次,从11位SPI数据中提取出8位数据位,去掉起始位、校验位等;最后进行键值转换处理,相关代码如下。

b=~a; /*将数据通过取反还原*/

c=((b&0x004)<<5); /*从11位SPI数据中提取出8位数据位*/

c|=((b&0x008)<<3);

c|=((b&0x010)<<1);

c|=((b&0x020)>>1);

c|=((b&0x040)>>3);

c|=((b&0x080)>>5);

c|=((b&0x100)>>7);

c|=((b&0x200)>>9);

if((c<0x87)&&(prep!=0xf0)) /*键值是否是标准的128键盘值?是否是断码值?*/

{if(c==0x58) /*如果是cap大小写转换键,则计数器加1*/

N++;

if(prep==0x12) /*前一个按键是Shift键,则输出复合值*/

{printk("1%c\n",KScan1[c]);}

if((c!=0x58)&&(prep!=0x12)) /*如果是普通按键*/

{if((N%2==1)&&('A'<=KScan[c])&&(KScan[c]<='Z'))/*判断大小写,默认为大写*/

printk("2%c\n",KScan[c]+32);

elseprintk("3%c\n",KScan[c]);

}

}

prep=c; /*更新前一个标志位*/

}对应流程图如图6.10所示。图6.10驱动设计流程图6.8.4测试步骤

(1)开发板Uart1和PC机COM1通过串口连接,同时通过网线连接。

(2)开启超级终端,建立名为EP9315的新连接,波特率配置为115200,8位数据位,无奇偶校验位,1位停止位,无数据流控制。

(3)开启PC机上的Tftp服务,config配置下载路径到对应的驱动(.o文件)及测试程序(test)存放路径如D:\MyDocuments,d:\MyDocuments\ps2.o),然后打开超级终端。

(4)在tmp路径下创建名为PS2的节点,主设备号为117,次设备号为0:

~# cd/dev

/dev# mknodps2c1170

(5)配置开发板的IP地址,与主机保证在同一网段内即可:

/dev# cd/

~# ifconfigeth026

~# cd/tmp

(6)通过Tftp命令,将驱动及测试程序传到开发板tmp目录下:

/tmp# tftp-g-rps2.o25

/tmp# tftp-g-rps2test25

(7)加载驱动程序,将控制权限转至测试程序ps2test,运行测试程序:

/tmp# insmodps2.o

/tmp# chmod777ps2test

/tmp# ./ps2test根据提示,从PS/2键盘输入任意按键进行测试,然后按Esc键,退出测试。为了调试方便,字母前添加数字3表示大写字母,2表示小写字母,1表示复合字母。测试完成后,调用rmmodps2卸载驱动即可。6.8.5调试中的注意事项

PS/2键盘不能热插拔,开发板上电时,必须保证PS/2键盘已经连上,键盘所需的5 V、3.3 V电压需在开发板上电时一起提供给键盘。6.9嵌入式Linux的8255驱动6.9.18255芯片原理

8255是8路并行输出芯片,具有3个8位端口PORTA、PORTB和PORTC。其中,各个端口可单独设置其输入/输出状态。输出时,通过开发板向对应端口写入8位数据即可向外输出;输入时程序读取对应端口可得到外部发送的8位数据。其具体控制字将在下文结合开发板实际连接介绍。本开发板上留有8路并行输入/输出接口,由8255芯片实现。其电路原理图如图6.11所示。图6.11电路原理图

此芯片接在BANKCS3上,片选地址连接如图6.12所示。图6.12BANK选通连接图当向地址0x30800000上写入对应数据时,即可选通8255芯片,对其各端口进行对应操作。

A、B、C端口对应地址为:

PORTA:0x30800000

PORTB:0x30800001

PORTC:0x30800002状态控制字寄存器地址为:

CONTROL:0x30800003本驱动试验程序将其作如下设置:

PORTA、PORTB为输出;PORTC为输入。芯片工作方式为0工作方式,对应的8位控制字为0x89。由于ARM各BANK默认采用32位传输方式,而8255是8位工作芯片。所以,在使用8255芯片时应将BANKCS3设置为8位宽度。这一点在程序开始时向地址0x8008000c处写入0x0000ffef即可。6.9.2Linux下8255驱动的实现本试验驱动主要通过ioctl函数完成驱动对各端口状态的控制以及向各端口输入/输出数据。驱动程序流程如图6.13所示。图6.13驱动程序流程在针对此驱动的测试程序中外接测试电路, 由PORTA端口控制8盏LED灯。连接PORTB与PORTC,PORTB输出数据,再由PORTC读取回显在超级终端中。测试程序中调动内核驱动程序的基本函数为

fd=open("/dev/port8255",O_RDWR);设置端口工作方式函数为

ioctl(fd,1,0x89);操作A、B、C端口的函数为

ioctl(fd,0,arg);测试流程如图6.14所示。图6.14测试流程图以上就是8255试验驱动程序的基本情况介绍。注意:①在驱动程序中,不能直接操作物理地址空间。因此,需使用地址映射函数:

addr=ioremap(0x30800000,4)其中,分配到的addr即为以0x30800000开始的连续4个地址空间的首地址。②关于8255设置上的其他信息,请参阅资料中附带的8255数据手册。6.10嵌入式Linux的PWM驱动6.10.1PWM控制器介绍

EP9315芯片内带有两个独立的PWM控制器,通过软件设置输出频率及占空比,可以输出不同的脉宽调制波形。在内部寄存器中,主要通过PWMtermcnt及PWMdutycycle来设置频率及占空比。换算公式如下:

PWMTermCnt=PWMDutyCycle = (期望占空比×(PWMTermCnt+1))-1;

PWM计算实例如下:要在66  MHz(15 nsec)的系统频率下产生一个100kHz(10μsec)、20%占空比的PWM输出。

(1) PWMxTermCnt=(66MHz/0.1MHz)-1=695(十进制);

(2) PWMxDutyCycle=(0.2×(695+1))-1=131(十进制)6.10.2PWM驱动设计

Linux下驱动实现流程如图6.15所示。图6.15驱动实现流程通过调用该驱动,即可产生要求的PWM波形。在对该驱动的验证程序中,我们用PWM波形驱动外接蜂鸣器以产生旋律。验证程序流程如图6.16所示。图6.16测试流程图当外接蜂鸣器奏出音乐时,说明此驱动可用,工作正常。注意:①应设置蜂鸣器的频率在人耳的听觉接收范围(20Hz~20 kHz)。②在程序中设置频率及占空比时,应遵循如下规则:当欲设置频率大于当前运行频率时,先设置频率而后设置占空比;当欲设置频率小于当前运行频率时,先设置占空比而后设置频率。6.11嵌入式Linux的串口驱动

EP93XX系列的ARM9微控制器均具有3个UART,它们的结构及寄存器符合16C550工业标准。16C550是一种工业标准的UART,此类UART芯片内部集成了可编程的波特率发生器、发送和接收FIFO、处理中断系统和各种总线状态错误检测电路等,并具有完全的MODEM控制能力。工作模式为全双工模式,支持5~8位的数据长度,具有1/2位停止位,可选奇偶校验位。6.11.1UART串行口数据传输格式异步串行方式是将传输数据的每个字符一位接一位(例如先低位、后高位)地传送的方式。数据的各不同位可以分时使用同一传输通道,因此串行I/O可以减少信号连线,最少用一对线即可进行。接收方对于同一根线上一连串的数字信号,首先要分割成位,再按位组成字符。为了恢复发送的信息,双方必须协调工作。在微型计算机中大量使用异步串行I/O方式,双方使用各自的时钟信号,而且允许时钟频率有一定误差,因此实现较容易。但是由于每个字符都要独立确定起始和结束(即每个字符都要重新同步),字符和字符间还可能有长度不定的空闲时间,因此效率较低。串行通信的字符格式如图6.17所示。图6.17串行通信的字符格式图6.17给出了异步串行通信中一个字符的传送格式。开始前,线路处于空闲状态,送出连续“1”。传送开始时首先发一个“0”作为起始位,然后出现在通信线上的是字符的二进制编码数据。每个字符的数据位长可以约定为5位、6位、7位或8位,一般采用ASCII编码。后面是奇偶校验位,根据约定,用奇偶校验位将所传字符中为“1”的位数凑成奇数个或偶数个。也可以约定不要奇偶校验,这样就取消奇偶校验位。最后是表示停止位的“1”信号,这个停止位可以约定持续1位或2位的时间宽度。至此一个字符传送完毕,线路又进入空闲,持续为“1”。经过一段随机的时间后,在下一个字符开始传送时才又发出起始位。每一个数据位的宽度等于传送波特率的倒数。在微机异步串行通信中,常用的波特率为50,95,110,150,300,600,1200,2400,4800,9600等。接收方按约定的格式接收数据,并进行检查,可以查出以下三种错误:

(1)奇偶错。在约定奇偶检查的情况下,接收到的字符奇偶状态和约定不符。

(2)帧格式错。一个字符从起始位到停止位的总位数不对。

(3)溢出错。若先接收的字符尚未被微机读取,后面的字符又传送过来,则产生溢出错。以上每一种错误都会给出相应的出错信息,提示用户处理。6.11.2EP9315的UART串口传输波特率

EP9315片内外设各模块需要外部晶振来提供工作时钟,外部晶振的时钟频率为14.7456 MHz,记作Fosc=

14.7456 MHz。在EP9315内部有一个锁相环电路,可以把外部晶振频率提升到处理器和所有外设的工作频率。处理器的工作频率记作Fclk。EP9315片内外设可分为AHB设备和APB设备,AHB设备的工作频率记作Hclk,APB设备的工作频率记作Pclk。锁相环的倍频工作可以通过设置寄存器ClkSet1、ClkSet2来完成,ClkSet1用于给第一个锁相环通道PLL1编程, ClkSet2用于给第二个锁相环通道PLL2编程。 图6.18以PLL1为例,说明Fosc的倍频过程。图6.18外部晶振的倍频原理Fout的计算公式为其中,PLL1_X1FBD、PLL1_X2FBD、PLL1_X2IPD、PLL1_PS的取值可以在寄存器ClkSet1中进行设置。第一个锁相环通道PLL1主要用于对处理器工作频率(Fclk)和总线设备工作频率(Hclk、Pclk)进行管理。通过设置SetClk1寄存的PLL1_X1FBD、PLL1_X2FBD、PLL1_X2IPD、PLL1_PS位可以得到PLL1的输出频率PLL1Fout,通设置SetClk1寄存器的FCLKDIV位可以选择PLL1Fout到Fclk的除数因子。通过设置SetClk1寄存器的HCLKDIV位可以选择PLL1Fout到Hclk的除数因子。通过设置SetClk1寄存器的PCLKDIV位可以选择HCLK到PCLK的除数因子。Fclk、Hclk、Pclk设置时应满足以下关系:

Fclk<=200 MHz;

Hclk<=100 MHz;

Pclk<=50 MHz;

Pclk<Hclk<=Fclk。第二个锁相环通道PLL2主要用于对USB设备模块和FastIrDA(FIR)模块的工作频率进行管理。另外,还可以选择使外部晶振不经过锁相环PLLs,而直接为片内外设提供工作时钟,例如用于串口通信的UART模块就可通过设置系统控制寄存器的PwrCnt的UARTBAUD位选择使其工作在Fosc或Fosc/2。各个设备模块工作频率的分配如图6.19所示。图6.19EP9315各设备模块的时钟分配串口通信的正常进行,是依靠收发双方使用相同的传输率来进行同步的,图6.20说明了传输/发送波特率的产生原理。图6.20UART模块传输波特率的产生原理以上是接收和发射信号的时钟频率产生的原理图,其中Baudratedivisor是一个16bits的二进制数,其数值由寄存器UART1LinCtrlMid和UART1LinCtrlLow共同决定。Baud16的周期是UARTCLK周期的Baudratedivisor倍,而Baudrate的周期又是Baud16周期的16倍。因此根据需要的Baudrate和UARTCLK就可计算出Baudratedivisor的值,计算公式如下:

DAUDDIV=-1例如,假设通过设置EP9315系统控制寄存器PwrCnt的位UARTBAUD使得UART模块的工作频率为14.7456 MHz

(直接使用外部晶振),即FUARTCLK=14.7456 MHz。同时需要串口的传输波特率为57600,则我们可以通过上式计算得到DAUDDIV=15。从而设置寄存器UART1LinCtrlMid

=0x00,UART1LinCtrlLow=0x0F。6.11.3EP9315的UART串口模块及其工作原理

UART模块的工作原理如图6.21所示。图6.21UART模块的工作原理

TransmitFIFO: 大小为16B,是一种先进先出的缓存,每一字节用来存放等待发送的5~8bits的信息数据。TransmitFIFO也可被禁用,从而仅作为1个8bit的寄存器使用。

Transmitterlogic:在数据传输过程中扮演并-串变换的角色(数据在EP9315内部为并行传输,EP9315和外部通信为串行传输)。从而Transmitterlogic输出的串行字符数据的结构依次为:起始位(startbit)、数据位(databit)、奇偶校验位(paritybit)、停止位(stopbit)。

ReiceiveFIFO:由16个11  bits的存储单元构成,在每一个11  bit的存储单元中,8  bit用于存放数据信息,3  bit用于存放状态信息。通过Receivelogic接收到的数据信息以及传输过程中产生的错误比特(errorbit)被存放在ReceiveFIFO中,然后被CPU通过APBinterface或DMAinterface读出。同样,ReceiveFIFO可被禁用作为一个一个字节的寄存器使用。

Receivelogic:在一个有效的开始脉冲被检测到后,Receivelogic将接收到的信息流进行串-并变换。Parity、frameerrorchecking以及breakerrorbits同样被进行串-并变换,并被存储到ReceiveFIFO中。对于发送数据情况而言,要发射的数据信息被写入TransmitFIFO,形成一个数据帧,这个要发送的数据帧以在寄存器UART1LinCtrlHigh中定义的格式为准。寄存器UART1Flag中的BUSY信号在TansmitFIFO中有数据被写入或有数据被发射的情况下都为高,只有TransmitFIFO中为空时,BUSY信号才被撤销。对于接收数据情况而言,当Receiver是无效状态时(这时UARTRxD为持续的高电平),若在数据输入端(UARTRxD)已检测到低电平,说明一个数据帧的startbit已被接收。这时受Baud16信号驱动的Receivecounter开始计数,并且在Baud16的第8个周期上(也就是1/2的Baudrate周期)进行数据采样;如果在Baud16的第8个周期上UARTRxD仍为低电平,则说明startbit有效,否则startbit无效;如果startbit已被检测到是有效的,那么每16个Baud16周期(一个Baudrate周期)就会采样一次数据信息,根据寄存器UART1LinCtrlHigh中定义的每一

温馨提示

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

评论

0/150

提交评论