分类:
2007-03-08 16:30:33
DMA配置
ZDMA在系统总线上,完成系统总线上器件的数据传送,如存储器。BDMA也有2个,桥接系统总线和外设总线,即可以完成分别位于两条总线上的器件的数据传送,也可完成外设总线上器件如SIO、UART、TIMER等之间数据传送。
DMA的四种数据传送方式。
DMA的触发选择:XDREQ/XDACK、S/W、H/W等。
一般地,DMA的初始化只需完成BDMA的目标地址寄存器的初始化:BDIDES0,1=0x40000000,即传送方向为内部存储器到外设,初始目的地址:0x0。
中断寄存器的配置和中断向量表的设计
中断有两种IRQ和FIQ,后者优先级高于前者。另外,ARM系统还做了些特殊安排以使FIQ有更快的响应速度,如FIQ的ISR可以直接放在0x1c(紧跟FIQ)开始的地址单元中,免去了跳转;属于FIQ的中断向量表可常驻cashe;FIQ较IRQ有更多的物理寄存器等。通常在简单的用户程序中,可以不使用FIQ,所有中断都设为IRQ(默认情况)。
ARM7有30个中断源,实际使用25个。其优先级如下所示:
一些重要的中断设置寄存器如下:
INTCON 0x01E00000 :中断控制。指定IRQ是否采用向量模式(一般采用非向量模式,这也是默认值)。指定CPU是否响应IRQ和FIQ。
INTPND:只读。指定中断源是否有中断请求,可以同时有多个中断请求。当对应的ISR结束时,通过向I_ISPC和F-ISPC写1来清除INTPND中对应的比特位,否则该中断将连续执行。
INTMSK:各中断源是否屏蔽。初始值时屏蔽。
INTMOD:指定各中断源是IRQ或FIQ,默认值全为IRQ。
I_PSLV: 0x01E00010 R/W IRQ priority of slave register 0x1b1b1b1b
I_PMST: 0x01E00014 R/W IRQ priority of master register 0x00001f1b
I_CSLV: 0x01E00018 R Current IRQ priority of slave register 0x1b1b1b1b
I_CMST: 0x01E0001C R Current IRQ priority of master register 0x0000xx1b
I_ISPR: 0x01E00020 R IRQ interrupt service pending register
I_ISPC: 0x01E00024 W IRQ interrupt service clear register
F_ISPC: 0x01E00024 W FIQ interrupt service clear register
优先级取默认值就可以了。
ISPR只读,指示当前被响应的中断源,没有或只有一个被响应,尽管此时INTPND中可能有几个中断请求。ISR结束时,通过向ISPC对应位写1来清除ISPR中的对应位。
在ARM7TDMI中,中断向量表的设置有两种模式:向量模式和非向量模式。前者只适于全IRQ的设置。采用非向量模式时,通过分析ISPR(发生中断时,其中只有一个位为1,其它全为0)找到要执行的ISR的入口地址。在向量模式中,当发生IRQ时,CPU自动产生跳转地址,如同异常中断的使用。各IRQ的一级ISR的跳转地址如下:
中断有异常中断(如:Dabort、Pabort、Undef等)和IRQ或FIQ两种。下面以向量模式下的IRQ为例介绍一下中断设置。
过程如下:IRQ中断向量表设置——>写一级ISR——>分配二级ISR的入口地址表——>写二级ISR——>把二级ISR的入口地址放到二级ISR的入口地址表中。这样在开中断的情况下,一个ISR就可以正常执行了。一个ISR的执行过程如下。
首先要在系统初始化时开中断:INTMSK各中断位清零且INTCON的IRQ位清零(使能)且CPSR的I比特清零(使能),缺一不可。中断发生时,首先由模式SYS或User切换至IRQ,同时完成现场保护(工作指针入栈、保存CPSR、PC->LR),然后PC直接跳到IRQ中断向量表的相应地址(一级ISR的入口),紧接着跳到一级ISR并执行;一级ISR通常由汇编写成,仅完成一个跳转任务(有时也看一下寄存器ISPR,判断该中断是否被错误触发,如果错误将直接返回),即从二级ISR的入口地址表中找到相应中断的入口地址,其间工作现场没有变化。二级ISR通常由c语言写成,中断的真正的响应程序就在此处。ISR结束时,要对INTMSK中的pending比特清零(通过置位ISPR中相应比特),否则将连续响应该中断。然后CPU自动切换至中断前的工作模式,并恢复现场。
在C语言中关键字”__irq”的作用:当ISR定义时有此关键字,则ISR结束后CPU自动从栈中恢复中断前模式的LR,并把它赋值给PC,完成ISR的正常返回。如果无此关键字,则CPU只能返回到二级ISR前的中断状态,此时仍为IRQ工作模式。当然也能够继续执行用户程序,只是工作模式不对,此模式下再不能响应其它IRQ中断。
事实上,CPU响应中断并执行ISR相当于一个程序调用过程。用户程序不必干预CPU的模式切换、现场保护、程序返回。
中断向量表的设置。一级中断向量表紧跟异常中断向量表,位于0x20~0xc0。只读。由于S3C44b0x没有MMU和地址映射功能,该中断向量表必须和异常中断向量表一起固化到系统地址空间的0x0处,即Flash的起始处。在线调试阶段也必须保证该表存在于Flash中。二级ISR的地址表一般位于RAM空间的最后256个字节处,紧跟在堆栈后,在汇编语言中由MAP语句创建(8个异常中断和25个IRQ,共33×4=132Byte),可读写。同时在c中定义一组指向相同地址空间的无符号型指针,当然指针名称必须和汇编中的定义相同。这样在C中的ISR初始化程序中,可直接把二级ISR的入口放到地址表中。如:pISR_EINT0=(unsigned)Isr_Eint0; pISR_EINT0为地址表中的指针,而Isr_Eint0为ISR的名称,也是其入口地址。二级ISR地址表和一级表不同的是,其各中断的顺序可任定,但必须保证汇编和C中的定义一致。
对于非向量模式,不使用IRQ中断向量表,但二级ISR地址表的设置是相同的。在本测试程序中boot.s同时包含了两种格式的设置,只要设置好INTCON中的mode比特,两种模式都可以用。注意非向量模式,在汇编中要设置IRQ和FIQ的入口地址。因为在非向量模式要靠IsrIRQ和IsrFIQ来定位响应的中断源位置。
另外,为了保证开中断后,程序不至于跑飞,最好编写所有的IRQ 的ISR,该ISR可以是个空函数,确保能正常返回就行了。
堆栈初始化和工作模式的切换
ARM7TDMI有7种工作模式,要用到6个stack,其中SYS和User共用一个Stack。堆栈设置采用流行的FD模式(full decresment)。通常放在RAM空间的次最高段(最高的256B为ISR的地址表),在16M的SDRAM中,各stack设置如下:
0x0cff_f000~0x0cff_fa00: Uers and SYS stack,2560B,够大了。
0x0cff_fa01~0x0cff_fb00: SVC stack, 256B;
0x0cff_fb01~0x0cff_fc00: Undef stack, 256B;
0x0cff_fc01~0x0cff_fd00: Abort stack, 256B;
0x0cff_fd01~0x0cff_fe00: IRQ stack, 256B;
0x0cff_fe01~0x0cff_ff00: FIQ stack, 256B;
0x0cff_ff01~0x0cff_ffff: ISR地址表, 256B;
CPU的模式切换通常由异常中断产生,或者在SVC或SYS模式下完成。User模式中用户程序不能改变工作模式(除了应用异常中断,如SWI),当然也不能改变CPSR的值(也就不能开关中断了!)。通常如果不用嵌入式OS,单任务的用户程序工作在SYS或SVC模式下更好一些,这样可以更方便的使用硬件资源。如果使用SVC模式,甚至可以不设置SYS and User stack。
系统加电重起时,首先进入SVC模式,完成初始化,在调用C的main函数之前再切换到SYS或User模式。因此可以把堆栈初始化放到最后执行,并最后设置SYS stack,这样进入main之后可以直接工作在SYS模式下。本测试程序就是如此设置的。
分布式加载
ADS1.2中的ARM linker支持分布式加载,即加载域(load)和执行域(image)的各个输出段(RO、RW、ZI)可以有不同的地址。可以很方便的生成供在线调试和下载的elf格式的文件。通常总线调试只需设置RO base=0x0c000000;而生成下载代码则要设置RO base=0x0,RW base=0x0c000000,并且一定要把boot.o设成first section,否则程序入口不在0x0则无法完成异常中断和普通中断,包括reset。至于ropi、rwpi、split的应用参见linker的有关资料。
链接器同时产生一组符号,给出各个域或者各个输出段的区间的长度,装载地址和执行地址。由于链接器和C库都没有将代码从它的装载区间拷贝到执行区间,或创建一个零初始化区域的功能,所以要由应用程序员利用这组符号产生的信息完成这项工作,这是在呼叫C程序之前必须完成的,举例如下:
LDR r0, = |Load$$DRAM$$Base|
LDR r1, = |Image$$DRAM$$Base|
CMP r0, r1 检查装载地址和执行地址是否相同
BEQ do_zi_init 相同,则不拷贝该区间,初始化零数据区
MOV r2, r1 ; 不相同,将装载区拷贝到执行区
LDR r4, = |Image$$DRAM$$length|
ADD r2, r2, r4
BL copy
do_zi_init
LDR r1, = |Image$$DRAM$$ZI$$Base|
MOV r2, r1
LDR r4, = |Image$$DRAM$$ZI$$length|
ADD r2, r2, r4
MOV r3, #0
BL zi_init 调用零初始化子程序
上例中使用了ARM Linker产生的与域有关的几个符号,域名可以在scatter文件中指定。在本测试程序中,因为只有一个load域和一个image域,因此可以使用ARM Linker产生的与输出段有关的符号,如|Image$$RO$$Base|、|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|、|Image$$ZI$$Limit|等。必须注CPU或系统本身都不能自动完成分布式加载的任务,所以系统在第一阶段初始化时(即汇编语言写的初始化代码),就必须编写用户程序根据上述几个符号把代码考到RAM区中。通常要把除了汇编写的第一阶段的初始化代码以外的RO代码、RW和ZI代码都要拷到RAM中,这样可以代码的执行速度。RW和ZI是必须要拷到RAM中,否则程序无法运行。注意ZI是RW区中的位于最后的一段零初始化的RAM区,RW limit和ZI limit两个值相等。在本程序中只拷贝了RW和ZI段。
LCD设置
本系统所带LCD没有内置控制器、显存、字符查找表等,需要靠ARM所带LCD控制器进行显示,在系统的SRAM中开辟一块显存区,本测试程序中显存framebuffer开在ZI区之后,且能保证不进入RAM高端的堆栈区,实际上黑白字符的显存为14400Byte(前者分辨率为480×240),彩色图片的显存115200B(分辨率为320×240)。支持MONO、灰4,灰16、256色四种模式。面板模式有4bit single、8bit single、4bit dual scan三种。黑白字符显示采用4bit dual scan,彩色图片采用8bit single。
往显存中写入数据,系统会自动把数据代表的内容显示在LCD的相应位置上,ASCII采用8×16点阵。图片中每象素需8比特信息(红3绿3兰2),BMP图片的显示原理还没搞清楚。
虚拟屏幕显示。显存的大小定义为SCR_XSIZE*SCR_YSIZE,即可以放进这么大的BMP图片,而能显示的范围是LCD_XSIZE*LCD_YSIZE,只能显示图片的某个区域,设置好PAGEWITDH和OFFSIZE参数,改变光标的位置,就可移动显示图片的其它部分(Lcd_moveviewport函数),本测试程序中令SCR_SIZE=LCD_SIZE,即没有使用虚拟屏幕显示。
去抖动算法和AFC。灰4和灰16图片的显示根据的是AFC原理。去抖动算法是保证相邻帧之间任一象素on/off的概率相当。这些是ARM中的LCD控制器自动完成。
彩色查找表。设定不同深度的颜色。红和绿:16取8,兰:16取4。
PS/2键盘接口设计
键盘接口通常有扫描和中断两种方式。扫描方式的键盘制作简单,但是要占用过多的微处理器的端口资源,并且由于采用查询方式接收键盘数据,导致CPU资源的浪费。中断方式的键盘较为复杂,但接口简单,如采用5芯或6芯的PS/2接口,而且由于采用中断方式接收键盘扫描码,相应地也减少了CPU资源的占用。目前标准PC机键盘技术已经很成熟,且成本也已经很低。本监控板将采用标准PC机键盘作为人机命令的输入接口。
PS/2接口信号线主要有两根:CLK、DATA。键盘和主机间的通信是同步和异步方式的结合。按键时在DATA线上发送11位的扫描码,起始位0、8个数据位(LSB在前)、1个校验位和一个结束位0。放键时先发送放键标志F0,接着再发送一遍扫描码。CLK的上升沿触发主机的中断,主机执行ISR(ExINT4567)接收1bit数据。接收完完整的8bits数据后,ISR将判断是否为F0,若是,则把紧接着接收的下一个8bits数据译码,变为ASCII或OEM码或者Windows虚拟码,放入64Byte的键盘缓冲区中,并通知主机上位程序进行相应的处理,如打印字符或处理特殊符号(ESC、Enter、F1之类)。
后来发现上述编程思路又问题:只实现了位同步,不能实现11bits的字节或帧同步,当发生干扰而产生INT0中断时,一个错误比特被读入,导致后续比特全错。改进如下:每接收11bit对起始位、结束位、校验位进行判断,若发生错误,则失步,下一比特被舍弃,重新同步。结果有所改进,但仍不理想。
下表是部分按键的扫描码、系统码、ASCII、Windows虚拟码的对应关系。
SWI的使用
Swi.h 头文件的定义;使用__swi伪操作对swi函数进行说明;在main中可直接调用该函数。SWI的两级ISR设置基本同普通的IRQ,但要设置功能号。
现在的问题是无法返回sys mode, 调用功能号不对。
UART设置
S3C44B0X有两个独立的UART,均采用RS232接口。每个UART有1个16B的Rx buffer 和一个Rx buffer(实际上可以不用,若通信双方的时钟精度足够号的话)。本测试程序中使用non FIFO mode.
AFC 和 non AFC mode:所谓AFC就是利用握手信号RTS和CTS来自动控制双方的通信,对端也必须是UART,若对端是modem,则只能用non AFC mode,即用软件读写RTS和CTS。
中断模式和DMA模式。本测试程序使用中断模式,但ISR未作任何处理,实际上是查询方式收发数据。发数据:通过UTRSTAT0中的状态指示位看发送数据寄存器UTXH0是否空,不空则等待,空则把数据写到该数据寄存器。然后Uart控制器会自动把数据串至RS232的Tx脚上。接收类同。这种方式适合发数据。接收数据时,CPU就不能干其它事了,好在人机交互时,CPU也只是等待命令,不干其它。若用于从主机快速接收大量数据,则必须取数据的函数放到UART的ISR中。