《ARM9嵌入式系统的设计基础教程第7至13章课件》由会员分享,可在线阅读,更多相关《ARM9嵌入式系统的设计基础教程第7至13章课件(443页珍藏版)》请在金锄头文库上搜索。
1、第第7章章嵌入式系统网络接口嵌入式系统网络接口7.1以太网接口以太网接口n嵌入式系统通常使用的以太网协议是IEEE802.3标准。从硬件的角度看,802.3模型层间结构如图7.1.1所示,以太网接口电路主要由媒质接入控制MAC控制器和物理层接口(PhysicalLayer,PHY)两大部分构成。图7.1.1802.3模型层间结构n1传输编码传输编码n在802.3版本的标准中,没有采用直接的二进制编码(即用0V表示“0”,用5V表示“1”),而是采用曼彻斯特编码(ManchesterEncoding)或者差分曼彻斯特编码(DifferentialManchesterEncoding),不同编码形
2、式如图7.1.2所示。图7.1.2不同编码形式n其中:曼彻斯特编码的规律是:每位中间有一个电平跳变,从高到低的跳变表示为“0”,从低到高的跳变表示为“1”。n差分曼彻斯特编码的规律是:每位的中间也有一个电平跳变,但不用这个跳变来表示数据,而是利用每个码元开始时有无跳变来表示“0”或“1”,有跳变表示“0”,无跳变表示“1”。n曼彻斯特编码和差分曼彻斯特编码相比,前者编码简单,后者能提供更好的噪声抑制性能。在802.3系统中,采用曼彻斯特编码,其高电平为+0.85V,低电平信号为-0.85V,这样指令信号电压仍然是0V。n2802.3Mac层的帧层的帧n802.3Mac层的以太网的物理传输帧如表
3、7.1.1所示。n表7.1.1802.3帧的格式nPR:同步位,用于收发双方的时钟同步,同时也指明了传输的速率,是56位的的二进制数101010101010,最后2位是10。SD:分隔位,表示下面跟着的是真正的数据而不是同步时钟,为8位的10101011。DA:目的地址,以太网的地址为48位(6个字节)二进制地址,表明该帧传输给哪个网卡。如果为FFFFFFFFFFFF,则是广播地址。广播地址的数据可以被任何网卡接收到。SA:源地址,48位,表明该帧的数据是哪个网卡发的,即发送端的网卡地址,同样是6个字节。nTYPE:类型字段,表明该帧的数据是什么类型的数据,不同协议的类型字段不同。如:0800
4、H表示数据为IP包,0806H表示数据为ARP包,814CH是SNMP包,8137H为IPX/SPX包。小于0600H的值是用于IEEE802的,表示数据包的长度。nDATA:数据段,该段数据不能超过1500B。因为以太网规定整个传输包的最大长度不能超过1514E(14B为DA,SA,TYPE)。nPAD:填充位。由于以太网帧传输的数据包最小不能小于60B,除去(DA、SA、TYPE的14B),还必须传输46B的数据,当数据段的数据不足46B时,后面通常是补0(也可以补其他值)。nFCS:32位数据校验位。32位的CRC校验,该校验由网卡自动计算,自动生成,自动校验,自动在数据段后面填入。不需
5、要软件管理。n通常,PR、SD、PAD、FCS这几个数据段都是网卡(包括物理层和Mac层的处理)自动产生的,剩下的DA、SA、TYPE、DATA这4个段的内容是由上层的软件控制的。n3以太网数据传输的特点以太网数据传输的特点n所有数据位的传输由低位开始,传输的位流是用曼彻斯特编码。n以太网是基于冲突检测的总线复用方法,冲突退避算法是由硬件自动执行的。n以太网传输的数据段的长度,DA+SA+TYPE+DATA+PAD最小为60B,最大为1514B。n通常的以太网卡可以接收3种地址的数据,一个是广播地址,一个是多播地址(或者叫组播地址,在嵌入式系统中很少用到),一个是它自己的地址。但有时,用于网络
6、分析和监控,网卡也可以设置为接收任何数据包。n任何两个网卡的物理地址都是不一样的,是世界上唯一的,网卡地址由专门机构分n配。不同厂家使用不同地址段,同一厂家的任何两个网卡的地址也是唯一的。根据网卡的地址段(网卡地址的前3个字节)可以知道网卡的生产厂家。n7.1.2嵌入式以太网接口的实现方法n在嵌入式系统中增加以太网接口,通常有如下两种方法实现:n(1)嵌入式处理器网卡芯片n这种方法只要把以太网芯片连接到嵌入式处理器的总线上即可。此方法通用性强,对嵌入式处理器没有特殊要求,不受处理器的限制,但是,嵌入式处理器和网络数据交换通过外部总线(通常是并行总线)交换数据,速度慢,可靠性不高,电路板走线复杂
7、。目前常见的以太网接口芯片,如CS8900、RTL8019/8029/8039、DM9008及DWL650无线网卡等。n(2)带有以太网接口的嵌入式处理器n带有以太网接口的嵌入式处理器通常是面向网络应用而设计的,要求嵌入式处理器有通用的网络接口(比如:MII接口),处理器和网络数据交换通过内部总线,速度快。n7.1.3在嵌入式系统中主要处理的以太网协议nTCP/IP是一个分层的协议,包含有用于层、传输层、网络层、数据链路层、物理层等。每一层实现一个明确的功能,对应一个或者几个传输协议。每层相对于它的下层都作为一个独立的数据包来实现。典型的分层和每层上的协议如表7.1.2所示。n表7.1.2TC
8、P/IP协议的典型分层和协议n1ARP(AddressResolationProtocol,地址解析协议),地址解析协议)n网络层用32位的地址来标识不同的主机(即IP地址),而链路层使用48位的物理(MAC)地址来标识不同的以太网或令牌环网接口。只知道目的主机的IP地址并不能发送数据帧给它,必须知道目的主机网络接口的物理地址才能发送数据帧。nARP的功能就是实现从IP地址到对应物理地址的转换。源主机发送一份包含目的主机IP地址的ARP请求数据帧给网上的每个主机,称作ARP广播,目的主机的ARP收到这份广播报文后,识别出这是发送端在询问它的IP地址,于是发送一个包含目的主机IP地址及对应的物理
9、地址的ARP回答给源主机。n为了加快ARP协议解析的数据,每台主机上都有一个ARPcache存放最近的IP地址到硬件地址之间的映射记录。其中每一项的生存时间(一般为20分钟),这样当在ARP的生存时间之内连续进行ARP解析的时候,不需要反复发送ARP请求了。n2ICMP(InternetControlMessagesProtocol,网络控制,网络控制报文协议)报文协议)nICMP是IP层的附属协议,IP层用它来与其他主机或路由器交换错误报文和其他重要控制信息。ICMP报文是在IP数据包内部被传输的。在Linux或者Windows中,两个常用的网络诊断工具ping和traceroute(Win
10、dows下是Tracert),其实就是ICMP协议。n3IP(InternetProtocol,网际协议),网际协议)nIP工作在网络层,是TCP/IP协议族中最为核心的协议。所有的TCP、UDP、ICMP及IGMP数据都以IP数据包格式传输(IP封装在IP数据包中)。IP数据包最长可达65535字节,其中报头占32位。还包含各32位的源IP地址和32位的目的IP地址。nTTL(time-to-live,生存时间字段)指定了IP数据包的生存时间(数据包可以经过的最多路由器数)。TTL的初始值由源主机设置,一旦经过一个处理它的路由器,它的值就减去1。当该字段的值为0时,数据包就被丢弃,并发送IC
11、MP报文通知源主机重发。nIP提供不可靠、无连接的数据包传送服务,高效、灵活。n不可靠(unreliable)的意思是它不能保证IP数据包能成功地到达目的地。如果发生某种错误,IP有一个简单的错误处理算法:丢弃该数据包,然后发送ICMP消息报给信源端。任何要求的可靠性必须由上层来提供(如TCP)。n无连接(connectionless)的意思是IP并不维护任何关于后续数据包的状态信息。每个数据包的处理是相互独立的。IP数据包可以不按发送顺序接收。如果一信源向相同的信宿发送两个连续的数据包(先是A,然后是B),每个数据包都是独立地进行路由选择,可能选择不同的路线,因此B可能在A到达之前先到达。n
12、IP的路由选择:源主机IP接收本地TCP、UDP、ICMP、GMP的数据,生成IP数据包,如果目的主机与源主机在同一个共享网络上,那么IP数据包就直接送到目的主机上。否则就把数据包发往一默认的路由器上,由路由器来转发该数据包。最终经过数次转发到达目的主机。IP路由选择是逐跳(hop-by-hop)进行的。所有的IP路由选择只为数据包传输提供下一站路由器的IP地址。n4TCP(TransferControlProtocol,传输控制协议),传输控制协议)nTCP协议是一个面向连接的可靠的传输层协议。TCP为两台主机提供高可靠性的端到端数据通信。它所做的工作包括:n发送方把应用程序交给它的数据分成
13、合适的小块,并添加附加信息(TCP头),包括顺序号,源、目的端口,控制、纠错信息等字段,称为TCP数据包。并将TCP数据包交给下面的网络层处理。n接受方确认接收到的TCP数据包,重组并将数据送往高层。n5UDP(UserDatagramProtocol,用户数据包协议),用户数据包协议)nUDP协议是一种无连接不可靠的传输层协议。它只是把应用程序传来的数据加上UDP头(包括端口号,段长等字段),作为UDP数据包发送出去,但是并不保证它们能到达目的地。可靠性由应用层来提供。n因为协议开销少,和TCP协议相比,UDP更适用于应用在低端的嵌入式领域中。很多场合如网络管理SNMP,域名解析DNS,简单
14、文件传输协议TFTP,大都使用UDP协议。n6.端口端口nTCP和UDP采用16位的端口号来识别上层的TCP用户,即上层应用协议,如FTP和TELNET等。常见的TCP/IP服务都用众所周知的1255之间的端口号。例如FTP服务的TCP端口号都是21,Telnet服务的TCP端口号都是23。TFTP(简单文件传输协议)服务的UDP端口号都是69。2561023之间的端口号通常都是提供一些特定的UNIX服务。TCP/IP临时端口分配10245000之间的端口号。n7.1.4网络编程接口nBSD套接字(BSDSockets)使用的最广泛的网络程序编程方法,主要用于应用程序的编写,用于网络上主机与主
15、机之间的相互通信。n很多操作系统都支持BSD套接字编程。例如,UNIX、Linux、VxWorks、Windows的Winsock基本上是来自BSDSockets。n套接字(Sockets)分为StreamSockets和DataSockets。StreamSockets是可靠性的双向数据传输,对应使用TCP协议传输数据;DataSockets是不可靠连接,对应使用UDP协议传输数。n下面给出一个使用套接字接口的UDP通信的流程。nUDP服务器端和一个UDP客户端通信的程序过程:n(1)创建一个Socket:nsFd=socket(AF_INET,SOCK_DGRAM,0)n(2)把Socke
16、t和本机的IP,UDP口绑定:nbind(sFd,(structsockaddr*)&serverAddr,sockAddrSize)n(3)循环等待,接收(recvfrom)或者发送(sendfrom)信息。n(4)关闭Socket,通信终止:nclose(sFd)n7.1.5以太网的物理层接口及编程n大多数ARM都内嵌一个以太网控制器,支持媒体独立接口(MediaIndependentInterfaceMII)和带缓冲DMA接口(BufferedDMAInterface,BDI),可在半双工或全双工模式下提供10M/100Mbps的以太网接入。在半双工模式下,控制器支持CSMA/CD协议;
17、在全双工模式下,支持IEEE802.3MAC控制层协议。ARM内部虽然包含了以太网MAC控制,但并未提供物理层接口,因此,需外接一片物理层芯片以提供以太网的接入通道。n常用的单口10M/100Mbps高速以太网物理层接口器件均提供MII接口和传统7线制网络接口,可方便地与ARM接口。以太网物理层接口器件主要功能一般包括:物理编码子层、物理媒体附件、双绞线物理媒体子层、10BASE-TX编码解码器和双绞线媒体访问单元等。如CS8900、RTL8019/8029/8039等。nCS8900A是CirrusLogic公司生产的16位以太网控制器,芯片内嵌片内RAM10BASE-T收发滤波器,直接IS
18、A总线接口。该芯片的物理层接口、数据传输模式和工作模式等都能根据需要而动态调整,通过内部寄存器的设置来适应不同的应用环境。nCS8900A采用3V供电电压,最大工作电流55mA,具有全双工通信方式,可编程发送功能,数据碰撞自动重发,自动打包及生成CRC校验码,可编程接收功能,自动切换于DMA和片内RAM,提前产生中断便于数据帧预处理,数据流可降低CPU消耗,自动阻断错误包,可跳线控制EEPROM功能,启动编程支持无盘系统,边沿扫描和回环测试,待机和睡眠模式,支持广泛的软件驱动,工业级温度范围,LED指示连接状态和网络活动情况等特点。采用TQFP-100封装。CS8900A内部结构方框图如图7.
19、1.3所示。图7.1.3CS8900A内部结构方框图n1CS8900A工作原理工作原理nCS8900A有两种工作模式:和I/O模式。当配置成MEMORYMODE模式操作时,CS8900A的内部寄存器和帧缓冲区映射到主机内存中连续的4KB的块中,主机可以通过这个块直接访问CS8900A的内部寄存器和帧缓冲区。MEMORY模式需要硬件上多根地址线和网卡相连。而在I/OMODE模式,对任何寄存器操作均要通过I/O端口0写入或读出。I/OMODE模式在硬件上实现比较方便,而且这也是芯片的默认模式。在I/O模式下,PacketPage存储器被映射到CPU的8个16位的I/O端口上。在芯片被加电后,I/O
20、基地址的默认值被置为300H。n使用CS8900A作为以太网的物理层接口,在收到由主机发来的数据报后(从目的地址域到数据域),侦听网络线路。如果线路忙,它就等到线路空闲为止,否则,立即发送该数据帧。在发送过程中,首先它添加以太网帧头(包括前导字段和帧开始标志),然后生成CRC校验码,最后将此数据帧发送到以太网上。n在接收过程中,它将从以太网收到的数据帧在经过解码、去帧头和地址检验等步骤后缓存在片内。在CRC校验通过后,它会根据初始化配置情况,通知主机CS8900A收到了数据帧,最后,用某种传输模式(FO模式、Memory模式、DMA模式)传到主机的存储区中。n2CS8900A引脚端和功能引脚端
21、和功能nCS8900A的ISA总线接口引脚端和功能如表7.1.3所示,EEPROM和引导编程接口引脚端和功能如表7.1.4所示,IOBASE-T接口引脚端和功能如表7.1.5所示,附加单元接口AUD引脚端和功能如表7.1.6所示,通用引脚端和功能如表7.1.7所示。引脚类型功能SA0:19I地址总线SD0:15I/O双向数据总线,三态输出RESETI复位输入端,高电平有效(至少保持400ns)AENI地址使能,高电平有效MEMRI存储器读信号,低电平有效MEMWI存储器写信号,低电平有效MEMCS16O存储器16位选择信号,OC(集电极开路)输出REFRESHI刷新信号,低电平有效。当REFR
22、ESH为低电平时,MEMR,MEMW,IOR,IOW,DMACK0,DMACKl和DMACK2都被忽略表7.1.3ISA总线接口引脚端和功能IORII/O读信号,低电平有效IOWII/O写信号,低电平有效IOCS16I16位I/O片选信号,低电平有效IOCHRDYOI/O通道就绪信号,OC(集电极开路)输出SBHEI系统总线高位使能信号,低电平有效INTRQ0:2O中断请求信号,三态输出DMARQ0:2ODMA请求信号,三态输出DMACK0:2IDMA应答信号,低电平有效CHIPSELI片选信号,低电平有效表7.1.4EEPROM和引导编程接口引脚端和功能引脚类型功能EESKIEEPROM时钟
23、输入信号EECSIEEPROM片选输入信号,低电平有效EEDataINIEEPROM数据输入,内部上拉ELCSI外部逻辑片选信号,内部上拉EEDataOUTOEEPROM数据输出CSOUTO外部引导编程选择信号输出,低电平有效表7.1.5IOBASE-T接口引脚端引脚类型功能TXD+/TXDO数据发送,差分对管输出RXD/RXDI数据接收,差分对管输入表7.1.6附加单元接口引脚端和功能引脚类型功能DO/DOOAUI数据输出,差分对管输出DI/DIIAUI数据输入,差分对管输入CI/CIIAUI振动输入,差分对管输入表7.1.7通用引脚端和功能引脚类型功能XTAL1:2I/O晶体振荡器输入输出
24、SLEEPI硬件睡眠控制输入信号,低电平有效,内部上拉LINKLED/HCOO线路正常输出信号或主控制器输出0信号,低电平有效,OC(集电极开路)输出BSTAUTS/HC1O总线状态输出信号或主控制器输出1信号,低电平有效,OC(集电极开路)输出LANLEDO网络状态指示输出信号,OC(集电极开路)输出TESTI测试输入使能信号,低电平有效,内部上拉RESI基准电阻输入端DVDD1:4I数字电路电源DVSS1:4I数字电路地AVDD1:4I模拟电路电源AVSS1:4I模拟电路地3电路连接电路连接采用CS8900A与S3C2410A连接构成的以太网接口电路如图7.1.4所示。n4CS8900A的
25、以太网接口驱动程序的以太网接口驱动程序于明于明n(1)初始化函数n初始化函数完成设备的初始化功能,由数据结构device中的init函数指针来调用。加载网络驱动模块后,就会调用初始化过程。首先通过检测物理设备的硬件特征来检测网络物理设备是否存在,之后配置设备所需要的资源。比如,中断。这些配置完成之后就要构造设备的数据结构device,用检测到的数据初始化device中的相关变量,最后向Linux内核中注册该设备并申请内存空间。函数定义为:nstaticint_initinit_cs8900a_s3c2410(void)nnstructnet_local*lp;nintret=0;ndev_cs
26、89x0.irq=irq;ndev_cs89x0.base_addr=io;ndev_cs89x0.init=cs89x0_probe;ndev_cs89x0.priv=kmalloc(sizeof(structnet_local),GFP_KERNEL);nif(dev_cs89x0.priv=0)nnprintk(KERN_ERRcs89x0.c:Outofmemory.n);nreturn-ENOMEM;nnmemset(dev_cs89x0.priv,0,sizeof(structnet_local);nnlp=(structnet_local*)dev_cs89x0.priv;nre
27、quest_region(dev_cs89x0.base_addr,NETCARD_IO_EXTENT,cs8900a);nspin_lock_init(&lp-lock);n/*boy,theydbettergettheseright*/nif(!strcmp(media,rj45)nlp-adapter_cnf=A_CNF_MEDIA_10B_T|A_CNF_10B_T;nelseif(!strcmp(media,aui)nlp-adapter_cnf=A_CNF_MEDIA_AUI|A_CNF_AUI;nelseif(!strcmp(media,bnc)nlp-adapter_cnf=A
28、_CNF_MEDIA_10B_2|A_CNF_10B_2;nelsenlp-adapter_cnf=A_CNF_MEDIA_10B_T|A_CNF_10B_T;nif(duplex=1)nlp-auto_neg_cnf=AUTO_NEG_ENABLE;nif(io=0)nprintk(KERN_ERRcs89x0.c:Moduleautoprobingnotallowed.n);nprintk(KERN_ERRcs89x0.c:Appendio=0xNNNn);nret=-EPERM;ngotoout;nnif(register_netdev(&dev_cs89x0)!=0)nprintk(K
29、ERN_ERRcs89x0.c:Nocardfoundat0x%xn,io);nret=-ENXIO;ngotoout;nnout:nif(ret)nkfree(dev_cs89x0.priv);nreturnret;nn在这个网络设备驱动程序中,设备的数据结构device就是dev_cs89x0。探测网络物理设备是否存在,利用cs89x0_probe函数实现,通过调用register_netdrv(structnet_device*dev)函数进行注册。n与init函数相对应的cleanup函数在模块卸载时运行,主要完成资源的释放工作,如取消设备注册、释放内存、释放端口等。函数定义为:nst
30、aticvoid_exitcleanup_cs8900a_s3c2410(void)nif(dev_cs89x0.priv!=NULL)n/*Freeuptheprivatestructure,orleakmemory:-)*/nunregister_netdev(&dev_cs89x0);noutw(PP_ChipID,dev_cs89x0.base_addr+ADD_PORT);nkfree(dev_cs89x0.priv);ndev_cs89x0.priv=NULL;/*getsre-allocatedbycs89x0_probe1*/n/*Ifwedontdothis,wecantre
31、-insmoditlater.*/nrelease_region(dev_cs89x0.base_addr,NETCARD_IO_EXTENT);nnn(2)打开函数n打开函数在网络设备驱动程序中是在网络设备被激活时调用,即设备状态由down至up。函数定义为:nstaticintnet_open(structnet_device*dev)nnstructnet_local*lp=(structnet_local*)dev-priv;nintret;nwritereg(dev,PP_BusCTL,readreg(dev,PP_BusCTL)&ENABLE_IRQ);nret=request_i
32、rq(dev-irq,&net_interrupt,SA_SHIRQ,cs89x0,dev);nif(ret)nprintk(%s:request_irq(%d)failedn,dev-name,dev-irq);ngotobad_out;nnif(lp-chip_type=CS8900)nwritereg(dev,PP_CS8900_ISAINT,0);nelsenwritereg(dev,PP_CS8920_ISAINT,0);nwritereg(dev,PP_BusCTL,MEMORY_ON);nlp-linectl=0;nwritereg(dev,PP_LineCTL,nreadreg
33、(dev,PP_LineCTL)|SERIAL_RX_ON|SERIAL_TX_ON);nlp-rx_mode=0;nwritereg(dev,PP_RxCTL,DEF_RX_ACCEPT);nlp-curr_rx_cfg=RX_OK_ENBL|RX_CRC_ERROR_ENBL;nif(lp-isa_config&STREAM_TRANSFER)nlp-curr_rx_cfg|=RX_STREAM_ENBL;nwritereg(dev,PP_RxCFG,lp-curr_rx_cfg);nwritereg(dev,PP_TxCFG,nTX_LOST_CRS_ENBL|TX_SQE_ERROR_
34、ENBL|TX_OK_ENBL|nTX_LATE_COL_ENBL|TX_JBR_ENBL|nTX_ANY_COL_ENBL|TX_16_COL_ENBL);nwritereg(dev,PP_BufCFG,nREADY_FOR_TX_ENBL|RX_MISS_COUNT_OVRFLOW_ENBL|nTX_COL_COUNT_OVRFLOW_ENBL|TX_UNDERRUN_ENBL);nwritereg(dev,PP_BusCTL,readreg(dev,PP_BusCTL)|ENABLE_IRQ);nenable_irq(dev-irq);nnetif_start_queue(dev);nD
35、PRINTK(1,cs89x0:net_open()succeededn);nreturn0;nbad_out:nreturnret;nn打开函数中对寄存器操作使用了两个函数:readreg和writereg。readreg函数用来读取寄存器内容,writereg函数用来写寄存器。函数定义为:ninlineintreadreg(structnet_device*dev,intportno)nnoutw(portno,dev-base_addr+ADD_PORT);nreturninw(dev-base_addr+DATA_PORT);nninlinevoidwritereg(structnet
36、_device*dev,intportno,intvalue)nnoutw(portno,dev-base_addr+ADD_PORT);noutw(value,dev-base_addr+DATA_PORT);nn(3)关闭函数n关闭函数释放资源减少系统负担,设备状态有up转为down时被调用。函数定义为:nstaticintnet_close(structnet_device*dev)nnnetif_stop_queue(dev);nwritereg(dev,PP_RxCFG,0);nwritereg(dev,PP_TxCFG,0);nwritereg(dev,PP_BufCFG,0);n
37、writereg(dev,PP_BusCTL,0);nfree_irq(dev-irq,dev);n/*Updatethestatisticshere.*/nreturn0;nn(4)发送函数n首先,在网络设备驱动加载时,通过device域中的init函数指针调用网络设备的初始化函数对设备进行初始化,如果操作成功,就可以通过device域中的open函数指针调用网络设备的打开函数打开设备,再通过device域中的包头函数指针hard_header来建立硬件包头信息。最后,通过协议接口层函数dev_queue_xmit调用device域中的hard_start_xmit函数指针来完成数据包的发送
38、。n如果发送成功,hard_start_xmit释放sk_buff,返回0。如果设备暂时无法处理,比如,硬件忙,则返回l。此时如果dev-tbusy置为非0,则系统认为硬件忙,要等到dev-tbusy置0以后才会再次发送。tbusy的置0任务一般由中断完成。硬件在发送结束会产生中断,这时可以把tbusy置0,然后用mark_bh()调用通知系统可以再次发送。n在CS8900A驱动程序中,网络设备的传输函数dev-hard_start_xmit定义为net_send_packet:nstaticintnet_send_packet(structsk_buff*skb,structnet_devi
39、ce*dev)nnstructnet_local*lp=(structnet_local*)dev-priv;nwritereg(dev,PP_BusCTL,0x0);nwritereg(dev,PP_BusCTL,readreg(dev,PP_BusCTL)|ENABLE_IRQ);nDPRINTK(3,%s:sent%dbytepacketoftype%xn,ndev-name,skb-len,n(skb-dataETH_ALEN+ETH_ALENdataETH_ALEN+ETH_ALEN+1);nspin_lock_irq(&lp-lock);nnetif_stop_queue(dev)
40、;n/*initiateatransmitsequence*/nwriteword(dev,TX_CMD_PORT,lp-send_cmd);nwriteword(dev,TX_LEN_PORT,skb-len);n/*Testtoseeifthechiphasallocatedmemoryforthepacket*/nif(readreg(dev,PP_BusST)&READY_FOR_TX_NOW)=0)nnspin_unlock_irq(&lp-lock);nDPRINTK(1,cs89x0:Txbuffernotfree!n);nreturn1;nn/*Writethecontents
41、ofthepacket*/nwriteblock(dev,skb-data,skb-len);nspin_unlock_irq(&lp-lock);ndev-trans_start=jiffies;ndev_kfree_skb(skb);nreturn0;nn(5)中断处理和接收函数n网络设备接收数据通过中断实现,当数据收到后,产生中断,在中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来,填充sk_buff中的一些信息。处理完后,如果是获得数据包,则执行数据接收子程序,该函数被中断服务程序调用。函数定义:nstaticvoidnet_rx(s
42、tructnet_device*dev)nnstructnet_local*lp=(structnet_local*)dev-priv;nstructsk_buff*skb;nintstatus,length;nintioaddr=dev-base_addr;nstatus=inw(ioaddr+RX_FRAME_PORT);nif(status&RX_OK)=0)nncount_rx_errors(status,lp);nreturn;nnlength=inw(ioaddr+RX_FRAME_PORT);n/*Mallocupnewbuffer.*/nskb=dev_alloc_skb(le
43、ngth+2);nif(skb=NULL)nnlp-stats.rx_dropped+;nreturn;nnskb_reserve(skb,2),/*longwordalignL3header*/nskb-len=length;nskb-dev=dev;nreadblock(dev,skb-data,skb-len);nDPRINTK(3,%s:received%dbytepacketoftype%xn,ndev-name,length,n(skb-dataETH_ALEN+ETH_ALENdataETH_ALEN+ETH_ALEN+1);nskb-protocol=eth_type_tran
44、s(skb,dev);nnetif_rx(skb);ndev-last_rx=jiffies;nlp-stats.rx_packets+;nlp-stats.rx_bytes+=length;nn在net_rx()函数中调用netif_rx()把数据传送到协议层。netif_rx()函数把数据放入处理队列,然后返回,真正的处理是在中断返回以后,这样可以减少中断时间。调用netif_rx()后,驱动程序不能再存取数据缓冲区skb。netif_rx()函数在net/core/dev.c中定义为:nintnetif_rx(structsk_buff*skb)nnintthis_cpu=smp_pro
45、cessor_id();nstructsoftnet_data*queue;nunsignedlongflags;nif(skb-stamp_sec=0)ndo_gettimeofday(&skb-stamp);nqueue=&softnet_datathis_cpu;nlocal_irq_save(flags);nnetdev_rx_statthis_cpu.total+;nif(queue-input_pkt_queue.qleninput_pkt_queue.qlen)nnif(queue-throttle)gotodrop;nenqueue:ndev_hold(skb-dev);n_s
46、kb_queue_tail(&queue-input_pkt_queue,skb);ncpu_raise_softirq(this_cpu,NET_RX_SOFTIRQ);nlocal_irq_restore(flags);n#ifndefOFFLINE_SAMPLEnget_sample_stats(this_cpu);n#endifnreturnsoftnet_datathis_cpug_level;nnif(queue-throttle)nnqueue-throttle=0;n#ifdefCONFIG_NET_HW_FLOWCONTROLnif(atomic_dec_and_test(&
47、netdev_dropping) netdev_wakeup();n#endifnngotoenqueue;nnif(queue-throttle=0)nnqueue-throttle=1;nnetdev_rx_statthis_cpu.throttled+;n#ifdefCONFIG_NET_HW_FLOWCONTROLnatomic_inc(&netdev_dropping);n#endifnndrop:netdev_rx_statthis_cpu.dropped+;nlocal_irq_restore(flags);nkfree_skb(skb);nreturnNET_RX_DROP;n
48、n中断函数net_interrupt在打开函数中申请,中断发生后,首先驱动中断管脚为高电平,然后主机读取CS8900A中的中断申请序列ISQ值,以确定事件类型,根据事件类型做出响应。函数定义为:nstaticvoidnet_interrupt(intirq,void*dev_id,structpt_regs*regs)nnstructnet_device*dev=dev_id;nstructnet_local*lp;nintioaddr,status;nioaddr=dev-base_addr;nlp=(structnet_local*)dev-priv;nnwhile(status=read
49、word(dev,ISQ_PORT)nnDPRINTK(4,%s:event=%04xn,dev-name,status);nswitch(status&ISQ_EVENT_MASK)nncaseISQ_RECEIVER_EVENT:n/*Gotapacket(s).*/nnet_rx(dev);nbreak;ncaseISQ_TRANSMITTER_EVENT:nlp-stats.tx_packets+;nnetif_wake_queue(dev);/*Informupperlayers.*/nif(status&(TX_OK|nTX_LOST_CRS|TX_SQE_ERROR|nTX_LA
50、TE_COL|TX_16_COL)!=TX_OK)nnif(status&TX_OK)=0)lp-stats.tx_errors+;nif(status&TX_LOST_CRS)lp-stats.tx_carrier_errors+;nif(status&TX_SQE_ERROR)lp-stats.tx_heartbeat_errors+;nif(status&TX_LATE_COL)lp-stats.tx_window_errors+;nif(status&TX_16_COL)lp-stats.tx_aborted_errors+;nnbreak;ncaseISQ_BUFFER_EVENT:
51、nif(status&READY_FOR_TX)nnnetif_wake_queue(dev);/*Informupperlayers.*/nnif(status&TX_UNDERRUN)nnDPRINTK(1,%s:transmitunderrunn,dev-name);nlp-send_underrun+;nif(lp-send_underrun=3)lp-send_cmd=TX_AFTER_381;nelseif(lp-send_underrun=6)lp-send_cmd=TX_AFTER_ALL;nnetif_wake_queue(dev);/*Informupperlayers.*
52、/nnbreak;ncaseISQ_RX_MISS_EVENT:nlp-stats.rx_missed_errors+=(status6);nbreak;ncaseISQ_TX_COL_EVENT:nlp-stats.collisions+=(status6);nbreak;nnn7.2CAN总线接口总线接口n7.2.1CAN总线概述nCAN(ControllerAreaNetwork,控制器局域网)是德国Bosch公司于1983年为汽车应用而开发的,它是一种现场总线(FieldBus),能有效支持分布式控制和实时控制的串行通信网络。1993年11月,ISO正式颁布了控制器局域网CAN国际标准
53、(IS011898)。n一个理想的由CAN总线构成的单一网络中可以挂接任意多个节点,实际应用中节点数目受网络硬件的电气特性所限制。例如:当使用PhilipsP82C250作为CAN收发器时,同一网络中允许挂接110个节点。CAN可提供1Mb/s的数据传输速率。CAN总线是一种多主方式的串行通信总线。基本设计规范要求有高的位速率,高抗电磁干扰性,并可以检测出产生的任何错误。当信号传输距离达到10Km时CAN总线仍可提供高达50Kb/s的数据传输速率。CAN总线具有很高的实时性能,已经在汽车工业、航空工业、工业控制、安全防护等领域中得到了广泛应用。nCAN总线的通信介质可采用双绞线、同轴电缆和光导
54、纤维,最常用的是双绞线。通信距离与波特率有关,最大通信距离可达10km,最大通信波特率可达1Mbps。CAN总线仲裁采用11位标识和非破坏性位仲裁总线结构机制,可以确定数据块的优先级,保证在网络节点冲突时最高优先级节点不需要冲突等待。CAN总线采用了多主竞争式总线结构,具有多主站运行和分散仲裁的串行总线以及广播通信的特点。CAN总线上任意节点可在任意时刻主动向网络上其他节点发送信息而不分主次,因此可在各节点之间实现自由通信。nCAN总线信号使用差分电压传送,两条信号线被称为CAN_H和CAN_L,静态时均是2.5V左右,此时状态表示为逻辑1,也可以叫做“隐性”。采用CAN_H比CAN_L高表示
55、逻辑0,称为“显性”,通常电压值为CAN_H=3.5V和CAN_L=1.5V。当“显性”位和“隐性”位同时发送的时候,最后总线数值将为“显性”。nCAN总线的一个位时间可以分成四个部分:同步段,传播时间段,相位缓冲段1和相位缓冲段2。每段的时间份额的数目都是可以通过CAN总线控制器编程控制,而时间份额的大小tq由系统时钟tsys和波特率预分频值BRP决定:tq=BRP/tsys。图7.2.1说明了CAN总线的一个位时间的各个组成部分。图7.2.1CAN总线的一个位时间n同步段:用于同步总线上的各个节点,在此段内期望有一个跳变沿出现(其长度固定)。如果跳变沿出现在同步段之外,那么沿与同步段之间的
56、长度叫做沿相位误差。采样点位于相位缓冲段1的末尾和相位缓冲段2开始处。n传播时间段:用于补偿总线上信号传播时间和电子控制设备内部的延迟时间。因此,要实现与位流发送节点的同步,接收节点必须移相。CAN总线非破坏性仲裁规定,发送位流的总线节点必须能够收到同步于位流的CAN总线节点发送的显性位。n相位缓冲段1:重同步时可以暂时延长。n相位缓冲段2:重同步时可以暂时缩短。n同步跳转宽度:长度小于相位缓冲段。n同步段,传播时间段,相位缓冲段1和相位缓冲段2的设定和CAN总线的同步、仲裁等信息有关。其主要思想是要求各个节点在一定误差范围内保持同步。必须考虑各个节点时钟(振荡器)的误差和总线的长度带来的延迟
57、(通常每米延迟为5.5ns)。正确设置CAN总线各个时间段,是保证CAN总线良好工作的关键。n7.2.2在嵌入式处理器上扩展CAN总线接口n一些面向工业控制的嵌入式处理器本身就集成了一个或者多个CAN总线控制器。例如:韩国现代公司的hms30c7202(ARM720T内核)带有两个CAN总线控制器;Phillips公司的LPC2194和LPC2294(ARM7TDMI内核)带有4个CAN总线控制器。CAN总线控制器主要是完成时序逻辑转换等工作,要在电气特性上满足CAN总线标准,还需要一个CAN总线的物理层芯片,用它来实现TTL电平到CAN总线电平特性的转换,即CAN收发器。n实际上,多数嵌入式
58、处理器都不带CAN总线控制器。通常的解决方案是在嵌入式处理器的外部总线上扩展CAN总线接口芯片,例如:Phillips公司的SJA1000CAN总线接口芯片,Microchip公司的MCP251x系列(MCP2510和MCP2515)CAN总线接口芯片,这两种芯片都支持CAN2.0B标准。SJA1000的总线采用的是地址线和数据线复用的方式,多数嵌入式处理器采用SJA1000扩展CAN总线较为复杂。nMCP2510是由Microchip公司生产的CAN协议控制器,完全支持CAN总线V2.OA/B技术规范。08字节的有效数据长度,支持远程帧;最大1Mb/s的可编程波特率;两个支持过滤器Filte
59、r,Mask)的接收缓冲区,三个发送缓冲区;支持回环(LoopBack)模式,便于测试;SPI高速串行总线,最大5MHz;3V到5.5V供电。nMCP2510主要由CAN协议引擎,用来为器件及其运行进行配置的控制逻辑,SRAM寄存器和SPI协议模块3部分组成。MCP2510支持CANT2、CAN2.0A、主动和被动CAN2.0B等版本的协议,能够发送和接收标准和扩展报文,还同时具备验收过滤以及报文管理功能。MCP2510包含三个发送缓冲器和两个接收缓冲器,减少了处理器(CPU)的管理负担。CPU的通信是通过行业标准串行外设接口(SPI)来实现的,其数据传输速率高达5Mbps。nCPU通过SPI
60、接口与器件进行通信。通过使用标准SPI读写命令对寄存器进行所有读写操作。器件上有一个多用途中断引脚以及各接收缓冲器专用的中断引脚,可用于指示有效报文是否被接收和载入各接收缓冲器。是否使用专用中断引脚由用户决定,若不使用,也可用通用中断引脚和状态寄存器(通过SPI接口访问)确定有效报文是否已被接收。n1CAN协议引擎协议引擎nCAN协议引擎的功能是处理所有总线上的报文发送和接收。报文发送时,首先将报文装载到正确的报文缓冲器和控制寄存器中。利用控制寄存器位、通过SPI接口或使用发送使能引脚均可启动发送操作。通过读取相应的寄存器可以检查通信状态和错误。任何在CAN总线上侦测到的报文都会进行错误检测,
61、然后与用户定义的滤波器进行匹配,以确定是否将其转移到两个接收缓冲器之一中。nCAN协议引擎的核心是有限状态机(FSM)。该状态机逐位检查报文,当各个报文帧发生数据字段的发送和接收时,状态机改变状态。FSM确保了报文接收、总线仲裁、报文发送以及错误信号发生等操作过程依据CAN总线协议进行。总线上报文的自动重发送也由FSM处理。n2CAN报文帧报文帧nMCP2510支持CAN2.0B技术规范中所定义的标准数据帧、扩展数据帧以及远程帧(标准和扩展),详细的描述请登录microchip,查阅MCP2510数据手册。n3寄存器映射表寄存器映射表nMCP2510寄存器映射表如表7.2.1所示。通过使用行(
62、低4位)列(高4位)值可对映射表中的寄存器地址进行确定。寄存器的地址排列优化了寄存器数据的顺序读写。一些特定控制和状态寄存器允许使用SPI位修改命令进行单独位的设定。可以使用位修改命令对表7.2.1中的阴影部分的寄存器进行位修改操作。n4SPI接口接口nMCP2510可以与许多微控制器的串行外设接口(SPI)直接相连,支持0,0和1,1运行模式。外部数据和命令通过SI引脚传送到器件中,而数据在SCK时钟信号的上升沿传送进去。MCP2510在SCK下降沿通过SO引脚发送。MCP2510SPI指令如表7.2.3所示。有关0,0和1,1运行模式详细的输入输出时序请登录microchip,查阅MCP2
63、510数据手册。nCANSPI接口函数为:nunsignedcharCAN_SPI_CMD(unsignedcharcmd,unsignedlongaddr,unsignedcharargl,unsignedchararg2)n其中cmd表示指令名称,addr为寄存器地址,argl和arg2为可选的参数。ncmd为SPI_CMD_READ时,将读取addr地址的寄存器值;argl和arg2没有使用。ncmd为SPI_CMD_WRITE时,将往addr地址的寄存器写argl值,arg2没有使用。ncmd为SPI_CMD_RTS时,将发送RTS请求,argl和arg2没有使用。ncmd为SPI_C
64、MD_READSTA时,将读取MCP2510的状态,并返回该状态。ncmd为SPI_CMD_BITMOD时,将对addr地址的寄存器进行位修改。位修改命令提供了一种对特定控制和状态寄存器中单独的位进行设定和清除的方法。argl为屏蔽字节,arg2为数据字节。屏蔽字节决定寄存器中的哪一位将被修改。屏蔽字节中的“1”表示允许对寄存器相应的位进行修改,0则禁止修改。数据字节确定寄存器位修改后的最终结果。如图7.2.3所示,如果屏蔽字节相应位设置为1,数据字节中的1表示将对寄存器对应位置1,而0则将对该位清零。ncmd为SPI_CMD_RESET时,将发送复位指令,复位指令为单字节指令,可以重新初始化
65、nMCP2510的内部寄存器,并设置配置模式。它一般在期间上电初始化过程中进行。图7.2.3MCP2510位修改指令n5报文发送报文发送nCAN报文发送函数为:nvoidMCP2510_TX(intTxBuf,intIdType,unsignedintid,intDataLen,Char*data)nMCP2510采用三个发送缓冲器。通过TxBuf参数指定发送到哪个缓冲器(TXBUF0、TXBUF1或TXBUF2。IdType决定发送报文帧的类型,STAN-DID表示标准数据帧,EXTID表示扩展数据帧。Id为帧ID,DataLen为待发送数据长度,必须小于等于8,data为待发送数据内容。n
66、6报文接收报文接收nCAN报文接收函数为:nvoidMCP2510_RX(intRxBuf,int*IdType,unsignedint*id,int*DataLen,char*data)nMCP2510具有两个全文接收缓冲器。通过RxBuf参数指定从哪个缓冲器接收armcchelp可以查看armcc的语法格式以及最常用的一些操作选项。n(2)armcppnarmcpp是ARMC+编译器。它将ISOC+或EC+编译成32位ARM指令代码。n(3)tccntcc是ThumbC编译器。该编译器通过了PlumHallCValidationSuite为ANSI一致性的测试。tcc将ANSIC源代码编译
67、成16位的Thumb指令代码。ADS开发工具集(4)tcppntcpp是ThumbC+编译器。它将ISOC+和EC+源码编译成16位Thumb指令代码。n(5)armsmnarmsm是ARM和Thumb的汇编器.它对用ARM汇编语言和Thumb汇编语言写的源代码进行汇编。n(6)armlinknarmlink是ARM连接器。n(7)armsdnarmsd是ARM和Thumb的符号调试器。ADS开发工具集n2GUI开发环境开发环境nADSGUI开发环境主要包含CodeWarrior和AXD,其中CodeWarrior是用于编译和链接的集成开发工具,而AXD则是支持单步执行、断点设置等功能的集成调
68、试工具。n(1)CodeWarriornCodeWarriorforARM是一套完整的集成开发工具,充分发挥了ARMRISC指令系统的优势,使产品开发人员能够很好的应用尖端的片上系统技术.该工具是专为基于ARMRISC的处理器而设计的。ADS开发工具集n(2)AXDnAXD调试器本身是一个软件,用户通过这个软件可以对包含有调试信息的、正在运行的可执行代码进行变量的查看、断点的设置、单步执行等调试操作。在ARM体系中,它有Multi-ICE、ARMulator和Angel等几种方式。AXD可以在Windows和UNIX下进行程序的调试,它为用C、C+和汇编语言的源代码提供了一个全面的Window
69、s和UNIX环境。ADS开发工具集n3、实用程序实用程序nADS提供以下的实用工具来配合前面介绍的命令行开发工具的使用。nfromELF是ARM映像文件转换工具。narmar是ARM库函数生成器。nFlashdownloader用于把二进制映像文件下载到ARM嵌入式设备上的Flash存储器中。ARM汇编伪指令n在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的操作码,也就是不会生成机器码,仅仅是在编译器软件中起着格式化的作用,通常称这些特殊指令助记符为伪指令。伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作用,一旦汇
70、编结束,伪指令的使命就完成。在ARM的汇编程序中,有如下几种伪指令:数据常量定义伪指令、数据变量定义伪指令、内存分配伪指令及其他伪指令。1数据常量定义伪指令n数据常量定义伪指令EQU用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言中的#define。EQU语法格式:名称EQU表达式,类型;其中EQU可用“*”代替。名称为EQU伪指令定义的字符名称,当表达式为32位的常量时,可以指定表达式的数据类型,可以有以下三种类型:CODE16、CODE32和DATA。2数据变量定义伪指令(Cont.)n数据变量定义伪指令用于定义ARM汇编程序中的变量、对变量赋值以及定义寄存器的别名等操作。常
71、见的数据变量定义伪指令有如下几种:(1)GBLA、GBLL和GBLS语法格式:GBLA(GBLL或GBLS)全局变量名GBLA、GBLL和GBLS伪指令用于定义全局变量,并将其初始化。其中:GBLA用于定义一个全局的数字变量,并初始化为0;GBLL用于定义一个全局的逻辑变量,并初始化F(假);nGBLS用于定义一个全局的字符串变量,并初始化为空;2数据变量定义伪指令(Cont.)n(2)LCLA、LCLL和LCLS语法格式:LCLA(LCLL或LCLS)局部变量名LCLA、LCLL和LCLS伪指令用于定义一个ARM程序中的局部变量,并将其初始化。其中:LCLA伪指令用于定义一个局部的数字变量,
72、并初始化为0;LCLL伪指令用于定义一个局部的逻辑变量,并初始化为F(假);LCLS伪指令用于定义一个局部的字符串变量,并初始化为空;2数据变量定义伪指令(Cont.)n(3)SETA、SETL和SETS语法格式:变量名SETA(SETL或SETS)表达式伪指令SETA、SETL、SETS用于给一个已经定义的全局变量或局部变量赋值。SETA伪指令用于给一个数学变量赋值;SETL伪指令用于给一个逻辑变量赋值;SETS伪指令用于给一个字符串变量赋值;2数据变量定义伪指令(Cont.)n(4)RLIST语法格式:名称RLIST寄存器列表RLIST伪指令可用于对一个通用寄存器列表定义名称,使用该伪指令
73、定义的名称可在ARM指令LDM/STM中使用。在LDM/STM指令中,列表中的寄存器访问次序为根据寄存器的编号由低到高,而与列表中的寄存器排列次序无关。3内存分配伪指令内存分配伪指令n内存分配伪指令一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。常见的数据定义伪指令有如下几种:n(1)DCB语法格式:标号DCB表达式n(2)DCW(或DCWU)语法格式:标号DCW(或DCWU)表达式3内存分配伪指令内存分配伪指令(Cont.)n(3)DCD(或DCDU)语法格式:标号DCD(或DCDU)表达式n(4)DCFD(或DCFDU)语法格式:标号DCFD(或DCFDU)表达式n(5
74、)DCFS(或DCFSU)语法格式:标号DCFS(或DCFSU)表达式3内存分配伪指令内存分配伪指令(Cont.)n(6)DCQ(或DCQU)语法格式:标号DCQ(或DCQU)表达式n(7)SPACE语法格式:标号SPACE表达式n(8)MAP语法格式:MAP表达式,基址寄存器n(9)FILED语法格式:标号FIELD表达式4汇编控制伪指令汇编控制伪指令n汇编控制伪指令用于控制汇编程序的执行流程,常用的汇编控制伪指令包括以下几条:(1)IF、ELSE、ENDIF语法格式:IF逻辑表达式n指令序列1nELSE指令序列2ENDIF4汇编控制伪指令汇编控制伪指令(Cont.)n(2)WHILE、WE
75、ND语法格式:WHILE逻辑表达式指令序列WENDn(3)MEXIT语法格式:MEXITMEXIT用于从宏定义中跳转出去。4汇编控制伪指令汇编控制伪指令(Cont.)n(4)MACRO、MEND语法格式:MACRO$标号宏名$参数1,$参数2,指令序列MENDMACRO、MEND伪指令可以将一段代码定义为一个整体,然后就可以在程序中通过宏指令多次调用该段代码。5其他常用的伪指令其他常用的伪指令n还有一些其他的伪指令,在汇编程序中经常会被使用,主要包括AREA、ALIGN、CODE16、CODE32、ENTRY、END、EXPOR(或GLOBAL)IMPORT、EXTERN、GET(或INCLU
76、DE)INCBIN、RN、ROUT等。5其他常用的伪指令其他常用的伪指令(Cont.)n(1)AREA语法格式:AREA段名属性1,属性2,AREA伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用“|”括起来,如|1_test|。属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。5其他常用的伪指令其他常用的伪指令(Cont.)n(2)ALIGNn语法格式:ALIGN表达式,偏移量n(3)CODE16、CODE32语法格式:CODE16(或CODE32)n(4)ENTRY语法格式:ENTRYn5其他常用的伪指令其他常用的伪指令(Cont.)n(5)END语法格
77、式:ENDn(6)EXPORT(或GLOBAL)语法格式:EXPORT标号WEAKn(7)IMPORT语法格式:IMPORT标号WEAK5其他常用的伪指令其他常用的伪指令(Cont.)n(8)EXTERN语法格式:EXTERN标号WEAKn(9)GET(或INCLUDE)语法格式:GET文件名n(10)INCBIN语法格式:INCBIN文件名n(11)RN语法格式:名称RN表达式ARM的汇编语言结构n在ARM(Thumb)汇编语言程序中,以相对独立的指令或数据序列的程序段为单位组织程序代码。段可以分为代码段和数据段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据。一个汇编程序至少应
78、该有一个代码段,也可以分割为多个代码段和数据段,多个段在程序编译链接时最终形成一个可执行的映象文件。可执行映象文件通常由以下几部分构成:n一个或多个代码段,代码段的属性为只读。n零个或多个包含初始化数据的数据段,数据段的属性为可读写。n零个或多个不包含初始化数据的数据段,数据段的属性为可读写。ARM汇编语言的语句格式n1.基本语句格式基本语句格式nARM(Thumb)汇编语言的语句格式为:n标号指令或伪指令 ;注释n规则:n如果一条语句太长,可将其分为若干行来书写,在行的末用续行符“”来标识下一行与本行为同一条语句。n每一条指令的助记符可以全部用大写、或全部用小写,但不能在一条指令中大、小写混
79、用。ARM汇编语言的语句格式(Cont.)n2.汇编语言程序中常用的符号汇编语言程序中常用的符号n在汇编语言程序设计中,可以使用各种符号代替地址、变量和常量等,以增加程序的可读性。以下为符号命名的约定:n符号名不应与指令或伪指令同名n符号在其作用范围内必须唯一。n符号区分大小写,同名的大、小写符号被视为两个不同的符号。n自定义的符号名不能与系统保留字相同。ARM汇编语言的语句格式(Cont.)n3.程序中的常量程序中的常量n程序中的常量是指其值在程序的运行过程中不能被改变的量。ARM(Thumb)汇编程序所支持的常量有逻辑常量、数字常量和字符串常量。n数字常量一般为32位的整数,无符号常量取值
80、范围为0232-1,有符号常量取值范围为-231231-1。n逻辑常量只有两种取值:真或假。n字符串常量为一个固定的字符串,一般用来提示程序运行时的信息。ARM汇编语言的语句格式(Cont.)n4.汇编语言程序中的变量汇编语言程序中的变量n程序中的变量是指其值在程序的运行过程中可以改变的量。n逻辑变量用于在程序的运行中保存逻辑值(真/假)。n数字变量用于在程序的运行中保存数字值,但数字值的大小不应超出数字变量所能表示的范围。n字符串变量用于在程序的运行中保存一个字符串,但字符串的长度不应超出字符串变量所能表示的范围。ARM汇编语言的语句格式(Cont.)n5.程序中的变量代换程序中的变量代换n
81、程序中的变量可通过代换操作取得一个常量。代换操作符为“$”。如果“$”在数字变量前面,编译器会将该数字变量的值转换为十六进制的字符串,并将该十六进制的字符串代换“$”后的数字变量。基于Windows下ADS的汇编语言程序结构nADS环境下的ARM汇编语言程序结构与其它环境下的汇编语言程序结构大体相同,整个程序也是以段为单元来组织代码。其语法规则总结如下:n所有标号必须在一行的顶格书写,其后不要添加“:”号;n所有的指令均不能顶格写;n大小写敏感(可以全部大写或全部小写,但不能大小写混合使用);n注释使用分号“;”。基于基于Linux下下GCC的汇编语言程序结构的汇编语言程序结构nLinux下G
82、CC的汇编语言结构与其它环境下的汇编语言结构相似,整个程序都是以程序段为单位来组织代码,但是在语言规则上与ADS环境下的ARM汇编语言规则有明显的区别。现将Linux下GCC的汇编语言规则总结如下:n所有标号必须在一行的顶格书写,并且其后必须添加“:”号;n所有的指令均不能顶格写;n大小写敏感(可以全部大写或全部小写,但不能大小写混合使用);n注释使用分号“”(注释的内容由“”号起到此行结束,注释可以在一行的顶格书写);ARM汇编语言程序调试汇编语言程序调试n无论进行嵌入式系统软件开发还是硬件电路设计,调试永远是不可缺少的、非常重要的一个环节。通常嵌入式系统的调试方法和类型有很多种,最为常见的
83、包括软件模拟调试、硬件仿真器在线调试、Wiggler线缆调试和Linux环境下的gdb程序调试。ADS软件模拟环境下的程序调试nADS软件模拟调试是利用ARMUL.dll提供的一个软ARM内核,调试工具和待调试的嵌入式软件都在主机上运行,由主机提供一个模拟的目标运行环境,可以进行语法和逻辑上的调试。它的优点是简单方便,不需要嵌入式目标板,软件的调试功能较强;功能有限,不能进行实时联机调试。ADS硬件仿真器环境下的程序调试n在ADS环境下利用JTAG硬件仿真器可以实现联机调试,即在线调试嵌入式设备的Flash中的程序或者SDRAM中的程序。由于仿真器自成体系,调试时既可以连接目标板,也可以不连接
84、目标板,当然仿真器的价格也相对比较贵。一般在程序的前期开发,通常让程序只在SDRAM中调试运行,最后才下载到Flash中进行调试运行。ADS硬件仿真器环境下的程序调试(Cont.)nARM仿真器是通过内部硬件实现PC并口协议到串行JTAG(JiontTestActionGruop)协议的转换。利用高速JTAG串行扫描链,通过调试通信通道(DebugCommunicationsChannel,DCC)连接ARM核心内嵌的名为“Embedded-ICE”的调试逻辑,调试逻辑实时监测ARM核心的寄存器、数据总线和地址总线。ADS硬件仿真器环境下的程序调试(Cont.)n1.Multi-ICEserv
85、er软件的安装软件的安装ADS硬件仿真器环境下的程序调试(Cont.)n2运行运行Multi-ICEservern保证硬件正确连接后,即可运行Multi-ICEserver,默认情况下,server会用自动配置来连接目标器件。当然,可以在settings菜单下选择配置的方式,一般选择Atuo-Configure即可。n如果正确连接到一个ARM内n核的嵌入式目标板,将显示n图。ADS硬件仿真器环境下的程序调试(Cont.)n3配置配置ADS以支持以支持JTAG仿真器仿真器n启动ADS的调试器AXD后,从菜单“Option”中选择“ConfigureTarget”,在弹出的窗口中,选择Multi-
86、ICE,如果没有此项,则需要将Multi-ICE驱动添加到对话框中。如图所示。ADSWiggler调试电缆环境下的程序调试nWiggler调试电缆实际上可以看出就是一个简易的JTAG“仿真器”,它也支持ADS集成开发环境和在线联机调试,支持单步、全速及断点等调试功能。ADSWiggler调试电缆环境下的程序调试(Cont.)n1.WigglerJTAG调试电缆的驱动安装调试电缆的驱动安装n要使用WigglerJTAG调试电缆来调试ARM处理器,除了ADS1.2集成开发环境外,还需要安装一个ARM调试代理,一般使用H-JTAG软件,H-JTAG软件的特点如下:n支持ARM7/ARM9,支持自动检
87、测和手动指定内核;n使用RDI接口,支持SDT2.51、ADS1.2、REALVIEW和IAR集成开发环境;n支持2个硬件断点或数量不限的软件断点;n支持ARM/Thumb模式;n支持LittleEndian&BIGEndian模式;n支持Semihosting调试;n支持Wiggler、SDTJTAG和自定义接口。n首先,用鼠标双击H-JTAG软件的安装文件H-JTAGV0.2.exe,启动H-JTAG安装界面,按照提示操作完成安装。ADSWiggler调试电缆环境下的程序调试(Cont.)n2.运行运行H-JATG软件软件n接着,将WigglerJTAG调试电缆一头通过并口延长线与PC机的
88、并口连接,另一头接到嵌入式目标板的JTAG插座上。然后启动H-JTAG。H-JTAG会自动检测ARM内核,如果JTAG连接正确将会在H-JTAG主窗口中显示处理器的型号.ADSWiggler调试电缆环境下的程序调试(Cont.)n3.配置配置ADS以支持以支持JTAG仿真器仿真器n启动ADS的调试器AXD后,从菜单“Option”中选择“ConfigureTarget”,在弹出的窗口中,添加或选择H-JTAG.dll(如图所示)。Linux环境下的gdb程序调试nLinux下提供了一个叫gdb的GNU调试程序,主要用来调试C、C+等应用程序。它可以提供:监视程序中变量的值;设置断点以使程序在指
89、定的代码行上暂停执行;单步执行;n语法格式:gdbgdb基本命令基本命令ARM汇编语言与C语言混合编程nARM体系结构支持C/C+以及汇编语言的混合编程,在一个完整的程序设计中,除了初始化部分用汇编完成以外,其主要的编程任务一般都用C/C+完成。n汇编语言和C/C+的混合编程通常有以下几种方式:n汇编程序中调用C程序nC程序中调用汇编程序nC程序中内嵌汇编语句n从汇编程序中访问C程序变量基本的ATPCSn基本的ATPCS规定了在混合编程时子程序调用的一些基本规则,主要包括寄存器的使用、堆栈的使用、参数传递和子程序结果的返回等方面的规则。n1.寄存器的使用规则n程序通过寄存器R0R3来传递参数,
90、这时这些寄存器可以记作A0A3,被调用的子程序在返回前无需恢复寄存器R0R3的内容。基本的ATPCS(Cont.)n在子程序中,使用R4R11来保存局部变量,这时这些寄存器可以记作V1V8。n寄存器R12用作子程序间scratch寄存器,记作IP,在子程序的连接代码段中经常会有这种使用规则。基本的ATPCS(Cont.)n寄存器R13用作数据栈指针,记做SP,在子程序中寄存器R13不能用做其他用途。n寄存器R14用作连接寄存器,记作LR,它用于保存子程序的返回地址。n寄存器R15是程序计数器,记作PC,它不能用作其他用途。nATPCS中的各寄存器在ARM编译器和汇编器中都是预定义的。基本的AT
91、PCS(Cont.)n2.堆栈的使用规则n栈指针通常可以指向不同的位置,当栈指针指向栈顶元素时,称为FULL栈。当栈指针指向与栈顶元素相邻的一个元素时,称为Empty栈。数据栈的增长方向也可以不同,当数据栈向内存减小的地址方向增长时,称为Descending栈;反之称为Ascending栈。基本的ATPCS(Cont.)n3.参数的传递规则n根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序,这两种子程序的参数传递规则不同的。n参数个数可变的子程序参数传递规则。n参数个数固定的子程序参数传递规则。基本的ATPCS(Cont.)n4.子程序结果返回规则n结果为一个3
92、2位的整数时,可通过寄存器R0返回。n结果为一个64位整数时,可以通R0和R1返回,依此类推。n结果为一个浮点数时,可以通过浮点运算部件的寄存器f0,d0或者s0来返回。n结果为一个复合的浮点数时,可以通过寄存器f0-fN或者d0dN来返回。n对于位数更多的结果,则需要通过调用内存来传递。汇编程序中调用C程序n2.C语言文件n/*Cfile,calledbyasmfile*/nintcFun(inta,intb,intc)nnreturna+b+c;nn这里的参数传递是利用寄存器r0r2。需要指出的是当函数的参数个数大于4时就要借助堆栈。C程序中调用汇编程序n在汇编程序中使用EXPORT伪指令
93、声明程序,使得本程序可以被其他的程序调用;在C语言中使用EXTERN关键词声明该汇编程序,这样就可以在C中使用该函数了。从C的角度,并不知道该函数的实现是用C还是汇编。C程序中内嵌汇编语句程序中内嵌汇编语句n在C中内嵌的汇编指令支持大部分的ARM和Thumb指令,不过其使用与汇编文件中的指令有些不同,存在一些限制,主要有下面几个方面:n不能直接向PC寄存器赋值,程序跳转要使用B或者BL指令;n在使用物理寄存器时,不要使用过于复杂的C表达式,避免物理寄存器冲突;nR12和R13可能被编译器用来存放中间编译结果;n一般不要直接指定物理寄存器,而让编译器进行分配;从汇编程序中访问从汇编程序中访问C程
94、序变量程序变量n在C程序中声明的全局变量可以被汇编程序通过地址间接访问,具体访问方法如下:n使用IMPORT伪指令声明该全局变量。n使用LDR指令读取该全局变量的内存地址,通常该全局变量的内存地址值存放在程序的数据缓冲区中。n根据该数据的类型,使用相应的LDR指令读取该全局变量的值,使用相应的STR指令修改该全局变量的值。从汇编程序中访问从汇编程序中访问C程序变量程序变量(Cont.)n各数据类型及其对应的LDR/STR指令如下:n对于无符号的char类型的变量通过指令LDRB/STRB来读写。n对于无符号的short类型的变量通过指令LDRH/STRH读写。n对于int类型的变量通过指令LD
95、R/STR来读写。n对于有符号的char类型的变量通过指令LDRSB来读取。n对于有符号的char类型的变量通过指令STRB来写入。n对于有符号的short类型的变量通过指令LDRH来读取。n对于有符号的short类型的变量通过指令LDRH来写入。n对于小于8个字的结构型变量,可以通过一条LDM/STM指令来读/写整个变量。n对于结构型变量的数据成员,可以使用相应的LDR/STR指令来访问,这时必须知道该数据成员相对于结构型变量开始地址的偏移量。从汇编程序中访问从汇编程序中访问C程序变量程序变量(Cont.)n下面是一个在汇编程序中访问C程序全局变量的例子。nAREAglobal_exp,CO
96、DE,READONLYnEXPORTasmsubnIMPORTglobv;声明全局变量nasmsubnLDRr1,=globv;将内存地址读入到R1中nLDRr0,r1;将数据读入到R0中nADDr0,r0,#2nSTRr0,r1;修改后再将值赋予变量nMOVpc,lrnEND从汇编程序中访问从汇编程序中访问C程序变量程序变量(Cont.)n程序中,变量globv1是在C程序中声明的全局变量,在汇编程序中首先使用IMPORT伪指令声明该变量,再将其内存地址读入到寄存器R1中,将其值读入到寄存器R0中,修改后再将寄存器R0的值赋予变量globv。第第10章章Bootloader设计基础设计基础1
97、0.1Bootloader概述nBootloader,启动引导程序,又叫引导加载程序,功能强大的Bootloader也就直接叫做板级支持包(BSP,BoardSupportPacket)或者固件(Firmware)。近年来,为了方便嵌入式产品的推广,也有些直接将Bootloader叫做BIOS。BIOS是PC机的“基本输入输出系统”,烧录在电脑主板上一块专门的芯片中。一般BIOS由主板厂商或者专门的BIOS生产商提供,不是开源的,用户不能修改其中的代码进行定制。而嵌入式系统的开发则离不开Bootloader的开发,它也是整个系统开发中的难点之一。10.1.1Bootloader的作用nBoot
98、loader是在嵌入式操作系统内核运行之前运行的一段小程序,也是系统开机后执行的第一段程序。通过这段小程序,可以初始化硬件设备、建立内存空间,从而将系统的软硬件环境设置成一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。Bootloader是依赖于底层硬件而实现的,因此建立一个通用的嵌入式系统Bootloader几乎是不可能的。n在PC机中,主板的BIOS和位于硬盘0磁道上的主引导记录(MasterBootRecord,MBR)中的引导程序(如LILO或GRUB等),两者一起的作用就相当于Bootloader在嵌入式系统中的作用,即实现整个系统的启动引导,并最终能引导操作系统的运行。
99、10.1.1Bootloader的作用n在嵌入式系统中,Bootloader对嵌入式设备中的主要部件如CPU、SDRAM、FLASH、串口等进行了初始化,这样可以使用Bootloader通过串口下载各种文件到设备的SDRAM中或者烧录Flash,然后将操作系统内核读入到内存中来或者直接跳转到内核的入口点,从而实现操作系统的引导。现在有些Bootloader也把对以太网的支持等功能也加进去了,这样一个功能比较强大的Bootloader实际上就已经相当于一个微型的操作系统了。10.1.1Bootloader的作用nBootloader从第一条指令跳转后,就开始初始化各种最重要的硬件,比如CPU的工
100、作频率、定时器、中断、看门狗、检测RAM大小和Flash等。一般,硬件初始化的这段程序是用汇编语言编写的,其后就用C语言编写。总体上Bootloader主要完成以下工作:10.1.1Bootloader的作用n初始化CPU速度;n初始化内存,包括启用内存库,初始化内存配置寄存器等;n初始化中断控制器,在系统启动时,关闭中断,关闭看门狗;n初始化串行端口(如果在目标上有的话);n启用指令/数据高速缓存;n设置堆栈指针;n设置参数区域并构造参数结构和标记,即引导参数;n执行POST(上电自检)来标识存在的设备并报告有何问题;n为电源管理提供挂起/恢复支持;n传输操作系统内核镜像文件到目标机。也可以
101、将操作系统内核镜像文件事先存放在Flash中,这样就不需要Bootloader和主机传输操作系统内核镜像文件,这通常是在做成产品的情况下使用。而一般在开发过程中,为了调试内核的方便,不将操作系统内核镜像文件固化在Flash中,这就需要主机和目标机进行文件传输;n跳转到内核的开始,在此又分为ROM启动和RAM启动。所谓ROM启动就是用XIP技术直接在Flash中执行操作系统镜像文件;所谓RAM启动就是指把内核镜像从Flash复制到RAM中,然后再将PC指针跳转到RAM中的操作系统启动地址。10.1.1Bootloader的作用n在嵌入式Linux软件系统的开发中,一般将软件分为启动引导程序(Bo
102、otloader)、操作系统内核(OSKernel)、根文件系统(FileSystem)、图形窗口系统(GUI)和应用程序(AP)等几个部分,其中前三部分是一个可运行的嵌入式系统必不可少的,它们在开发的过程中,被分别独立地编译链接或打包为一个二进制目标文件,然后下载(烧录)到嵌入式系统的ROM(一般是Flash)中。后两部分如果有的话,n通常也是和根文件系统一n起打包后烧录到Flash中。n因此,在Bootloader阶段,n也提供了对Flash设备的分n区格式化的支持,其空间n分配通常如图所示。10.1.2Bootloader的工作模式的工作模式n对于嵌入式系统的开发人员而言,Bootloa
103、der通常包含“启动加载”和“下载”这两种不同的工作模式。当然,这两种工作模式的区别一般仅仅对于开发人员才有意义,而对最终用户来说,Bootloader的作用就是用来加载操作系统,从而启动整个嵌入式系统。10.1.2Bootloader的工作模式的工作模式n启动加载(Bootloading)模式正常启动模式n下载(Downloading)模式提供给开发人员或者技术支持人员使用10.1.3Bootloader的启动流程的启动流程n分为stage1和stage2两个阶段一般依赖于CPU体系结构的代码,比如设备初始化代码等,都放在stage1中,而且通常都用汇编语言来实现,以达到短小精悍且启动快的目
104、的;而stage2则通常用C语言来实现,这样可以实现各种复杂的功能(比如串口、以太网接口的支持等)Bootloader的第一阶段1.硬件设备初始化2.为加载Bootloader的stage2准备RAM空间3.拷贝Bootloader的stage2到RAM空间中4.设置好堆栈5.跳转到stage2的C入口点main()函数处Bootloader的第二阶段1.初始化本阶段要使用到的硬件设备2.检测系统内存映射(memorymap)3.将kernel映像和根文件系统映像从flash上读到RAM空间中4.为内核设置启动参数5.调用内核10.2S3C2410平台下平台下Linux的的Bootloader
105、nVivinU-BOOT10.2S3C2410平台下平台下Linux的的Bootloadern1.vivi简介简介nvivi是由韩国mizi公司为ARM处理器系列设计的一个bootloader。它同样支持启动加载模式和下载工作模式。n在下载模式下,vivi为用户提供一个命令行人机接口,通过这个人机接口可以使用vivi提供的一些命令。如果嵌入式系统没有键盘和显示,那么可以利用vivi中的串口,将其和宿主机连接起来,利用宿主机中的串口软件(如windows中的超级终端或者Linux中的minicom)来控制。10.2.1vivinvivi常用的命令Load,Part,bon,Param,Boot,
106、Flashnvivi文件结构文件结构代码包括代码包括arch,init,lib,drivers和和include等等几个目录,共几个目录,共200多条文件多条文件nvivi的配置和编译的配置和编译#makedistclean。清除一些早先生成的无用。清除一些早先生成的无用的目标文件。的目标文件。#makemenuconfig。然后可以根据菜单中的。然后可以根据菜单中的信息进行配置。信息进行配置。make”命令开始编译命令开始编译10.2.1vivi10.2.1vivin3.vivi的配置和编译的配置和编译nvivi的配置和嵌入式Linux内核一样,可以采用菜单化的形式进行。其步骤主要如下:n#
107、makedistclean。清除一些早先生成的无用的目标文件。n#makemenuconfig。然后可以根据菜单中的信息进行配置。n编译。菜单配置完毕后,保存退出。然后执行“make”命令开始编译。10.2.1vivinvivi的第一阶段主要完成了依赖于CPU的体系结构硬件初始化,包括禁止中断、初始化串口、复制第二阶段到RAM中等。由于这些代码是和硬件紧密相关的,因此要求读者在阅读时对照S3C2410处理器的数据手册,查阅相关的寄存器的描述,以便更好地理解。这些汇编代码全部就集中在viviarchs3c2410目录下的head.S这一个汇编文件中,当然还有相关的头文件。10.2.1vivinv
108、ivi第二阶段的分析第二阶段的分析nvivi的第二阶段的入口就是init/main.c,按照源代码的组织流程,根据模块化划分的原则,共分为8个功能模块即八个步骤,在源代码的注释中以step非常清晰的给出了区分。10.2.1vivin第一步:vivi从main()函数开始执行,函数开始通过putstr(vivi_bannner)打印出vivi的版本。n第二步:主要是初始化GPIO,本的思路和方法就是在把握好整个系统硬件资源的前提下,根据芯片的数据手册把所有的初始值设定,在这里利用set_gpios这个函数就可以完成初始化了。10.2.1vivin第三步:进行内存映射初始化和内存管理单元(MMU)
109、的初始化工作n第四步:初始化堆,然后内存会发生变化。在这里,实际上就是实现动态内存分配策略。10.2.1vivin第五步:初始化mtd设备n第六步:配置参数,主要是init_priv_data函数。n第七步:提供vivi人机接口的各种命令。n第八步:进入Bootloader的两种模式之一.10.2.2U-bootnU-Boot,全称UniversalBootloader,是遵循GPL条款的开放源码项目n它还支持NetBSD,VxWorks,QNX,RTEMS,ARTOS,LynxOS等嵌入式操作系统。其目前主要支持的目标操作系统有OpenBSD,NetBSD,FreeBSD,4.4BSD,Li
110、nux,SVR4,Esix,Solaris,Irix,SCO,Dell,NCR,VxWorks,LynxOS,pSOS,QNX,RTEMS,ARTOS等,因此功能比较强大,这也是U-Boot中Universal的一层含义。10.2.2U-bootnU-Boot的主要特点有:n开放源码;支持多种嵌入式操作系统内核,如Linux、NetBSD,VxWorks,QNX,RTEMS,ARTOS,nLynxOS;支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;较高的可靠性和稳定性;较高的可靠性和稳定性;高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布
111、等;丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;较为丰富的开发调试文档与强大的网络技术支持;10.2.2U-bootn常用命令:Help/?Bdinfosetenvprintenvsaveenvmwmdmmflinfoerase起始地址结束地址.cp源地址目标地址大小imi起始地址bootm起始地址tftboot起始地址镜像名reset10.2.2U-bootnU-boot文件结构文件结构nU-boot代码采用了一种高度模块化的编程方式,与移植树有关的有以下几个目录.nboard:这个目录存放了所有U-boot支持的目标板的子目
112、录,如board/smdk2410/*就是我们说关心的.要将U-boot移植到自己的s3c2410x目标板上,必须参考这个目录下的内容,比如对比Flash以及Flash宽度和大小的定制邓就要修改其中的flash.c。ncommon:独立于处理器体系结构的通用代码,如内存大小探测与故障检测;cpu:与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件;10.2.2U-bootndriver:通用设备驱动,如CFIFLASH驱动(目前对INTELFLASH支持较好)nfs:这个目录中存放了U-boot支持的文件系统.examples:可在U-Boot下运行的示例程序
113、;如hello_world.c,timer.c;nInclude:U-Boot头文件;这个目录存放头文件的公共目录,其中include/configs/smdk2410.h定义了所有和S3C2410X相关的资源的配置参数,我们往往只需修改这个文件就可以配置目标板的参数,如波特率、引导参数、物理内存映射等。10.2.2U-bootnlib_xxx:处理器体系相关的文件,如lib_ppc,nlib_arm:目录分别包含与PowerPC、ARM体系结构相关的文件;nnet:与网络功能相关的文件目录,如bootp,nfs,tftp;npost:上电自检文件目录。尚有待于进一步完善;nrtcrtc:驱动
114、程序;ntools:用于创建U-BootS-RECORD和BIN镜像文件的工具;10.3其他常见的Bootloadern1.WinCE的BootloaderNboot和Eboot是WinCE的Bootloader。Nboot是nandflashbootloader的简写,CPU可以直接从nandflash启动,但是其代码大小不能超过4k,功能有限;Eboot则支持ethernetnetwork(以太网),功能强大,用于Ehternet在线调试和下载。Eboot提供的命令命令说明Help列出所有支持的命令并加以说明Eboot从宿主机上通过网线下载CE映像并加载Write向某一内存地址写入数据Re
115、ad显示某一内存地址的数据Jump跳转到某一地址执行程序Xmodem从计算机的超级终端接收以Xmodem协议传送的文件Toy测试平台CPU的计数器是否运转Flash擦除或者更新Flash中的数据Tlbread显示CPU的所有TLB表Tlbwrit设置CPU的TLBMacaddr设置CPU的MAC地址Seti设置平台的IP地址n2.BlobBlob是BootloaderObject的缩写,是一款功能强大的Bootloader,目前常用于Intel推出的Xscale架构的CPU的引导,譬如SA1110、PXA255/270等。nBlob的代码也可以分为两个阶段。第一阶段从start.s文件开始,这
116、也是开机执行的第段代码,这部分代码是在Flash中运行,主要功能包括对S3C2410的一些寄存器的初始化和将Blob第二阶段代码从Flash拷贝到SDRAM中。这一阶段的代码被编译后最大不能超过1kB。第二阶段的起始文件为trampoline.s,被复制到SDRAM后,就从第一阶段跳到这个文件开始执行,先进行一些变量设置、堆栈的初始化等工作后,跳转到main.c进入C函数。第二阶段最大为63KB第第11章章Linux操作系统基础操作系统基础11.1嵌入式嵌入式Linux的开发环境的开发环境11.1.1交叉开发概述交叉开发概述用于开发和调试目标板上所用到的操作系统、应用程序等所有软件。这种在宿主
117、机上开发程序、在目标板上运行程序的方式,通常就叫做交叉开发n目标板(Target)可以是嵌入式应用软件的实际运行环境,当然也可以是替代实际环境的仿真系统(如软件模拟器)。n宿主机(Host)通过串口、网络连接或调试接口(如JTAG仿真器)与目标机通信。宿主机的软硬件资源比较丰富,其操作系统主要有Windows和Linux两种,其上用于开发程序的那套软件工具,通常叫做开发工具链。11.1.2桌面桌面Linux的开发工具的开发工具链链nGNU开发工具链(toolchain)主要包括GNUCompilerCollection、GNUlibc以及用来编译、测试和分析软件的GNUbinutils三个大的
118、模块。11.1.3嵌入式嵌入式Linux的交叉开的交叉开发工具链发工具链n基基于于ARM平平台台的的交交叉叉工工具具链链,这这里里将将其其目目标标平平台台名名为为arm-linux-gnu,比比如如arm-linux-gcc、arm-linux-gdb等。等。n分步构建交交叉开发工具链的整个过程:1.下载源代码下载源代码到相关的网站下载包括binutils、gcc、glibc(如ftp.gnu.org)及linux(如ftp.kernel.org)内核的源代码.注意:glibc和内核源代码的版本必须与目标机上实际使用的版本保持一致.11.1.3嵌入式嵌入式Linux的交叉开的交叉开发工具链发工
119、具链2.建立环境变量建立环境变量声声明明以以下下环环境境变变量量的的目目的的是是在在之之后后编编译译工工具具库库的的时时候候用用到到,很很方方便便输输入入,尤尤其其是是可可以以降降低低输输错错路路径径的风险。的风险。3.配置、安装配置、安装binutilsinutils是是GNU工工具具之之一一,它它包包括括连连接接器器、汇汇编编器器和和其其他他用用于于目目标标文文件件和和档档案案的的工工具具,它它是是二二进进制制代代码码的的维维护护工工具具。安安装装Binutils工工具具包包含含的的程程序序有有addr2line、ar、as、c+filt、gprof、ld、mm、objcopy、 ranl
120、ib、 readelf、 size、 strings、strip、libiberty、libbfd和和libopcodes。11.1.3嵌入式嵌入式Linux的交叉开的交叉开发工具链发工具链n4.配置配置linux内核头文件内核头文件n编译器需要通过系统内核的头文件来获得目标平台所支持的系统函数调用所需要的信息。对于LINUX内核,最好的方法是下载一个合适的内核,然后复制获得头文件。n首先执行makemrproper进行清理工作。n接下来执行makeconfigARCH=arm(或makemenuconfig/xconfigARCH=arm)进行配置.11.1.3嵌入式嵌入式Linux的交叉开
121、的交叉开发工具链发工具链5.第一次编译gccn完成此过程需要执行三个步骤,分别如下:(1)修改t-linux下的内容(2)配置gcc(3)编译、安装gcc11.1.3嵌入式嵌入式Linux的交叉开的交叉开发工具链发工具链n6.交叉编译交叉编译glibcn这一步骤生成的代码是针对目标机cpu的,因此它属于一个交叉编译过程。该过程要用到linux内核头文件,默认路径为$PREFIX/arm-linux/sys-linux,因而需要在$PREFIX/arm-linux中建立一个名为sys-linux的软连接,使其内核头文件所在的include目录.或者也可以在接下来要执行的configure命令中使
122、用-with-headers参数指定linux内核头文件的实际路径。11.1.3嵌入式嵌入式Linux的交叉开的交叉开发工具链发工具链n7.第二次编译第二次编译gccn由于第一次安装的gcc没有交叉glibc支持,现在已经安装了glibc,所以需要重新编译来支持glibc。n到此为止整个交叉开发工具链就完全生成了。11.2桌面桌面Linux的安装的安装11.2.1双操作系统环境双操作系统环境一般会用到两个桌面操作系统,即linux和windows操作系统,其中Linux主要有Redhat/Fedora、Suse、Mandrake等发行版本uWindows与与Linux的双重启动的双重启动u为L
123、inux操作系统准备硬盘空间11.2.2Cygwin模拟环境模拟环境nCygwin是GNU的开发人员为了能将Linux系统下一些应用移植到Windows环境下而开发的一套中间移植工具即模拟环境。安装完成后,就是Windows下的一个目录,而里面又提供了Linux操作系统环境。11.2.3VMware虚拟机环境虚拟机环境nVMwareworkstation是VMware公司设计的专业虚拟机,可以在Windows平台上为几乎任何的其他操作系统提供虚拟运行环境。顾名思义,只要物理主机的内存、CPU等配置足够,就可以在Windows平台上,再“虚拟”出一台或多台“PC机”,而且使用简单,容易上手,是目
124、前用得非常广泛的工具软件。11.3Linux的使用的使用11.3.1Linux基本命令基本命令1.addusern示例:创建pdr帐户nadduserpdr2.catn示例:ncattext在屏幕上显示文件text的内容;ncat-ntextfile1textfile2把textfile1的文件内容加上行号后输入textfile2这个文件里;3.Cdn示例:假设用户当前目录是示例:假设用户当前目录是/home/xu现需要现需要更换到更换到/home/xu/pro目录中目录中n$cdpro4.cpn示例:示例:n$cp-r/usr/xu/usr/liu/表示将表示将/usr/xu目录中的所有文件
125、及其子目录拷贝目录中的所有文件及其子目录拷贝到目录到目录/usr/liu中。中。5.df示例:列出各文件系统的磁盘空间使用情况。n#df6.dfn示例:显示包含在每个文件以及目录/home/fran的子目录中的磁盘块数。ndu-a/home/fran7.Exportn示例:显示当前所有环境变量的设置情况n#export8.fdiskn示例:查看当前系统中磁盘的分区状况示例:查看当前系统中磁盘的分区状况,包括硬盘、包括硬盘、U盘等盘等nfdisk-l9.lnn示例:要为当前目录下的file文件建立一个硬链接,名为/home/lbt/doc/file/,可用如下命令:nlnfile/home/lb
126、t/doc/file10.locaten示例:nlocatefilename:寻找系统中所有叫filename的文件11.Lsn示例:将/bin目录以下所有目录及文件详细资料列出:nls-lR/bin12.minicomn示例:开启minicom得配置界面nminicoms13.Mkdirn示例:在当前目录中创建嵌套的目录层次示例:在当前目录中创建嵌套的目录层次inin和和inin下的下的mail目录,权限设置为只有文件拥有者有目录,权限设置为只有文件拥有者有读、写和执行权限。读、写和执行权限。nmkdir-p-m700./inin/mail/14.Mountn示例:示例:n挂载挂载ntfs格
127、式的格式的hda7分区到分区到/mnt/cdrom文件夹文件夹nmount-oiocharset=cp936/dev/hda7/mnt/cdrom15.mvn示例:$mv/usr/xu/*.表示将/usr/xu中的所有文件移到当前目录用.表示16.Passwdn示例:npasswdpengdrn17.pingn示例:npingsina18.pwdn示例:查看当前工作:npwd19.rebootn示例:做个重开机的模拟(只有纪录并不会真的重开机)。nrebootw20.rmdirn示例:在工作目录下的BBB目录中,删除名为Test的子目录。若Test删除后,BBB目录成为空目录,则BBB亦予删除
128、。nrmdir-pBBB/Testn21.setupn功能说明:设置程序。n语法:setupn22.sun功能说明:变更用户身份。n示例:变更账号为超级用户,并在执行df命令后还原使用者。nsu-cdfrootn22.tarn功能说明:备份或解压文件。n示例:压缩目录/etc为tar.gz后缀。n#tarcvfbackup.tar/etcn解压#tarzxvffile.tar.gzn#tarjxvffile.tar.bz2n24.umountn功能:卸除文件系统。n示例:卸载/mnt区:numount/mnt/cdromn25whereisn功能:查询某个二进制命令文件、帮助文件等所在目录.n
129、比如:查找“ls”这个二进制命令文件所在的目录nwhereisls11.3.2vi编辑器的使用编辑器的使用nvi是visualinterface的简称,它在Linux上的地位就同Edit程序在DOS上一样,可以执行输出、删除、查找、替换、块操作等众多文本操作,而且用户可以根据自己的需要对其进行定制,这是其他编辑程序所没有的。它不是一个排版程序,不象Word或WPS那样可以对字体、格式、段落等其他属性进行编排,它只是一个文本编辑程序。n1.vi的基本模式及模式间转换的基本模式及模式间转换nvi编辑器的使用按不同的使用方式可以分为3种状态,分别是命令模式(CommandMode)、输入模式(Ins
130、ertMode)和末行模式(LastLineMode),各模式区分如下:n(1)命令模式n(2)输入模式n(3)末行模式n2.vi的基本操作的基本操作n(1)进入与离开)进入与离开vin要进入vi可以直接在系统提示字元下键入vi档案名称,vi可以自动帮你载入所要编辑的档案或是开启一个新档。进入vi後萤幕左方会出现波浪符号,凡是列首有该符号就代表此列目前是空的。n要离开vi可以在指令模式下键入“:q”(不保存离开),“:wq”(保存离开)指令则是存档后再离开(注意冒号)。n(2)vi的删除、修改与复制n(3)vi的光标移动n由于许多编辑工作都是由游标来定位的,所以vi提供许多移动游标的方式。n例
131、如:n0:移动到游标所在行的最前面n$:移动到游标所在行的最後面n(4)vi的查找与替换n在vi中的查找与替换也非常简单,其操作有些类似在Telnet中的使用。其中,查找的命令在命令行模式下,而替换的命令则在底行模式下(以“:”开头).n例如:n查找/pattern:从光标开始处向文件尾搜索patternn?pattern从光标开始处向文件首搜索patternn(5)vi的文件操作11.3.3gcc编译器编译器n编译器的作用是将用高级语言或者汇编语言编写的源代码,翻译成处理器上等效的一系列操作命令。针对嵌入式系统来说,其编译器数不胜数,其中gcc和汇编器as是非常优秀的编译工具,而且免费。n编
132、译器的输出被称为目标文件。对于任何嵌入式系统而言,有一个高效的编译器、链接器和调试器是非常重要的,gcc不仅在桌面领域中表现出色,还可以为嵌入式系统编译出高质量的代码。n使用语法:gccoptionfilename.11.3.4make工具和工具和Makefile文件文件n无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,都经常要用到make或makeinstall。利用make工具,可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源
133、文件之间纷繁复杂的相互关系。nMake工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile文件是许多编译器(包括WindowsNT下的编译器)维护编译信息的常用方法。n以下将以一个示例的方式来说明Makefile文件的编写规则。在这个示例中有2个C文件和1个头文件,要写一个Makefile来告诉make命令如何编译和链接这几个文件。实现的规则是:n如果这个工程没有编译过,那么所有C文件都要编译并被链
134、接;n如果这个工程的某几个C文件被修改,那么只编译被修改的C文件,并链接目标程序;n如果这个工程的头文件被改变了,那么需要编译引用了这几个头文件的C文件,并链接目标程序;11.4Linux内核结构内核结构n从结构上来讲,操作系统有微内核结构和单一结构之分,WindowsNT和MINIX是典型的微内核操作系统,而Linux则是单一结构的操作系统。微内核结构只提供内存管理、中断管理等最基本的服务,服务之间通过进程间通信来进行交互,因此效率相对较低,但它可方便地在内核中添加新的组件,结构清晰;单一内核的访问是通过系统调用来实现,其效率高,但结构相对复杂,且不容易、不方便向内核中添加新的组件。11.4
135、.1核心子系统核心子系统1.内存管理内存管理n对任何一台计算机而言,其内存以及其它资源都是有限的。为了让有限的物理内存满足应用程序对内存的大需求量,Linux采用了称为“虚拟内存”的内存管理方式。2.进程调度进程调度n进程实际是某特定应用程序的一个运行实体。在Linux系统中,能够同时运行多个进程,Linux通过在短的时间间隔内轮流运行这些进程而实现“多任务”。3.进程间通信进程间通信n为了完成某特定任务,有时需要综合两个程序的功能,例如一个程序输出文本,而另一个程序对文本进行排序。4.虚拟文件系统虚拟文件系统nLinux操作系统中单独的文件系统并不是由驱动器号或驱动器名称(如A:或C:等)来
136、标识的,而是和UNIX操作系统一样,将独立的文件系统组合成了一个层次化的树形结构,并且由一个单独的实体代表这一文件系统。5.网络接口网络接口nLinux和网络几乎是同义词。实际上Linux是就是Internet或WWW的产物。6.其它其它n除上述主要组成部分之外,内核还包含设备驱动程序和一些一般性的任务和机制,这些任务和机制可使Linux内核的各个部分有效地组合在一起,它们是上述主要部分高效工作的必要保证。11.4.2设备驱动程序设备驱动程序n设备驱动程序也是内核的一部分,它由一组数据结构和函数组成,其中的大部分函数是对驱动程序接口的实现。驱动程序通过这组数据结构和函数控制一个或多个设备,并通
137、过驱动程序接口与内核的其它部分交互。nLinux有许多不同的设备驱动程序,这也是Linux在嵌入式系统开发中广泛应用的原因之一,而且驱动程序还在不断增长。n虽然这些驱动程序驱动的设备不同,完成的工作各异,但它们都具有一些一般的属性:(1)Kernelcode:n设备驱动程序和内核中的其它代码相似,是kenel的一部分,如果发生错误,可能严重损害系统。(2)Kenelinterfacesn设备驱动程序必须向Linux内核或者它所在的子系统提供一个标准的接口。例如,终端驱动程序向Linux内核提供了一个文件I/O接口,而SCSI设备驱动程序向SCSI子系统提供了SCSI设备接口,接着,向内核提供了
138、文件I/O和buffer2cache的接口。(3)Kernelmechanismsandservicesn设备驱动程序使用标准的内核服务,例如内存分配、中断转发和等待队列来完成工作。UNIXSVR4提出了设备-驱动程序接口/驱动程序-内核接口规范(DDI/DKI),由它来规范化内核与驱动程序之间的接口。(4)Loadablen大多数的Linux设备驱动程序,可以在需要的时候作为内核模块加载,在不再需要的时候卸载。(5)ConfigurablenLinux设备驱动程序可以建立在内核。至于哪些设备建立到内核,可以在内核编译的时候配置。(6)Dynamicn在系统启动,每一个设备启动程序初始化的时候
139、,它会查找它管理的硬件设备。如果一个设备驱动程序所控制的设备不存在并没有关系。这时这个设备驱动程序只是多余的,占用很少的系统内存,而不会产生危害。11.5Linux目录结构目录结构n11.5.1Linux源文件的目录结构一般桌面Linux安装后,在/usr/src/Linux-*.*.*(版本号,比如2.4.18)目录下有内核源代码,内核代码非常庞大,包括驱动程序在内有好几百兆字节。下面介绍下内核的目录结构n(1)arch目录包括了所有和体系结构相关的核心代码。n(2)include目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在include/linux子目录下。n(3)ini
140、t目录包含核心的初始化代码(不是系统的引导代码),有main.c和Version.c两个文件。这是研究核心如何工作的好起点。n(4)drivers目录中是系统中所有的设备驱动程序。它又进一步划分成几类设备驱动,每一种有对应的子目录,如声卡的驱动对应于drivers/sound。n(5)ipc目录包含了核心进程间的通信代码。n(6)modules目录存放了已建好的、可动态加载的模块。n(7)fs目录存放Linux支持的文件系统代码。不同的文件系统有不同的子目录对应,如ext3文件系统对应的就是ext3子目录。n(8)Kernel内核管理的核心代码放在这里。同时与处理器结构相关代码都放在arch/
141、*/kernel目录下。n(9)net目录里是核心的网络部分代码,其每个子目录对应于网络的一个方面。n(10)lib目录包含了核心的库代码,不过与处理器结构相关的库代码被放在arch/*/lib/目录下。n(11)scripts目录包含用于配置核心的脚本文件。n(12)documentation目录下是一些文档,是对每个目录作用的具体说明。11.5.2Linux运行系统的目录运行系统的目录结构结构nLinux运行后,它的目录结构和源文件目录结构有所不同。运行系统目录树的主要部分有/root、/usr、/var、/home等。(1)/root目录中包括:引导系统的必备文件,文件系统的挂装信息以及
142、系统修复工具和备份工具等。(2)/usr目录中包含通常操作中不需要进行修改的命令程序文件、程序库、手册和其它文档等,它并不和特定的CPU相关,也不会在通常的使用中修改。因此,将/usr目录挂装为只读性质的。(3)/var目录中包含经常变化的文件,例如打印机、邮件、新闻等的假脱机目录、日志文件、格式化后的手册页以及临时文件等。(4)/home中包含用户的主目录,用户的数据保存在其主目录中,如果有必要,也可将/home划分为不同的文件系统,例如/home/students和/home/teachers等。(5)/proc目录下的内容并不是ROM中的,而是系统启动后在内存中创建的,它包含内核虚拟文件
143、系统和进程信息,例如CPU、DMA通道以及中断的使用信息等。(6)/etc包含了系统相关的配置文件,比如开机启动选项等。(7)/bin包含了引导过程必需的命令,也可由普通用户使用。(8)/sbin和/bin类似,尽管其中的命令可由普通用户使用,但由于这些命令属于系统级命令,因此无特殊需求不使用其中的命令。(9)/dev包含各类设备文件。(10)/tmp包含临时文件。引导后运行的程序应当在/var/tmp中保存文件,因为其中的可用空间大一些。(11)/boot包含引导装载程序要使用的文件,内核映象通常保存在这个目录中。(12)/mnt是临时文件系统的挂装目录。比如U盘、光盘、软盘等都可以在这个目
144、录下建立挂载点。11.6Linux文件系统文件系统nLinux利用虚拟文件系统,把文件系统操作和不同文件系统的具体实现细节分离了开来。很长时期以来,文件系统的接口保持了一定的稳定性,即使变化也是向下兼容的。但是文件系统的框架结构发生了彻底的变化。起初的框架只支持一种文件系统,并且所有的文件都必须存放在与系统有物理连接的本地磁盘上。11.6.1文件系统与内核的关系文件系统与内核的关系n任何一个操作系统都必须要提供持久性存储和管理数据的手段。在Linux系统中,“文件”用来保存数据,而“文件系统”可以让用户组织、操纵以及存取不同的文件。文件系统的基本组成单位是文件,文件系统中的所有文件通过目录、链
145、接等组织成一棵完整的树型结构,其根为“/”,文件在叶子位置,各子目录处在中间节点的位置。11.6.2常见通用常见通用Linux文件系文件系统统n1.EXT2文件系统文件系统nEXT2是由RemyCard发明的,它是Linux的一个可扩展的、功能强大的文件系统。至少在Linux社区中,EXT2是最成功的文件系统,是所有当前的Linux发布版的基础。象大多数文件系统一样,EXT2文件系统建立在这样的前提下:文件的数据存放在数据块中,这些数据块的长度都相同。n2.EXT3文件系统文件系统nExt3文件系统是直接从Ext2文件系统发展而来,它很大程度上是基于Ext2的,因此,它在磁盘上的数据结构从本质
146、上与Ext2文件系统的数据结构是相同的。事实上,如果Ext3文件系统已经被彻底卸载,那么,就可以把它作为Ext2文件系统来重新安装;反之,创建Ext2文件系统的日志,并把它作为Ext3文件系统来重新安装也是一种简单、快速的操作。11.6.3常见嵌入式常见嵌入式Linux文件文件系统系统n在嵌入式Linux应用中,主要的存储设备为RAM(DRAM,SDRAM)和ROM(常采用FLASH存储器),常用的基于存储设备的文件系统类型包括:jffs2,yaffs,cramfs,romfs,ramdisk,ramfs/tmpfs等。1.基于Flash的文件系统nFlash(闪存)作为嵌入式系统的主要存储媒
147、介,有其自身的特性。Flash的写入操作只能把对应位置的1修改为0,而不能把0修改为1(擦除Flash就是把对应存储块的内容恢复为1),因此,一般情况下,向Flash写入内容时,需要先擦除对应的存储区间,这种擦除是以块(block)为单位进行的。2.基于基于RAM的文件系统的文件系统n(1)RamdisknRamdisk是将一部分固定大小的内存当作分区来使用。它并非一个实际的文件系统,而是一种将实际的文件系统装入内存的机制,并且可以作为根文件系统。将一些经常被访问而又不会更改的文件(如只读的根文件系统)通过Ramdisk放在内存中,可以明显地提高系统的性能。n(2)ramfs/tmpfsnRa
148、mfs是LinusTorvalds开发的一种基于内存的文件系统,工作于虚拟文件系统(VFS)层,不能格式化,可以创建多个,在创建时可以指定其最大能使用的内存大小。(实际上,VFS本质上可看成一种内存文件系统,它统一了文件在内核中的表示方式,并对磁盘文件系统进行缓冲。)n3.网络文件系统网络文件系统(NFS,NetworkFileSystem)n网络文件系统是FreeBSD支持的文件系统中的一种,它允许一个系统在网络上共享目录和文件。通过使用NFS,用户和程序可以象访问本地文件一样访问远端系统上的文件。NFS是由SUN公司于1984年推出。它的通讯协议设计与主机及嵌入式终端系统无关,用户只要在主
149、机中用mount就可将某个文件夹挂到终端系统上。11.6.4根文件系统的选择根文件系统的选择n选择一个文件系统用于根文件系统是一个取舍的过程,最后的决定往往是对一个文件系统性能和目标用途的折中。通常选择一个文件系统需要注意以下几个特点。1.可写可写:是否该文件系统能被写数据。2.可保存可保存:是否该文件系统在重启后能够保存修改后的东西,一般是在有可写的基础上才会有该功能。3.可压缩可压缩:是否挂载的文件系统内容可被压缩,这对一个嵌入式系统非常有用,可以节约宝贵的存储空间。4.存在存在RAM:是否可以在挂载之前将该文件系统的内容第一次从存储设备压缩到RAM中,通常许多文件系统被直接从存储设备挂载
150、。5.可恢复可恢复:当突然断电后能否恢复对文件系统的修改。第第12章章嵌入式嵌入式Linux软件设计软件设计12.1移植的基本概念移植的基本概念n移植是嵌入式Linux软件设计中用得最多的一个概念,广义上讲移植包括软件移植和硬件移植。从狭义上讲,移植就是指软件移植,即将一个软件从一个平台迁移到另一个与其不同的平台上工作。通常情况下,移植分为以下3种情况。n1、从一个硬件平台移植到另一个硬件平台、从一个硬件平台移植到另一个硬件平台n2、从一个操作系统移植到另一个操作系统、从一个操作系统移植到另一个操作系统n3、从一种软件库环境移植到另一种软件库环、从一种软件库环境移植到另一种软件库环境境12.2
151、Bootloader的移植的移植nBootloader是操作系统和硬件的纽带,它负责初始化硬件,引导操作系统内核,检测各种参数给操作系统内核使用。事实上,一个功能完备的大型Bootloader,就相当于一个小型的操作系统。在嵌入式领域中,操作系统移植的关键在于Bootloader的移植以及操作系统内核与硬件相关部分的移植。12.2.1关键文件的修改关键文件的修改n1.vivi顶层Makefile文件的修改nvivi作为Linux系统的启动代码,在编译配置时需要用到函数库,包括交叉编译器库和头文件,交叉编译开关选项设置,还包括Linux内核代码中的库和头文件,所以,通常需要修改vivi工程管理文
152、件Makefile。n2.vivi中与硬件相关的初始化n与具体运行在哪一个处理器平台上相关的文件都存放在vivi/arch/目录下,本系统使用S3C2410x处理器,对应的目录为s3c2410。其中head.s文件是vivi启动配置代码,加电复位运行的代码就是从这里开始的。n3.对不同Flash启动的修改nvivi能从NorFlash或NandFlash启动,因此启动程序、Linux内核及根文件系统,甚至还包括图形用户界面都需要存放在NorFlash或NandFlash中。n4.内核启动参数设置n经过修改后,S3C2410x开发板能从NandFlash中启动运行Linux,也能从NorFlas
153、h中启动,所以相应地也要修改启动命令.n5.Flash驱动的实现n移植vivi的最后一步就是实现Flash驱动,程序员需要根据自己系统中具体Flash芯片的型号及配置来修改驱动程序,使Flash设备能够在嵌入式系统中正常工作。12.2.2串口设置示例串口设置示例n串口作为一种常用的通信方式,在嵌入式开发中起到极其重要的作用,几乎所有的嵌入式设备都提供了串口的支持,并且都在Bootloader中就给出了支持,以为下一步开发提供方便,比如操作系统内、文件系统等下载等。n对vivi而言,串口的初始化是在vivi初始化的第一个阶段进行,具体是在arch/s3c2410/head.s文件中设置,且一般串
154、口波特率设置为115200Buad。有关S3C2410数据手册中的串口相关寄存器的功能和波特率设置见6.1节,比如,若希望波特率设置为115200,而PCLK又等于40MHz,那么UBRDIVn就应该设置为:nUBRDIVn=(int)(40000000/(11520016)1=(int)(21.7)-1=20n其中,PCLK=50700000,UBRDIV0的值向下取整。12.2.3Bootloader的交叉编的交叉编译译n为了进行交叉编译,需要修改vivi目录下的Makefile文件,将其中的编译器要由gcc改为交叉编译器arm-linux-gcc。然后使用make命令,系统将根据Make
155、file文件自动完成整个编译。编译完成后,系统将自动在vivi的根目录下生成一个名为“vivi”的二进制目标文件,用于下载到嵌入式目标设备的Flash中。12.2.4Bootloader的下载的下载nBootloader的下载(又叫做烧录)是利用JTAG口进行的,操作平台可以是Windows或桌面Linux,只是两者用的工具软件不同而已,这里以Windows操作平台以及sjf2410工具软件为例进行介绍。在下载之前,需要将生成的可执行文件从桌面Linux下转移到Windows的某个目录下(如d:vivi)。n利用Jflash线将PC机和嵌入式目标板的JTAG口正确连接。这里要注意,Jflash
156、线和Wiggler线的形状非常相似,不要混淆。n启动sjf服务,安装giveio.sys驱动。打开sjf目录下的loaddrv.exe,将弹出LoadDrv窗口.n然后再依次点击install和start按钮,就会提示“servicealreadyruning”,也就是驱动已经安装成功.n在DOS环境下手动运行sjf2410命令:sjf2410/f:vivi,其中“/f:是”参数而不是目录.nvivi下载成功后,用串口将PC和嵌入式目标板连接起来,并启动Windows中的超级终端.12.3嵌入式嵌入式Linux内核的移植内核的移植n内核是嵌入式Linux系统的核心部分,因为Linux与Wind
157、ows不同,前者的内核和文件系统、图形用户系统(GUI窗口系统)可以分开,它们的开发、移植、下载甚至运行都是可以分开的。内核移植是一个比较复杂的任务,当然也是嵌入式系统开发中非常重要的一个过程。内核移植一般包括内核配置、内核编译和内核下载3大步骤。12.3.1内核移植的准备内核移植的准备n移植内核首先要准备好编译内核的编译器即交叉编译工具链,然后从相关的网站(ftp.kernel.org)下载要移植的内核源代码代码(基本上都是C语言编写)。12.3.1内核移植的准备内核移植的准备n3.arch/arm目录下目录下Config.in修改修改nConfig.in文件是用来设置后面介绍的menuco
158、nfig配置菜单的,它们是一一对应关系。这里把嵌入式目标板的CPU平台加在相应的地方,这样在配置Linux内核时就能够选择是否支持该平台了。最初标准的2.4.18内核中没有S3C2410的相关信息,所以需要在该文件中进行有效的配置,以加入支持S3C2410处理器的相关信息。12.3.1内核移植的准备内核移植的准备n4.arch/arm/boot目录下目录下Makefile修改修改n编译出来的内核存放在该目录下。这里用来指定内核解压到实际硬件内存系统中的物理地址。一般如果内核无法正常启动,很可能是这里的地址设置不正确。n5.arch/arm/boot/compressed目录下目录下Makefi
159、le修改修改n该文件从vmlinux中创建一个压缩的vmlinuz镜像文件。该文件中用到的SYSTEM、ZTEXTADDR、ZBSSADDR、和ZRELADDR是从arch/arm/boot/Makefile文件中得到的。12.3.1内核移植的准备内核移植的准备n6.arch/arm/boot/compressed目录目录下添加下添加head-s3c2410.sn7.arch/arm/def-configs目录目录n这里定义了一些平台的这里定义了一些平台的config文件,文件,比如比如lart和和assert等。把配置好的等。把配置好的S3C2410的配置文件复制到这里即可。的配置文件复制到
160、这里即可。12.3.1内核移植的准备内核移植的准备n8.arch/arm/kernel目录下目录下Makefile修改修改n该文件主要用来确定文件类型的依赖关系。n9.arch/arm/kernel目录下的文件目录下的文件debug-armv.s修改修改n在该文件中添加如下代码,目的是关闭外围设备的时钟,以保证系统正常运行。12.3.1内核移植的准备内核移植的准备n10.arch/arm/kernel目录下的文件目录下的文件entry-armv.s修改修改n在适当的地方加入如下代码,此为CPU初始化时的处理中断的汇编代码。n11.arch/arm/mm目录下的相关文件目录下的相关文件n此目录下
161、的文件是和ARM平台相关的内存管理内容,只有mm-armv.c文件需要移植。12.3.1内核移植的准备内核移植的准备n12.arch/arm/mach-s3c2410目录下目录下的相关文件的相关文件n这个目录在2.4.18版本的内核中是不存在的,但在高版本中已经添加了对这款处理器的支持。不过发布的内核只是对处理器的基本信息提供支持,有关开发板的外设,例如USB、电源管理等都要用户自己添加。12.3.2关键文件的修改关键文件的修改n1.设置目标平台和指定交叉编译器设置目标平台和指定交叉编译器n在源代码的最上层根目录下的Makefile文件中,指定所移植的硬件平台,以及所使用的交叉编译器。n2.a
162、rch/arm目录下目录下Makefile修改修改n内核系统的启动代码是通过此文件产生的。n3.arch/arm目录下目录下Config.in修改修改nConfig.in文件是用来设置后面介绍的menuconfig配置菜单的,它们是一一对应关系。这里把嵌入式目标板的CPU平台加在相应的地方,这样在配置Linux内核时就能够选择是否支持该平台了n4.arch/arm/boot目录下目录下Makefile修改修改n编译出来的内核存放在该目录下。这里用来指定内核解压到实际硬件内存系统中的物理地址。一般如果内核无法正常启动,很可能是这里的地址设置不正确。n5.arch/arm/boot/compres
163、sed目录下目录下Makefile修改修改n该文件从vmlinux中创建一个压缩的vmlinuz镜像文件。该文件中用到的SYSTEM、ZTEXTADDR、ZBSSADDR、和ZRELADDR是从arch/arm/boot/Makefile文件中得到的。n6.arch/arm/boot/compressed目录下添目录下添加加head-s3c2410.sn7.arch/arm/def-configs目录目录n这里定义了一些平台的config文件,比如lart和assert等。把配置好的S3C2410的配置文件复制到这里即可。n8.arch/arm/kernel目录下目录下Makefile修改修改
164、n该文件主要用来确定文件类型的依赖关系。n9.arch/arm/kernel目录下的文件目录下的文件debug-armv.s修改修改n在该文件中添加如下代码,目的是关闭外围设备的时钟,以保证系统正常运行。n10.arch/arm/kernel目录下的文件目录下的文件entry-armv.s修改修改n在适当的地方加入如下代码,此为CPU初始化时的处理中断的汇编代码。n11.arch/arm/mm目录下的相关文件目录下的相关文件n此目录下的文件是和ARM平台相关的内存管理内容,只有mm-armv.c文件需要移植。n12.arch/arm/mach-s3c2410目录下的相目录下的相关文件关文件n这
165、个目录在2.4.18版本的内核中是不存在的,但在高版本中已经添加了对这款处理器的支持。不过发布的内核只是对处理器的基本信息提供支持,有关开发板的外设.12.3.3内核的配置与裁剪内核的配置与裁剪n配置内核与裁剪是移植内核过程中很重要的一步,也是非常复杂的一步,配置时一定要小心,否则操作系统将无法运行。配置内核的目的是裁剪掉不必要的文件和目录,获得一个最简的、又能满足用户开发的操作系统,以解除嵌入式开发过程中所遇到的存储空间有限的困扰。n通常有4种主要的配置内核的方法。n1.makeconfign2.makeoldconfign3.makemenuconfign4.makexconfig12.3
166、.4内核的编译内核的编译n编译内核通常也需要几个步骤,一是清除以前编译通过的残留文件;二是编译内核image文件和可加载模块;三是安装模块。在编译内核之前,可先参考内核目录下的README文件和Documentation/Changes文件,其中README文件告诉我们通过的安装内核的方法,Changes文件主要告诉编译和运行内核需要的最低工具软件列表。n具体介绍编译内核的基本步骤如下:nmakedep命令用在内核2.4或之前,用于建立源文件之间的依赖关系,在执行内核配置命令之后使用,nmakeclean命令用于删除前面留下来的中间文件,该命令不会删除.config等配置文件。这个步骤是可选的
167、.nmakezImage命令用于编译生成压缩形式的内核映象,编译成功后,就会在archarmboot目录下生成zImage文件.n如果在配置菜单的过程中,有些选项被选择为模块的,即选项前为M,并且在回答Enableloadablemodulesupport(CONFIG_MODULES)时选了“Yes”的,则接下来就还要用命令makemodules来编译这些可加载模块,并用makemodules_install将makemodules生成的模块文件复制到到相应目录。n如果是直接升级PC桌面Linux系统的内核,那么接下来还要用makeinstall来安装新内核。12.3.5内核的下载内核的下载
168、n启动超级终端(波特率为115200),连好串口线,在开机的瞬间快速的按空格键(不能是回车键),就进入到vivi控制台命令行下。n在vivi命令行上输入:loadflashkernelx(含义就是:向flash芯片中烧写kernel,采用xmodem协议),回车后会提示等待。n立即选择要发送的文件,比如zImage文件,这里Linux环境下源代码arch/arm/boot目录下的zImage内核映像文件已转移到windows的某个目录下。12.4嵌入式嵌入式Linux文件系统的移植文件系统的移植n文件系统是Linux/UNIX系统的一个重要组成部分,也是操作系统正常工作时的必要组成部分,在启动
169、时内核需要根文件系统来挂载和组织文件。在目前的Linux操作系统中,内核代码映像文件保存在根文件系统中,系统引导启动程序会从这个根文件系统设备上把内核执行代码加载到内存中去运行。12.4嵌入式嵌入式Linux文件系统的移植文件系统的移植n在Linux中,用户能看到的文件空间是用一个单树状结构来组织的,根文件系统的最顶层称为root,其下的每一个目录都有其具体的目的和用途,一般是根据FHS(FilesystemHierarchyStandard)定义建立一个正式的文件系统结构的。FHS即文件系统结构标准,它在UNIX/Linux操作系统的文件系统中是用于确定在何处存储何种文件的标准。n常见的根文
170、件系统有Romfs、JFFS2、NFS、ext2、RamDisk、cramfs等。12.5Linux下设备驱动程序的开发下设备驱动程序的开发nLinux驱动开发是嵌入式软件设计中的主要内容,也是嵌入式Linux移植中工作量最大的部分。这里主要概述Linux设备驱动框架、驱动程序的组成及常用的加载驱动程序的方法,并通过一实例来详细介绍字符设备驱动程序的开发过程。12.5.1驱动程序概述驱动程序概述n设备驱动程序是应用程序与硬件之间的一个中间软件层,可以看作是一个硬件抽象层(HAL,HardwareAbstractLayer),为应用程序屏蔽了硬件的细节。在应用程序看来,硬件设备只是一个设备文件,
171、应用程序可以像操作普通文件一样对硬件设备进行操作;在操作系统看来,设备驱动程序是内核的一部分,它主要实现的功能有:对设备进行初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据,回送应用程序请求的数据以及检测和处理设备出现的错误。n1.设备类型分类设备类型分类n在Linux操作系统下有3类主要的设备文件类型:字符设备、块设备、网络设备。字符设备和块设备的主要区别在于是否使用了缓冲技术。n(1)字符设备字符设备n字符设备(chardevice)和普通文件之间有主要的区别:普通文件可以来回读/写,而大多数字符设备仅仅是数据通道,只能顺序读/写。n(2)块设备)块设
172、备n块设备(blockdevice)是文件系统的物质基础,它也支持像文件一样被访问。n(3)网络设备)网络设备n网络设备是一个物理设备,如以太网卡,但软件也可以作为网络设备,典型的是回送设备(loopback)。n2.设备驱动与文件系统的关系设备驱动与文件系统的关系nLinux通过设备文件系统对设备进行管理,各种设备都以文件的形式存放在/dev目录下,称为“设备文件”。应用程序可以像普通文件一样打开、关闭和读/写这些设备文件。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。12.5.2重要的数据结构和函数重要的数据结构和函数n1.设备驱动中关键数据结构设备驱动中关键数据
173、结构n设备驱动程序所提供的这组入口点由几个结构向系统进行说明,分别是file_operations数据结构、inode数据结构和file数据结构。n2.设备驱动开发中的基本函数设备驱动开发中的基本函数n设备驱动程序所提供的入口点,在设备驱动程序初始化的时候向系统进行说明,以便系统在适当的时候调用。同时,初始化部分一般还负责为设备驱动程序申请系统资源,包括内存、中断、时钟、I/O端口等(这些资源也可以在open子程序或别的地方申请),在这些资源不用的时候,应该释放它们,以利于资源的共享。n(1)设备注册函数设备注册函数n(2)内存操作函数内存操作函数n(3)中断申请和释放函数中断申请和释放函数n
174、(4)I/O端口操作函数端口操作函数12.5.3字符设备驱动程序的字符设备驱动程序的组成组成n设备驱动程序作为内核的一部分它完成的功能包括:对设备初始化和释放;把数据从内核传送到硬件并从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。n1.驱动程序的注册和注销驱动程序的注册和注销n设备驱动程序通过命令insmod以模块的方式动态加载后此时的入口点是init_module函数或宏mdolueinit。n2.设备操作设备操作n在设备成功注册之后,就可对它进行打开、读写、控制和释放等操作。在Linux内核中,字符设备使用fie_operations结
175、构来定义设备的各种操作集合。n3.驱动程序的中断处理驱动程序的中断处理n在实际的系统中,设备的许多工作通常与处理器不同步,而且总比处理器慢。如果让处理器一直等待到设备准备好时才进行操作会造成处理器资源的浪费一种好的方法就是在设备准备好后通知处理器来进行处理,这种方法就是中断。由于系统的中断资源有限驱动程序在使用中断前需要申请,使用完后需要释放。Linux中,中断的申请和释放分别是通过nrequest_irq()函数和free_rq()函数来实现的。12.5.4动态和静态加载方式动态和静态加载方式nLinux设备驱动模块属于内核的一部分,可以用静态和动态两种方式来进行编译和加载。两者方式的开发过
176、程稍有不同,也各有特点。n1.静态加载方式n(1)特点n静态方式就是将驱动程序的源代码事前放到内核源代码中,和整个内核一起编译。它需要修改内核源代码和文件系统,并重新烧录下载到嵌入式设备中,这样当内核启动时就会加载驱动程序。n(2)内核的修改n设备驱动程序写完后,就可以将文件加到linux的内核中了。n(3)文件系统的修改n在内核中加上驱动程序后,还不能直接在应用程序中使用驱动程序中的函数,如open,close等,因为还需要在文件系统中提供设备访问接口,也就是/dev/目录下的设备名与设备号。n2.动态加载方式n(1)特点n动态加载方式就是说将驱动程序编译成一个可加载、卸载的模块目标文件,然
177、后添加到内核中去即可。这种方法的好处就是通过将于内核中一些不常用的驱动采取动态加载方式,从而可以减少内核的大小,并且模块被插入内核后,它就和内核其他部分一样可方便的被使用。n(2)驱动程序添加到内核中n对于动态驱动程序的源代码,其初始化函数和静态方式的定义不同,这里要用这样一些函数:int_initdevice_init(void);void_exitdevice_exit(void);module_init(device_init);module_exit(device_exit)。n(3)文件系统下设备名的创建n驱动添加安装好后,还需要修改文件系统.12.6应用程序开发应用程序开发n基于嵌
178、入式Linux的应用程序开发方式与流程,与基于Windows的应用程序开发有很大的不同。在Windows环境中,开发者习惯使用各种功能强大的集成编译开发环境(IDE),完成程序编辑、编译后,直接运行即可。但在基于嵌入式Linux的应用程序开发过程中,目前还缺乏比较简单、高效的开发工具和手段,同时,由于应用程序的最终运行平台是嵌入式目标系统,而程序开发与调试又仍然需要借助PC平台的桌面系统来完成,因而在程序的开发与调试过程中,需要频繁地将目标文件从桌面Linux系统中加载到嵌入式目标设备中,这是一个相对比较耗时的过程。12.6.1应用程序的加载方式应用程序的加载方式n在桌面Linux上编辑源文件
179、,交叉编译生成ELF可执行文件后,然后需要将生成的可执行文件加载到嵌入式目标系统上运行。此过程的实现有多种方式,最为常见的方式有U盘拷贝、FTP下载和NFS挂载方式。12.6.2应用程序的应用程序的GDB/GDBSERVER联机调试联机调试n在前面的章节中我们介绍了基于ADS以及单机环境下的多种调试方式,但是嵌入式Linux的联机环境下,常用的调试代理工具为GDBSERVER。它是一个轻量级的调试器,运行在目标机上,然后与运行在主机上的GDB通过RSP(RemoteSerialProtoco1)协议进行通讯从而完成远程联机调试工作。n1.GDB/GDBSERVER调试模型调试模型n在调试过程中
180、,主机和目标机之间使用串口或者网络作为通信的通道.n2.RSP通讯协议通讯协议nRSP协议将GDBGDBSERVER间通讯的内容看做是数据包,数据包的内容都使用ASCII字符。每一个数据包都遵循这样的格式:$调试信息接受方在收到数据包之后,对数据包进行校验,若正确回应“+”,反之回应。n3.调试步骤调试步骤n(1)交叉编译被调试程序文件n(2)运行嵌入式目标机中的GDBSERVER,并加载被调试程序文件n(3)运行宿主机中的gdb,并远程连接目标机的调试程序12.6.3字符设备应用程序的开发字符设备应用程序的开发n在前面介绍了LED设备驱动程序的开发,那么就可以编写应用程序使用了设备驱动程序中
181、的函数了。下面是一个LED应用程序的源码,假设程序文件名为ledApp.c。12.6.3字符设备应用程序的开发字符设备应用程序的开发n#includen#includen#includen#includenintmain(intargc,char*argv)nninton;nintled_no;nintfd;nif(argc!=3|sscanf(argv1,“%d”,&led_no)!=1|sscanf(argv2,%d,&on)!=1|on1|LED_NO1)nfprintf(stderr,“Usage:ledappLED_NO0|1n”);/*0代表熄灭,1代表点亮*/nexit(1);n
182、nfd=fopen(“/dev/myLeds”,0);nif(fd分类中选择预处理n在包含路径中填入./include,./include/minigui,./include/pthread-win32n在LINKE目录下选择general,然后把LIB里的库文件加在对象/库模块的后面,也就是那两个minigui.libpthreadVC1.lib,n最后在分类中选择INPUT把库文件地址连接进去也就是./LIBn打开wvfb文件夹下的wvfb.exe,然后在vc+中执行helloworld.n这里要注意的是,MiniGUIforWin32是预编译的函数库,因此,其中的功能特性是固定的,包括编
183、译时选项和运行时选项。13.4.2MiniGUI的移植的移植n1.PC机上配置,编译,安装,运行机上配置,编译,安装,运行MiniGUIn(1)在redhat9.0上配置FrameBuffern要激活VESAFrameBuffer驱动程序,需要修改/boot/grub/menu.lst文件,并在kernel打头的一行添加vga=0x0317.n其中RedHatLinux(2.4.208,FrameBuffer)就是设置了VESAFrameBuffer的引导选项。n(2)在)在PC上编译并安装上编译并安装MiniGUI开发开发包包n第一步,在PC上编译并安装libminiguin第二步,在PC上
184、安装MiniGUI的资源n第三步,编译应用程序例子n(3)Redhat上MiniGUI的运行n由于必须要在控制台模式才能运行MiniGUI。要启动控制台,按住CtrlAlt的同时,n按F1F6中的任意一个均可,然后登录系统,进入sample目录,直接运行即可。n2.交叉编译,并在嵌入式目标机上运行交叉编译,并在嵌入式目标机上运行MiniGUIn(1)交叉编译libminiguin(2)安装MiniGUI资源文件n(3)编译应用程序例子n(4)在嵌入式目标机上运行MiniGUI13.4.3MiniGUI应用编程库应用编程库nMiniGUI在嵌入式产品中移植好后,就要进行相应的产品应用程序的开发。
185、应用程序开发人员可以直接调用MiniGUI的窗口以及图形接口编写自己的应用程序,也可以利用MiniGUI内建的各种控件(control/widget)来快速开发自己的应用程序。MiniGUI提供了各种丰富的控件,如按钮,工具栏等。同时还为开发者提供了自定义控件的接口,并能方便地对已有控件进行扩展。13.5Qt/Embeddedn13.5.1Qt/Embedded的应用架构的应用架构nQt/Embedded简称为Qt/E,是著名的QT开发商Trolltech推出的面向嵌入式系统领域的QT开发包,是一组用于访问嵌入式设备的QtC+API函数库。它以原始桌面Qt为基础,并做了许多出色的调整以适用于嵌
186、入式环境,提供了丰富的窗口小部件(Widgets),并且还支持窗口部件的定制,可为用户提供漂亮的图形界面。13.5Qt/EmbeddednFramebuffer是Linux2.2.x以上内核的版本中的提供的一种图形驱动程序接口。这种接口采用内存映射(mmap)系统调用,将显示设备抽象为帧缓冲区。用户可以将它看成是显示内存的一个映象,将其映射到进程地址空间之后,就可以直接进行读写操作了,而写操作可以立即反映在屏幕上。Framebuffer驱动程序是最重要的驱动程序之一,正是这个驱动程序才能使系统屏幕显示内容。13.5.2QVFB虚拟运行环境虚拟运行环境nQVFB是“Qt/EmbeddedVirt
187、ualFramebuffer”的简称,即Qt/E的虚拟帧缓冲,是在桌面Linux中为嵌入式图形应用程序提供一个虚拟的运行环境。它实际上就是由一个X11的应用程序在共享的内存去也中来模拟了一个缓冲帧,通过指定显示设备的宽度,高度和颜色深度,虚拟出来的缓冲帧将和物理的嵌入式显示设备在每个像素上保持一致。n使用QVFB的方法如下:n将Qt/E源代码中的qvfb目录作为正常的x86PC平台的Qt/X11应用程序来编译,而不要做为Qt/E的应用程序来编译。n这时就可以运行一些已经编译后的二进制Qt/E应用程序,命令行中要用参数-qws表示将它作为Qt/E服务器。13.6Qt开发及运行环境的创建开发及运行
188、环境的创建n13.6.1Qt/E应用程序在应用程序在PC虚拟平台上虚拟平台上的运行的运行n1、Qt/E应用程序在PC虚拟平台上的运行n首先首先,下载Qt开发环境的源程序包。n2、Qt/E应用程序在嵌入式设备中的运行n要将写好的Qt/E应用程序移植到嵌入式设备上运行,需要对Qt/Embedded采用交叉编译的方式重新进行编译,生成一些适合于嵌入式Linux的函数库。n3、Qtopia窗口系统在PC虚拟平台上的运行n要在PC平台上建立起Qtopia窗口系统的虚拟运行环境,除了同前面一样要编译BuildQt/X11、BuildQvfb(QTEDIR/tools/qvfb)和Buildlibqte外,
189、就另外还需要编译Qtopia这个GUI系统。n4、Qtopia窗口系统在嵌入式设备中的运行n实际上就是将Qtopia窗口系统移植到嵌入式设备中的运行,其前面几个步骤同“Qt/E应用程序在嵌入式设备中的运行”,只是最后将需要拷贝到开发板上的文件整理到一个单独的文件夹里面,然后再下载烧录到嵌入式设备的FLASH中。13.6.2Qt/E应用程序在嵌入应用程序在嵌入式设备中的运行式设备中的运行n要将写好的Qt/E应用程序移植到嵌入式设备上运行,需要对Qt/Embedded采用交叉编译的方式重新进行编译,生成一些适合于嵌入式Linux的函数库。移植过程中都是采取宿主机和目标机的开发模式。宿主机是一台运行
190、RedhatLinux的PC机,目标机是基于S3C2410的嵌入式设备。n先在宿主机上调试通过后,再移植到目标机上。详细步骤如下:n(1)BuildQt/Embeddedn(2)修改tmake配置文件n(3)生成可执行文件13.6.3Qtopia窗口系统在窗口系统在PC虚拟平台上的运行虚拟平台上的运行n要在PC平台上建立起Qtopia窗口系统的虚拟运行环境,除了同前面一样要编译BuildQt/X11、BuildQvfb(QTEDIR/tools/qvfb)和Buildlibqte外,就另外还需要编译Qtopia这个GUI系统,13.6.4Qtopia窗口系统在嵌窗口系统在嵌入式设备中的运行入式
191、设备中的运行n实际上就是将Qtopia窗口系统移植到嵌入式设备中的运行,其前面几个步骤同“Qt/E应用程序在嵌入式设备中的运行”,只是最后将需要拷贝到开发板上的文件整理到一个单独的文件夹里面,然后再下载烧录到嵌入式设备的FLASH中。13.7Qt应用程序的开发应用程序的开发n13.7.1Qt/E基本控件基本控件1.窗体交互界面。窗体交互界面。Qt拥有丰富的满足不同需求拥有丰富的满足不同需求的窗体,使用起来很灵活,而且它容易被子类的窗体,使用起来很灵活,而且它容易被子类化,以满足不同的需求。窗口部件是用户界面化,以满足不同的需求。窗口部件是用户界面的一个原子:它从窗口系统接收鼠标、键盘和的一个原
192、子:它从窗口系统接收鼠标、键盘和其它事件,并且在屏幕上绘制自己的表现。每其它事件,并且在屏幕上绘制自己的表现。每一个窗口部件都是矩形,并且它们按一个窗口部件都是矩形,并且它们按Z轴顺序轴顺序排列的。一个窗口部件可以被它的父窗口部件排列的。一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分。或者它前面的窗口部件盖住一部分。13.7.1Qt/E基本控件基本控件n2、对话框、对话框n使用可视化Qt图形设计器设计工具用户可以建立自己的对话框。Qt使用布局管理自动设置窗体和别的窗体之间相对的尺寸和位置,这样可以确保对话框能够最好的利用屏幕上的可用空间。nQt提供很多常用的对话框,例如,选择文
193、件等。13.7.2开发工具开发工具Qtdesigner介绍介绍nQt图形设计器是一个具有可视化用户接口的设计工具。Qt的应用程序可以完全用纯命令行下的代码编写,也可Qt图形设计器这种集成开发环境来开发。13.7.3控制台上的控制台上的Qt/E应用编程应用编程n所谓控制台上的Qt/E应用程序,就是指利用Qt/E库函数设计的图形化应用程序,直接在Linux的控制台下启动运行,而不是在GUI桌面系统中运行。这里的控制台可以目标机的嵌入式Linux,也是宿主机的桌面Linux,只是后者要先运行qvfb。n1.一个典型的Qt应用程序分析n/hello.cpp精简的源程序n1#includen2#incl
194、uden3intmain(intargc,char*argv)n4n5QApplicationapp(argc,argv);n6QLabel*hello=newQLabel(HelloQt/Embedded!,0);n7app.setMainWidget(hello);n8hello-show();n9returnapp.exec();n10n第1行和第2行包含了两个头文件,这两个头文件中包含了QApplication和QLabel类的定义。n第5行创建了一个QApplication对象,用于管理整个程序的资源,它需要2个参数,因为Qt本身需要一些命令行的参数。n第6行创建了一个用来显示Hel
195、loQt/Embedded!的部件。在Qt中,部件是一个可视化用户接口,按钮、菜单、滚动条都是部件的实例。部件可以包含其它部件,例如,一个应用程序窗口通常是一个包含QMenuBar、QToolBar、QStatusBar和其它部件的一个部件。在QLabel函数中的参数0表示,这是一个窗口而不是嵌入到其它窗口中的部件。n第7行设置hello部件为程序的主部件,当用户关闭主部件后,应用程序将会被关闭。如果没有主部件的话,即使用户关闭了窗口程序也会在后台继续运行。n第8行使hello部件可视,一般来说部件被创建后都是被隐藏的,因此可以在显示前根据需要来订制部件,这样的好处是可以避免部件创建所造成的闪
196、烁。n第9行把程序的控制权交还给Qt,这时候程序进入就绪模式,可是随时被用户行为激活,例如点击鼠标、敲击键盘等。n2.完成一个Qt应用程序的建立步骤n第一步:新建一个空的工程文件(.pro)n第二步:新建一个用户接口窗体(.ui)n第三步:生成hello.h和hello.cppn第四步:编写主函数main()n第五步:修改工程文件hello.pron第六步:生成Makefile文件13.7.4Qtopia上的上的Qt/E应用编程应用编程n要想将开发的应用程序添加到GUI窗口系统qtopia中运行,则还需要完成下面步骤:n1.准备好camera程序的图标文件n2.建立.desktop文件,这是最为重要的。n3.运行