深入理解嵌入式Linux设备驱动程序(txt+pdf+epub+mobi电子书下载)


发布时间:2020-05-30 04:37:38

点击下载

作者:曹国辉

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

深入理解嵌入式Linux设备驱动程序

深入理解嵌入式Linux设备驱动程序试读:

前言

随着计算机技术及相关技术的发展,嵌入式技术已经在通信、网络、工控、航空航天、医疗电子等领域得到了广泛的应用。近些年来,三网融合、智能电网、物联网等战略性新兴产业的快速发展也为嵌入式产业带来巨大的发展机遇。而嵌入式操作系统,作为驱动嵌入式系统硬件的灵魂,在嵌入式技术中占据着极其重要的地位。

智能化、网络化是当今电子设备发展最重要的趋势。这种趋势给设备软件带来的最大挑战就是开发成本和复杂度激增,对于任何一家企业,使用自己的嵌入式操作系统根本不可能适应市场竞争的需求和压力。与此同时,Linux正在成为嵌入式软件平台的重要选择,由于免费且开放源代码,不仅显著降低了嵌入式操作系统的使用成本,而且可以从广泛的开放源代码社区获得丰富的资源。因此越来越多的嵌入式企业开始选择Linux系统作为公司产品的软件平台。

但是,Linux因为其开源特性,使得开放的源码质量难以进行控制,同时相关的开发工具也没有商业操作系统便利,这就对嵌入式Linux开发人员的技术水平提出了更高的要求。嵌入式驱动程序开发是嵌入式软件开发中的最底层,与硬件密切相关,也是最难的一部分,因此,广大嵌入式开发人员迫切需要一本详细讲解如何进行嵌入式Linux驱动开发的教程,本书正是为了满足广大嵌入式开发人员这一需求而编写的。

本书站在一个驱动开发工程师的角度,以实际开发板硬件为基础,循序渐进地详细讲述了嵌入式Linux驱动程序开发的方法。全书主要分为三大部分:

第一部分(1~4章)阐述了在没有操作系统情况下嵌入式系统的硬件体系架构,裸机程序的启动运行过程,BootLoader的功能及U-boot引导系统的过程。

第二部分(5~8章)重点讲解了嵌入式Linux设备驱动开发基础及基本理论,包括嵌入式Linux模块编程、Linux设备及设备驱动模型、Platform平台设备驱动、设备驱动程序中常用的并发控制,阻塞与非阻塞,轮询、异步通知等基本Linux设备驱动程序开发核心技术。

第三部分(9~15章)详细分析了常用的硬件设备驱动程序,包括LCD屏设备驱动、触摸屏设备驱动、网卡设备驱动、I2C设备驱动、USB设备驱动等。这是对Linux设备驱动程序架构模型及基本理论的具体应用。

全书思路清晰,环环相扣,融为一体。

本书的前身是南京英贝得嵌入式培训中心的高级班嵌入式Linux设备驱动班的讲义,在编写时,立足于实践,理论和实践相结合。书中所有提到的相关程序,笔者都亲自在电脑里调试运行过,在阐述每一章节时,遵循提出问题、分析问题、解决问题的思路,以问题为中心,一步步分析Linux设备驱动程序架构,分析Linux设备驱动架构是如果解决现实中的问题及其优缺点。在阐述每一个具体设备驱动程序时,遵循从硬件原理分析、无操作系统的驱动程序、Linux下的驱动程序的思路进行讲解分析,符合读者接受新知识的思维。

本书的特色主要在于不但详细讲解了怎么做,更重要的是分析了为什么要这么做,在分析Linux内核驱动源码的基础上,提炼出了Linux驱动架构模型的核心思想及实现思路;基于对每个具体设备的Linux设备驱动源码的分析,详细阐述了整个设备驱动程序的工作流程及实现思路,在理解整个Linux设备驱动流程和实现思路的基础上,阐述了编写设备驱动和移植设备驱动的基本思路和方法。

全书是笔者多年项目开发和教学经验的总结,可作为高校嵌入式专业教材以及广大嵌入式爱好者的参考书。

本书由曹国辉和曾志鹏共同完成,第1、8、10、11、12、13、14、15章由曹国辉编写,第2、3、4、5、6、7、9章由曾志鹏编写,全书由曹国辉整理定稿。

本书在编写过程中引用了一些互联网上的文献资料,无法一一注明出处,在此向原作者表示感谢。由于笔者水平有限,书中难免存在错误与不妥之处,欢迎广大读者朋友不吝赐教。

联系邮箱:guohuicao@126.com。

本书源代码可在电子工业出版社官网“在线资源”中下载。

曹国辉 曾志鹏第1章嵌入式ARM系统开发基础本章目标● 掌握嵌入式ARM集成开发环境ADS1.2的使用方法● 掌握嵌入式ARM应用系统的启动过程● 掌握嵌入式ARM裸机中断处理过程● 理解ARM应用程序加载地址和运行地址的概念● 掌握嵌入式ARM应用系统的基本程序设计方法1.1 嵌入式ARM系统开发概述

嵌入式ARM系统开发是指在ARM裸机上进行开发,在ARM上没有“跑”任何操作系统和驱动,全部由自己编程实现,相当于把ARM当做高级单片机来使用。下面介绍嵌入式ARM系统开发的一些基础知识。1.1.1 ARM系统可执行映像文件格式

ARM系统的可执行映像文件格式主要有ELF(.elf)、AXF(.axf),和BIN(.bin),下面对这三种映像格式文件分别进行介绍。

ELF:Linux操作系统下可执行映像文件格式,在Linux环境下用GCC编译器生成的可执行映像文件格式即为ELF格式,在Linux操作系统下可直接运行。

AXF:ARM的调试文件,由ARM集成开发工具ADS生成,除了包括可执行代码外,还包括其他调试信息,用于ADS调试。

BIN:真正的可执行文件,包括ARM可执行的指令和数据,可以使用相关工具(Objcopy)从ELF文件中生成,写到FLASH或RAM中可直接运行。

ARM中的各种源文件(包括汇编文件、C语言程序及C++程序等)经过ARM编译器编译后生成ELF(Executable and Linking Format)格式的目标文件。这些目标文件和相应的C/C++运行时所用到的库经过ARM连接器处理后,生成ELF格式的映像文件(Image),这种ELF格式的映像文件是一种可执行文件。

BIN文件是真正的可执行文件。AXF文件是ARM的调试文件,除了包含BIN的内容之外,还附加了其他的调试信息。这些调试信息加在可执行的二进制数据的前面,所以把AXF文件写到ARM的指令执行地址(一般是0x0)将不能运行;因为在此地址前几十个字节的数据不是可执行的二进制数据,而是头部的调试信息。而BIN文件正是去掉了调试信息的可以执行的“精华”部分。

可执行映像文件主要分为3个段,即RO段、RW段和ZI段,如下图所示。● RO段:只读代码段;● RW段:RW区域放的是已赋值(赋0除外)的全局变量;● ZI段:ZI区域放的是未赋值的全局变量或初始化为0的全局变

量。1.1.2 ARM开发调试工具

ARM公司为ARM系统的开发提供了图形化的集成开发环境——ADS,目前使用的版本是ADS1.2;ADS提供了项目代码管理、编辑、编译等功能。关于ADS的使用,读者可参考其他相关资料,这里主要介绍一些ARM的调试系统框架及基本原理。

ARM芯片本身提供了在线调试功能,这使得我们可以在线进行实时调试,不需要再像其他单片机那样使用仿真器进行开发调试。ARM CPU提供了JTAG接口,通过JTAT接口我们可以给ARM发送调试命令,实现访问ARM内部的指令寄存器和数据寄存器,暂停程序的执行等调试功能。同时通过JTAG接口,我们可以烧写程序到系统的Nor FLASH或Nand FLASH上。ARM的在线调试功能正是通过JTAG口来实现的,典型的ARM系统调试框图如下图所示。

典型的ARM调试系统主要分为三部分:调试主机、协议转换器(调试代理)、目标机系统(调试目标)。调试主机一般是一台运行调试软件(如ADS)的计算机。调试主机可以发出一些高层的调试命令,如设置断点、访问内存等。

协议转换器(如MULTI-ICE)用来将调试主机发出的高层调试命令转换为底层的ARM JTAG调试命令。

调试目标一般就是指基于ARM内核MCU目标开发板。经过协议转换器进行命令解释,主机上运行的调试软件就可以通过JTAG接口直接和ARM内核对话。通过扫描链,可以把ARM/THUMB指令插入到ARM的指令流水线当中去执行。通过插入特定的ARM指令,我们可以检查、保存或者改变内核和系统的状态。为了支持底层的调试,ARM处理器提供了硬件上的调试扩展。这些调试扩展包括:● 停止程序的运行;● 检查和修改ARM920T的内核状态;● 观察和修改内存;● 恢复程序的运行。1.1.3 加载地址和运行地址

由于ARM系统开发的应用程序在运行和没有运行时所在的位置可能不一样,例如,应用程序没有运行时存放在Nand FLASH中,运行时被搬到RAM中执行,所以就引出了加载地址(应用程序的加载)和运行地址(应用程序的运行)的概念。● 加载地址:映像文件位于存储器(还没有运行,一般在ROM

中,也可以在RAM中)时的地址;● 运行地址:映像文件运行时的地址。

应用程序的加载和运行地址在应用程序链接时通过链接选项指定,如下图所示。

看看ADS开发文档ARM Developer suite 1.2中的ADS_CodeWarriorIDEGuide.pdf怎么说的: RO Base This text field sets both the load address and execution address of the region containing the RO section. If you do not enter a value,the value defaults to 0x8000。

从ADS的帮助文档知道,RO Base设置的是加载地址和运行地址,在这里加载地址和运行地址是一致的,通过ADS调试裸机程序时,映像文件首先要加载到目标板的内存中。

ADS链接器预定义如下变量来表示应用程序运行时的地址,ADS的预定义变量和含义分别如下:● |Image$$RO$$Base|:RO段起始地址;● |Image$$RO$$Limit|:RO段结束地址加1;● |Image$$RW$$Base|:RW段起始地址;● |Image$$RW$$Limit|:RW段结束地址加1;● |Image$$ZI$$Base|:ZI段起始地址;● |Image$$ZI$$Limit|:ZI段结束地址加1。1.2 嵌入式ARM系统的启动代码分析1.2.1 ARM上电启动概述

ARM系统上电启动后,从0x0地址处开始执行,根据系统的配置0x0地址可以映射在Nor FLASH或SDRAM中,在Nor FLASH或Nand FLASH中存放有系统的启动初始化程序,与计算机的BIOS类似,完成系统最底层的初始化工作。

ARM系统上电后,首先就运行系统初始化程序,系统初始化程序主要完成系统最基本的硬件初始化,为后面的C语言应用程序提供运行环境。ARM系统初始化启动代码完成的主要功能如下:● 初始化ARM CPU异常处理向量表;● 禁止看门狗;● 禁止中断;● 初始化系统时钟,包括CPU主频FCLK、系统总线时钟频率HCLK、

外设总线时钟频率PCLK;● 初始化SDRAM控制器;● 设置ARM CPU在各种模式下的栈指针(栈顶);● 设置ARM中断向量表,安装中断处理程序;● 搬运可执行映像文件的RW段到RAM中,并初始化ZI段为0;● 跳转到C语言应用程序的Main函数,开始执行C语言应用程序。

到此,系统的初始化启动程序就完成了ARM系统的启动过程。

ARM系统的初始化启动代码一般用汇编语言编写,根据以上的分析,我们知道ARM系统初始化启动程序的流程如下图所示。1.2.2 ARM上电初始化启动代码分析

ARM系统的初始化启动代码源文件为2440init.s,是汇编语言源文件。下面对汇编启动代码的主要部分进行分析讲解。 GET option.inc GET memcfg.inc GET 2440addr.inc

程序的开始是通过GET汇编伪指令设置该源码文件要用到的一些头文件的,其中option.inc定义ARM系统的一些配置选项,在这里主要定义中断向量起始地址_ISR_STARTADDRESS和系统时钟的分频系数M_MDIV、M_PDIV、M_SDIV等。GET伪指令也可以用INCLUDE伪指令替代。 UserStack EQU (_STACK_BASEADDRESS-0x3800); 0x33ff4800~ SVCStack EQU (_STACK_BASEADDRESS-0x2800); 0x33ff5800~ UndefStack EQU (_STACK_BASEADDRESS-0x2400); 0x33ff5c00~ AbortStack EQU (_STACK_BASEADDRESS-0x2000); 0x33ff6000~ IRQStack EQU (_STACK_BASEADDRESS-0x1000); 0x33ff7000~ FIQStack EQU (_STACK_BASEADDRESS-0x0); 0x33ff8000~

接下来程序定义ARM CPU在各种工作模式下的栈顶位置,在系统初始化程序的最后阶段需要设置CPU在各种工作模式下的栈顶指针(SP寄存器),为C语言程序的运行做准备。

_STACK_BASEADDRESS定义在option.inc文件中,如下所示。 ; Start address of each stacks, _STACK_BASEADDRESS EQU 0x33ff8000

其中以“;”开始的行代表注释。 MACRO $HandlerLabel HANDLER $HandleLabel $HandlerLabel sub sp,sp,#4; decrement sp(to store jump address) stmfd sp!,{r0}; PUSH the work register to stack ldr r0, = $HandleLabel; load the address of HandleXXX to r0 ldr r0,[r0]; load the contents(service routine start address) of HandleXXX str r0,[sp,#4]; store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc}; POP the work register and pc(jump to ISR) MEND

上面这段代码定义了一个宏,该宏实现的主要功能是把中断服务程序首地址HandleLabel装载到指令寄存器(PC寄存器)中,当中断产生时,系统能正确执行中断服务处理程序。在后面有关的中断系统处理的章节中,我们会详细分析该宏的使用方法。

接下来我们通过IMPORT伪指令导入启动代码中需要用到的外部符号。 ; declare ARM-linker internel self-define variable and Main IMPORT |Image$$RO$$Limit|; End of ROM code ( = start of ROM data) IMPORT |Image$$RW$$Base|; Base of RAM to initialise IMPORT |Image$$ZI$$Base|; Base and limit of area IMPORT |Image$$ZI$$Limit|; to zero initialise IMPORT Main

Main为C语言程序的入口函数,也可以改成别的。

接下来才是真正系统初始化程序的开始。首先使用AREA伪指令定义代码段,段名为Init,在链接时,通过链接选项把Init段链接到可执行映像文件的第一个段,如下所示。 AREA Init,CODE,READONLY ENTRY b ResetHandler b HandlerUndef ; handler for Undefined mode b HandlerSWI ; handler for SWI interrupt b HandlerPabort ; handler for PAbort b HandlerDabort ; handler for DAbort b . ; reserved b HandlerIRQ ; handler for IRQ interrupt b HandlerFIQ ; handler for FIQ interrupt ; @0x20 b . ; Must be @0x20.

ENTRY伪指令定义代码段的入口。系统启动程序的前32字节用来存放ARM异常向量表。当异常发生时,CPU自动跳转到异常向量表处执行异常处理程序。当系统上电时,首先执行第一条指令“b ResetHandler”,该代码通过一条跳转指令跳到ResetHandler处执行复位处理程序。

代码中“b.”表示跳到当前位置,“.”表示当前指令位置。 ResetHandler ldr r0, = WTCON ; watch dog disable ldr r1, = 0x0 str r1,[r0] ldr r0, = INTMSK ldr r1, = 0xffffffff ; all interrupt disable str r1,[r0] ldr r0, = INTSUBMSK ldr r1, = 0x3ff ; all sub interrupt disable str r1,[r0] ; To reduce PLL lock time, adjust the LOCKTIME register ldr r0, = LOCKTIME ldr r1, = 0xffffff str r1,[r0] ; Added for confirm clock divide. for 2440. set pll ; Setting value Fclk: Hclk: Pclk ldr r0, = CLKDIVN ldr r1, = CLKDIV_VAL ; 3 = 1: 2: 4 str r1,[r0] ; Configure UPLL ldr r0, = UPLLCON ldr r1, = ((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV) str r1,[r0]

系统复位处理程序ResetHandler首先禁止看门狗,再禁止中断,然后设置CPU系统时钟和时钟分频系数,最后设置USB时钟和相应的分频系数。

设置好系统时钟后,紧接着开始初始化SDRAM控制器,主要设置SDRAM的总线宽度和操作时序等。设置完SDRAM控制器后,SDRAM就可以正常工作了。 ; Set memory control registers ldr r0, = SMRDATA ldr r1, = BWSCON ; BWSCON Address add r2, r0, #52 ; End address of SMRDATA 0 ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne%B0 ; Initialize stacks bl InitStacks

完成SDRAM控制器的设置后,通过调用InitStacks函数来初始化ARM CPU在各种工作模式下的栈指针(SP寄存器)。

InitStacks函数的工作流程比较简单,首先设置CPU相应的工作模式,然后把在该工作模式下的栈指针寄存器SP的值设置为对应的栈顶地址即可。通过修改当前程序状态寄存器CPSR来修改CPU的当前工作模式。InitStacks代码如下所示。 InitStacks ; Don't use DRAM,such as stmfd,ldmfd… ; SVCstack is initialized before ; Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1' mrs r0,cpsr bic r0,r0,#MODEMASK orr r1,r0,#UNDEFMODE|NOINT msr cpsr_cxsf,r1 ; UndefMode ldr sp, = UndefStack ; UndefStack = 0x33FF_5C00 orr r1,r0,#ABORTMODE|NOINT msr cpsr_cxsf,r1 ; AbortMode ldr sp, = AbortStack ; AbortStack = 0x33FF_6000 orr r1,r0,#IRQMODE|NOINT msr cpsr_cxsf,r1 ; IRQMode ldr sp, = IRQStack ; IRQStack = 0x33FF_7000 orr r1,r0,#FIQMODE|NOINT msr cpsr_cxsf,r1 ; FIQMode ldr sp, = FIQStack ; FIQStack = 0x33FF_8000 bic r0,r0,#MODEMASK|NOINT orr r1,r0,#SVCMODE msr cpsr_cxsf,r1 ; SVCMode ldr sp, = SVCStack ; SVCStack = 0x33FF_5800 ; USER mode has not be initialized. mov pc,lr

系统栈初始化完成后,接下来进行代码的重定位。所谓代码的重定位,就是把应用程序的代码段和数据段从加载地址位置搬到运行地址位置。在初始化代码中,本例选择从Nor FLASH启动,代码段的加载地址和运行地址一致,所以不需要进行代码重定位。对于RW的系统全局初始化数据段需要进行重定位,要从ROM中的位置搬到RAM中去。系统重定位代码如下所示。 ; Copy and paste RW data/zero initialized data LDR r0, = |Image$$RO$$Limit| ; Get pointer to ROM data LDR r1, = |Image$$RW$$Base| ; and RAM copy LDR r3, = |Image$$ZI$$Base| ; Zero init base = > top of initialised data CMP r0, r1 ; Check that they are different BEQ %F2 1 CMP r1, r3 ; Copy init data LDRCC r2, [r0], #4 ; --> LDRCC r2, [r0] + ADD r0, r0, #4 STRCC r2, [r1], #4 ; --> STRCC r2, [r1] + ADD r1, r1, #4 BCC %B1 2 LDR r1, = |Image$$ZI$$Limit|; Top of zero init segment MOV r2, #0 3 CMP r3, r1 ; Zero init STRCC r2, [r3], #4 BCC %B3

首先把全局初始化数据段搬运到RW运行地址位置,全局初始化数据段在ROM中的位置从RO_Limit开始,数据长度为ZI_Base – RW_Base。把全局初始化数据段搬运到RW段后,紧接着初始化ZI段数据为0,ZI段的起始地址为ZI_Base,结束地址为ZI_Limit。

本例中,程序运行后(即运行时域),RO段存于ROM中,RW段和ZI段存于RAM中,加载时域(程序还没有执行)代码段和RW数据段存于ROM中。本例中可执行映像文件的代码重定位如下图所示。

代码重定位完成后,ARM系统的启动初始化工作基本完成了,接着通过一条跳转指令跳到C代码部分,开始执行C语言应用程序代码,如下所示。 ; jump to Main ,Main start execute bl Main b .

Main为C语言应用程序代码的总入口函数,系统启动代码把控制权提交给Main后,Main开始执行,且永不返回。到此,系统启动初始化代码的任务就完成了。1.3 嵌入式ARM系统的中断系统

中断机制是计算机系统中一种非常重要的外部事件处理机制。中断机制是指在计算机执行当前任务的过程中,当外部事件发生时,计算机可以暂停当前正在执行的任务,转去处理外部事件,待外部事件处理完成后,继续恢复原来执行的任务。

ARM9采用二级中断机制。所谓二级中断,是指当外部中断产生时,系统需要通过两次跳转,才能执行真正的中断服务处理程序。下面以按键中断处理过程为例来详细说明ARM9系统中的中断处理过程。

按键使用外部中断EINT8、EINT11、EINT13、EINT14、EINT15、EINT19,它们公用一个外部中断源EINT8_23,中断号为5。

中断向量:中断服务程序的入口地址。

中断向量地址:内存中存放中断服务程序入口地址的地址。1.3.1 ARM中断机制代码分析

外部中断产生后,要想正确执行相应的中断处理程序,首先需要在内存中建立中断向量表。系统中断向量表中存放中断服务处理程序的起始地址。系统启动代码中建立的中断向量的代码如下所示。 ; interrupt service routing start address _ISR_STARTADDRESS EQU 0x33ffff00 ; ^ is same with MAP, the following also can be: ; MAP _ISR_STARTADDRESS for define interrupt service routing service ; ^_ISR_STARTADDRESS; _ISR_STARTADDRESS = 0x33FF_FF00 AREA RamData, DATA, READWRITE MAP _ISR_STARTADDRESS HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4 ; Don't use the label 'IntVectorTable' ; The value of IntVectorTable is different with the address you think it may be ; IntVectorTable ; @0x33FF_FF20 HandleEINT0 # 4 HandleEINT1 # 4 HandleEINT2 # 4 HandleEINT3 # 4 HandleEINT4_7 # 4 HandleEINT8_23 # 4 HandleCAM # 4; Added for 2440 HandleBATFLT # 4 HandleTICK # 4 HandleWDT # 4 HandleTIMER0 # 4 HandleTIMER1 # 4 HandleTIMER2 # 4 HandleTIMER3 # 4 HandleTIMER4 # 4 HandleUART2 # 4 ; @0x33FF_FF60 HandleLCD # 4 HandleDMA0 # 4 HandleDMA1 # 4 HandleDMA2 # 4 HandleDMA3 # 4 HandleMMC # 4 HandleSPI0 # 4 HandleUART1 # 4 HandleNFCON # 4; Added for 2440 HandleUSBD # 4 HandleUSBH # 4 HandleIIC # 4 HandleUART0 # 4 HandleSPI1 # 4 HandleRTC # 4 HandleADC # 4 ; @0x33FF_FFA0 END

MAP用于定义一个内存表的首地址,在这里用MAP定义的首地址为_ISR_STARTADDRESS,内存表长度为0xA0,给每一个中断源分配一个占4字节的内存单元,用来存放该中断对应的中断服务程序入口地址。其中标号HandleEINT8_23是存放EINT8_23中断服务程序的入口地址的地址;标号HandleIRQ表示存放外部中断异常处理程序的地址,即HandleIRQ本身是一个地址。在本例中,HandleIRQ的值为0x33FFFF18,内存地址0x33FFFF18开始的4字节的存储单元存放的是外部中断异常处理程序的地址。HandleEINT8_23的值为0x33FFFF34,内存地址为0x33FFFF34,开始的4字节的存储单元存放外部中断EINT8_23的中断服务程序地址。

前面提到过在系统的启动代码中定义了一个HANDLER宏,该宏的作用是把中断处理程序的入口地址加载到PC寄存器中。在接下来的代码中,使用HANDLER宏来定义外部中断异常处理程序的入口,代码如下所示。 HandlerIRQ HANDLER HandleIRQ

宏展开后,代码如下所示。 HandlerIRQ sub sp,sp,#4 ; decrement sp(to store jump address) stmfd sp!,{r0} ; PUSH the work register to stack ldr r0,= HandleIRQ ; load the address of HandleXXX to r0 ldr r0,[r0] ; load the contents(service routine start address) of HandleXXX str r0,[sp,#4] ; store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ; POP the work register and pc(jump to ISR)

HandleIRQ为前面定义的一个地址,其值为0x33FFFF18,里面存放着外部中断异常处理程序的入口地址(即IsrIRQ),在前面的中断向量表中已经定义。

在系统的启动代码中定义外部中断异常处理程序,代码如下所示。 IsrIRQ sub sp,sp,#4 ; reserved for PC stmfd sp!,{r8-r9} ldr r9,=INTOFFSET ldr r9,[r9] ldr r8,=HandleEINT0 add r8,r8,r9,lsl #2 ldr r8,[r8] str r8,[sp,#8] ldmfd sp!,{r8-r9,pc}

外部中断异常处理程序根据中断号获取该中断对应的中断服务处理程序地址,然后把该中断号对应的中断服务程序地址装载到计算机PC寄存器中,使得CPU转去执行该中断号对应的中断服务处理程序。

外部中断异常处理程序根据外部中断号(INTOFFSET)和外部中断向量首地址(HandleEINT0)来计算对应的中断的中断向量地址,然后从该中断向量地址中取出中断向量(即中断服务程序地址)装到计算机中,开始执行中断服务程序。

外部中断异常处理程序和宏HANDLER实现的功能差不多,那为什么不用HANDLER来定义外部中断异常处理程序呢?

原因在于HANDLER和IsrIRQ获取中断向量地址方式不一样,HANDLER直接获取中断向量地址,而IsrIRQ是通过中断向量基地址(HandleEINT0)和中断号(INTOFFSET)计算出该中断对应的中断向量地址的。

系统启动代码在初始化堆栈后,开始安装外部中断异常处理程序,代码如下所示。 ; Setup IRQ handler ; HandleIRQ is for save interrupt service routing address ldr r0,=HandleIRQ ; This routine is needed ldr r1,=IsrIRQ ; if there isn't 'subs pc,lr,#4' at 0x18, 0x1c str r1,[r0]

安装外部中断异常的流程非常简单,即把外部中断异常处理程序IsrIRQ的地址存放到外部中断异常向量表的HandleIRQ表项中,HandleIRQ表示存放外部中断异常处理程序地址的地址。

安装了外部中断异常处理程序后,还要继续安装外部中断EINT8_23中断处理程序。外部中断EINT8_23的中断服务程序安装在C语言源文件中,其代码如下所示。 #define pISR_EINT8_23 (*(unsigned *)(_ISR_STARTADDRESS+0x34)) pISR_EINT8_23 = (U32)KeyISR; // set up KeyBoard interrupt service routing

外部中断EINT8_23中断服务程序的安装非常简单,把外部中断EINT8_23中断服务程序的KeyISR的地址存放到外部中断向量表的EINT8_23表项中即可。1.3.2 ARM中断服务处理程序的响应过程

分析完ARM中断服务处理的相关代码后,下面来看一下按键中断的处理响应过程。

按键的中断处理流程如下:

用户按下按键时,通过外部中断EINT8_23触发外部中断异常,计算机跳转到异常向量表的外部中断异常向量地址处执行。外部中断异常向量地址里只有一条跳转指令,于是CPU开始跳转到HandlerIRQ处执行外部中断异常处理程序。外部中断异常处理程序从中断向量表获取外部中断服务处理程序IsrIRQ的入口地址,开始执行外部中断服务处理程序IsrIRQ。IsrIRQ根据外部中断号EINT8_23和外部中断向量表首地址计算该中断号对应的中断向量地址,然后从该中断向量地址中取出该中断向量(即该中断服务程序的入口地址)装载到计算机寄存器中,于是开始执行真正的中断服务处理程序KeyISR。1.4 按键中断实验

1)任务描述

在ARM裸机环境下,开发应用程序主要完成如下功能:当按下某一个按键时,对应的LED灯亮。对按键的响应要求采用中断机制实现。

2)分析设计

这个任务看起来很简单,但因为是在裸机下,所以其实也不容易。通过分析,我们把该任务分成以下几个小任务:● 设计ARM系统的启动代码;● 设计ARM系统的中断服务程序;● 设计ARM系统的主程序。

中断处理流程:通过分析,我们知道该程序的中断服务处理程序流程如下图所示。第2章嵌入式Linux设备驱动程序开发环境的构建本章目标● 掌握嵌入式Linux设备驱动程序开发环境的构建方法● 掌握ubuntu的安装及配置● 掌握交叉编译器的安装方法开发嵌入式Linux应用程序或驱动程序需要在Linux环境下进行开发,所以需要在计算机上安装Linux操作系统。常用的Linux操作系统有Redhat、ubuntu等。在计算机上安装Linux操作系统,有两种方式:直接安装Linux操作系统或者在虚拟机上安装Linux操作系统。我们以在虚拟机上安装现在使用非常方便的ubuntu操作系统为例,讲解嵌入式Linux设备驱动程序开发环境构建的方法和步骤。2.1 安装虚拟机软件VMware 6.0.2

虚拟机安装版本为VMware 6.0.2。

在Windows XP操作系统下,用鼠标双击VMware-workstation-6.0.2-59824图标,开始安装虚拟机VMware,如下图所示。

双击VMware-workstation-6.0.2-59824图标,出现VMware的安装界面,所有的选项都采用默认值,用鼠标单击【下一步】按钮,然后出现安装进度条,系统开始安装VMware,等待安装完成后,出现如下图所示的安装完成界面。用鼠标单击【Finish】按钮,完成虚拟机的安装。系统提示重新启动计算机,选择【是】按钮重新启动计算机,重新启动后,虚拟机安装完成。2.2 新建虚拟机

打开Vmware虚拟机软件,选择【File】→【New】→【Virtual Machine】,弹出新建虚拟机向导对话框,注意以下几个重要的选项,其他选项都采用默认即可。

选择操作系统和版本,如下图所示。

选择虚拟机名称和存放的路径,如下图所示。

设置虚拟机硬盘大小为20 GB,如下图所示。

单击【完成】按钮,这样就新建了一个虚拟机。

我们还要设置虚拟机的内存,步骤如下:单击虚拟机VMware的【VM】→【settings】菜单,弹出虚拟机设置对话框,设置虚拟机使用的内存为512 MB或1024 MB,如下图所示,然后单击【OK】按钮。到此,该虚拟机的硬盘大小设置为20 GB,内存设置为512 MB,接着就可以在该虚拟机上安装ubuntu操作系统了。

注:键盘和鼠标控制权在虚拟机和Windows系统之间的切换是通过组合键【Ctrl】+【Alt】来实现的。2.3 安装Linux操作系统ubuntu

Ubuntu安装版本为ubuntu 8.10。

单击VMware软件工具栏上的【】来启动虚拟机,如下图所示。

单击【VM】→【Removable Devices】→【CD_ROM】→【Edit】菜单,弹出CD_ROM设备对话框,如下图所示。

在Connection中选中【Use ISO image】,设置ubuntu软件安装包的路径。然后重新启动虚拟机,开始在虚拟机上安装ubuntu操作系统。

重新启动虚拟机后,出现如下图所示的ubuntu安装界面。

选择语言版本为【English】,然后按回车键,出现如下图所示的安装界面。

选择【Install Ubuntu】,然后按回车键,开始安装ubuntu,出现ubuntu安装进度界面,如下图所示。

根据出现的安装向导,主要设置一下所在城市、用户名和密码,其余的安装对话框都采用默认值,选择【Forward】即可。

设置安装所在城市:

当出现安装所在城市对话框时,选择【ShangHai】,通过选择地图上的点来进行选择,如下图所示。

设置系统的用户名和密码:

系统安装过程中出现设置用户名和密码对话框时,设置登录到ubuntu的用户名和密码,如下图所示。

然后单击【Forward】按钮,在出现的最后安装向导对话框中单击【Install】按钮,则开始安装ubuntu。Ubuntu安装完成后,出现安装完成对话框,如下图所示。

单击【Restart now】开始重新启动ubuntu系统。在系统重启过程中出现如下图所示界面。

提示移走光驱中的磁盘后按回车键,这时单击【VM】→【Removable Devices】→【CD_ROM】→【Edit】菜单,弹出CD_ROM设备对话框,如下图所示。

在Connection中选中【Use physical drive】,单击【OK】按钮,然后按回车键即可,系统正常启动。在出现输入用户名和密码提示框时,输入刚才在安装过程中设置的用户名和密码即可。这样就进入了刚安装好的ubuntu操作系统。2.4 安装VMware tools工具软件

VMware tools工具软件用于在Windows系统和虚拟机的ubuntu系统之间进行数据传递,安装好VMware tools之后,鼠标可以在Windows系统和虚拟机之间平滑切换,在Windows系统和虚拟机之间可以直接通过【复制】和【粘贴】的方式进行数据交换。VMware tools的安装步骤如下:(1)设置VMware tools软件的路径为C:\Program Files\VMware\VMware Workstation\ linux.iso。

单击【VM】→【Removable Devices】→【CD_ ROM】→【Edit】菜单,弹出CD_ROM设备对话框,如下图所示。

单击【OK】按钮,这时在ubuntu的桌面上出现一个光驱图标,如下图所示。

双击VMware Tools图标,打开光盘文件夹,可以在该光盘目录下看到VMwareTools-6.0.2- 59824.tar.gz文件,如下图所示。

返回系统桌面,再单击系统菜单【Places】→【Home Folder】,进入用户目录cao,在用户目录下建立Soft目录,把VMwareTools-6.0.2-59824.tar.gz复制到Soft目录下。

在安装VMwareTools-之前需要先解锁root用户,单击桌面系统菜单【System】→【Administration】→【Users and Groups】,弹出用户设置对话框,如下图所示。

选中root用户,然后单击下面的【Unlock】,再单击【Close】按钮关闭对话框。

接着开始设置root用户密码,单击桌面系统菜单【Applications】→【Accessories】→【Terminal】,进入用户字符终端操作界面,如下图所示。

按照上图所示,输入命令sudo passwd root来设置root用户密码。● 提示password for cao时,输入cao用户密码;● 提示Enter new UNIX password时,输入root用户密码;● 提示Retype new UNIX password时,再次输入root用户密码。

最后显示password更新成功。

最后输入命令su切换到root用户,在提示Password时,输入刚才设置的root用户密码即可;命令提示符由以cao开头改为以root开头,表示已经成功切换到了root用户。(2)运行安装VMware Tools工具的脚本文件。

打开ubuntu字符操作终端,进入到Soft目录,解压VMwareTools-6.0.2-59824.tar.gz软件包,解压完成后,我们看到在Soft目录下生成了一个新的文件夹vmware-tools-distrib,如下图所

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载