2440A学习笔记.docx

上传人:b****4 文档编号:6153187 上传时间:2023-05-09 格式:DOCX 页数:76 大小:339.88KB
下载 相关 举报
2440A学习笔记.docx_第1页
第1页 / 共76页
2440A学习笔记.docx_第2页
第2页 / 共76页
2440A学习笔记.docx_第3页
第3页 / 共76页
2440A学习笔记.docx_第4页
第4页 / 共76页
2440A学习笔记.docx_第5页
第5页 / 共76页
2440A学习笔记.docx_第6页
第6页 / 共76页
2440A学习笔记.docx_第7页
第7页 / 共76页
2440A学习笔记.docx_第8页
第8页 / 共76页
2440A学习笔记.docx_第9页
第9页 / 共76页
2440A学习笔记.docx_第10页
第10页 / 共76页
2440A学习笔记.docx_第11页
第11页 / 共76页
2440A学习笔记.docx_第12页
第12页 / 共76页
2440A学习笔记.docx_第13页
第13页 / 共76页
2440A学习笔记.docx_第14页
第14页 / 共76页
2440A学习笔记.docx_第15页
第15页 / 共76页
2440A学习笔记.docx_第16页
第16页 / 共76页
2440A学习笔记.docx_第17页
第17页 / 共76页
2440A学习笔记.docx_第18页
第18页 / 共76页
2440A学习笔记.docx_第19页
第19页 / 共76页
2440A学习笔记.docx_第20页
第20页 / 共76页
亲,该文档总共76页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

2440A学习笔记.docx

《2440A学习笔记.docx》由会员分享,可在线阅读,更多相关《2440A学习笔记.docx(76页珍藏版)》请在冰点文库上搜索。

2440A学习笔记.docx

2440A学习笔记

目录

目录1

一.启动代码2

1.禁止看门狗2

2.禁止中断2

3.设定时钟2

4.使能Cache2

5.初始化sdram2

6.初始化堆栈4

7.注册中断4

8.初始化NAND6

9.启动加载7

二.相应模块10

1.看门狗10

2.中断10

3.通用IO口13

4.时钟和电源管理14

5.存储器管理15

6.内存管理单元和cache17

7.串口21

8.IIC25

9.NANDFLASH27

附录31

中断源和子中断源的关系31

Irq和fiq的本质区别31

关于CPSR_cxsf31

HANDLER32

AboutBANK32

U-BOOT启动原理33

虚拟地址和物理地址的映射35

Steppingstone36

NANDFLASH读写方法37

1.根据2410寄存器定义如下的命令宏41

2.NAND设备的初始化42

3.NAND设备的识别42

4.NAND的擦操作43

5.NAND的读操作43

6.NAND的写操作44

TODO:

46

一.启动代码

1.禁止看门狗

WTCON:

watchdogtimercontrolsets0;

2.禁止中断

allinterruptdisableandallsubinterruptdisable

3.设定时钟

锁定时间,在重新设置好PLLCON之后,在新的PLL时钟使用之前,PLL会锁住时钟一会,这个时间就是LOCKTIME。

4.使能Cache

mrcp15,0,r0,c1,c0,0

orrr0,r0,#(1<<30):

OR:

(1<<31)

mcrp15,0,r0,c1,c0,0

5.初始化sdram

设置存储器控制寄存器,存储器的配置数据都存储在SMRDATA为起始地址的数据表中,

下面的代码可以一次将预先配置好的初始化数据存入与存储器控制器相关的13个寄存器,

这些寄存器则是以0x33F005E0为起始地址的13个连续的32位寄存器

;Setmemorycontrolregisters;SMRDATA:

0x33F005E0

adrr0,SMRDATA;can'tuseldrr0,=xxxximportant!

!

!

ldrr1,=BWSCON;BWSCONAddress,BWSCON可设为0x22111110

addr2,r0,#52;EndaddressofSMRDATA

0

ldrr3,[r0],#4

strr3,[r1],#4

cmpr2,r0

bne%B0

存储器具体设置请参考存储器管理。

在系统使用SDRAM之前,需要对S3C2440的存储器控制器进行初始化。

其中对与SDRAM(Bank6)相关的寄存器进行了特殊的设置,以使SDRAM能够正常工作。

由于C语言程序使用的数据空间和堆栈空间都定位在SDRAM上,因此,如果没有对SDRAM(Bank6)的正确初始化,系统就无法正确启动。

下面介绍与SDRAM相关的寄存器设置。

1、 总线宽度&等待控制寄存器(BWSCON)0x48000000

在这个寄存器中,可以配置BANK1~BANK7的DW、WS、ST,DW决定了数据总线宽度;WS是等待状态是否使能,如果此位为1,WAIT信号可以用于延长nOE信号的有效时间;ST决定是否使用UB/LB(与SRAM相关)。

此寄存器还可以读取(不能写)BANK0的数据总线宽度。

对于SDRAM,只需要设置BANK6和BANK7的DW相关位,此处设置为32位。

BWSCON(0x48000000)=0x22xxxxxx,后面的24位设置和FLASH有关,一个例子是设置为0x22111120。

2、 BANK控制寄存器(BANKCONN)

BANK0~BANK5的控制寄存器相同,复位值均为0x0700。

包括对Tacs(nGCSn前的地址建立时间)、Tcos(nOE信号有效前的片选建立时间)、Tacc(访问周期,当WAIT使能时,Tacc必须大于等于4个时钟周期)、Tcoh(nOE后的片选保持时间)、Tcah(nGCSn后的地址保持时间)、Tacp(Page模式下的访问周期)、PMC(Page模式配置)。

BANK6、BANK7的控制寄存器相同。

如果存储器类型为ROM或者SRAM,情况与BANK0~BANK5类似,如果存储器类型为SDRAM,则需要设置Trcd(RAS到CAS的延时)和SCAN(列地址位数),根据我们使用的SDRAM以及S3C2440的HCLK(SDRAM时钟由系统的HCLK提供)时钟情况,Trcd取3个时钟,列地址位数是9,则BANKCON6(0x4800001C)=0x00018005,BANKCON7(0x48000020)=0x00018005。

3、 SDRAM刷新控制寄存器(REFRESH)

包括REFEN位(刷新使能),此位一般设置为1;TREFMD位(SDRAM刷新模式),初始值为0;Trp(SDRAMRAS预充电时间),此位需要根据时钟HCLK与选用的SDRAM芯片决定,一般设置为2或3个时钟;Tsrc位(SDRAM半行周期时间),由Trc-Trp得到,Trc和Trp的值由时钟和SDRAM芯片决定;REFRESHCOUNTER位(SDRAM刷新计数值),刷新计数值=2^11+1-HCLK*SDRAM刷新时间(刷新时间参考S3C2440和SDRAM技术手册)。

4、 BANKSIZE寄存器(BANKSIZE)

BURST_EN位是ARM核突发模式使能位,设为0或1均可,为了提高效率,此处可以设为1;SCKE_EN,SDRAM掉电模式使能控制,设为1;SCLK_EN,设为1,SCLK仅在访问周期才被激活;BK76MAP,BANK6和BANK7的内存映射,我们使用2片4B*4M*16bit的SDRAM组成32位256MB存储器,而且BANK6和BANK7大小要相等,所以此处设为2,即128MB/128MB。

BANKSIZE(0x48000028)=0x00000032。

5、 SDRAM模式寄存器集合寄存器(MRSR)

WBL位,突发写长度,一般设为0,突发写长度固定;TM位,测试模式,一般设为00,模式寄存器集合(固定);CL位,CAS延时,根据SDRAM芯片和HCLK时钟计算;BT位,突发类型,一般设为0,连续的(固定);BL位,突发长度,一般设为000(固定的)。

MRSR(0x4800002C)=0x00000030,(0x48000030)=0x00000030。

6.初始化堆栈

#defineSDRAM_END0x34000000

#define_STACK_BASEADDRESS(SDRAM_END-0x8000);0x33ff8000

UserStackEQU(_STACK_BASEADDRESS-0x3800);0x33ff4800~

SVCStackEQU(_STACK_BASEADDRESS-0x2800);0x33ff5800~

UndefStackEQU(_STACK_BASEADDRESS-0x2400);0x33ff5c00~

AbortStackEQU(_STACK_BASEADDRESS-0x2000);0x33ff6000~

IRQStackEQU(_STACK_BASEADDRESS-0x1000);0x33ff7000~

FIQStackEQU(_STACK_BASEADDRESS-0x0);0x33ff8000~

;everymodehasitsownsp;

mrsr0,cpsr;readstatusregister;

bicr0,r0,#MODEMASK;MODEMASK:

system;

orrr1,r0,#UNDEFMODE|NOINT

msrcpsr_cxsf,r1;UndefMode

ldrsp,=UndefStack

orrr1,r0,#ABORTMODE|NOINT

msrcpsr_cxsf,r1;AbortMode

ldrsp,=AbortStack

7.注册中断

系统可能存在多个IRQ/FIQ的中断处理程序。

为了从向量表入口处的跳转最终能找到正确的中断处理程序,需要设计一套处理机制和方法来实现。

可以在ARM的异常向量表之外,增加一张关联中断控制器的向量表,向量表中的内容对应每个具体的中断源,可以协助跳转到不同的中断处理程序。

当响应外设的一个中断请求时,首先触发ARM核的中断,进人中断程序,再通过中断控制器识别中断源,使PC能够自动获得中断处理程序的地址。

有的芯片支持特殊的硬件分支功能,依据中断源自动跳转到向量表的相应地址,多数情况下是用软件来处理异常分支。

当异常或者中断发生的时候,处理器设置PC为一个特殊的内存地址。

这个地址叫做中断向量表。

中断向量表入口是中断、异常的分支入口。

内存映射地址0x00000000是为中断向量表保留的。

在某些处理器中断向量表地址为0xffff0000。

某些操作系统如wince/linux可以利用这个特征。

当异常或者中断发生的时候,处理器挂起正常执行的程序并开始加载中断向量表,每个中断入口包含一个指向specificroutine(这个不知道怎么翻译)的分支指令。

从上面可以知道中断向量表存放在0x00000000以及0xffff0000(这两个都是物理地址),所以MMU开了以后找到这两个对应的虚拟地址就可以了。

中断发生的时候,发生IRQ异常中断当前PC值-4保存到LR_IRQ里面,然后执行

bHandlerIRQ

然后是执行

HandlerIRQ;代码中使用宏HANDLER实现的。

subsp,sp,#4;预留一个用来存放PC地址

stmfdsp!

{r0};保存R0,因为下面使用了

ldrr0,=HandleIRQ;将HandleIRQ(服务程序)的地址装载到R0

ldrr0,[r0]

strr0,[sp,#4];保存到刚才预留的地方

ldmfdsp!

{r0,pc};弹出堆栈,恢复R0,并且将刚才计算好的HandleIRQ地址弹出到PC

在以ARM7TDMI-S为内核的ARM芯片,其堆栈增长方向是由高地址向低地址增长。

因此我们给每个任务分配的堆栈也必须按照这样的方式进行“push”和“pop”操作。

即可这样理解:

1、当我们在任务堆栈上执行类似的“push”操作时,首先将所需保存的数据压入到堆栈中,然后再对“任务的SP寄存器的值“-4。

(这里-4的偏移量是因为我的整个移植代码都是在ARM指令集下进行的所以偏移量的设置是4);

2、当我们在任务堆栈上执行类似的“pop“操作时,首先将堆栈中的数据弹出堆栈,然后再对”任务的SP寄存器的值“+4。

(这里+4的偏移量是因为我的整个移植代码都是放在ARM指令集下进行的,所以偏移量的设置是4);

堆栈是向下生长的,所以SUBSP,SP,#4就相当于PUSHXX,但是这个XX这个时候并没有用,因为这里用的是强制移动SP指针实现的。

然后得到服务程序的地址,再将这个值放回刚才预留的栈的空位上面,最后就是POP出R0恢复,并且将刚才得到的服务程序的地址送到PC,那么实现的效果就是跳转到HandleIRQ里面了。

接着看刚才是怎么安装的HandleIRQ的

;SetupIRQhandler

ldrr0,=HandleIRQ;Thisroutineisneeded

ldrr1,=IsrIRQ;ifthereisnot'subspc,lr,#4'at0x18,0x1c

strr1,[r0]

可以看出,这里将IsrIRQ的地址的值保存到HandleIRQ中,也就是说,上面的IRQ服务程序,这个时候实际上就是指IsrIRQ!

所以接着的事情就是转移到IsrIRQ中执行:

IsrIRQ

subsp,sp,#4;预留一个值来保存PC

stmfdsp!

{r8-r9}

ldrr9,=INTOFFSET;计算偏移量,下面解释

ldrr9,[r9]

ldrr8,=HandleEINT0

addr8,r8,r9,lsl#2

ldrr8,[r8]

strr8,[sp,#8];因为保存了2个寄存器R8R9,所以SP下移了8位

ldmfdsp!

{r8-r9,pc};恢复寄存器,弹出到PC,同上面的一样

怎么保存,操作SP,跟最后弹出到PC的部分和上面的例子一样,下面说说中间的计算部分计算偏移量,其实原理很简单,首先INTOFFSET保存着当前是哪个IRQ中断,例如0代表着HandleEINT0,1代表HandleEINT1.....等等,这不是乱来,有一个表的,这个是由S3C2440的datasheet说的,自己可以去查看。

然后得到中断处理函数的向量表,这个表的首地址就是HandleEINT0,那么很自然的想到,怎么查表?

那还不简单?

HandleEINT0+INTOFFSET不就完了?

基地址加偏移量就得到表中某项了,当然,因为这里是中断处理向量,每一项占用4个字节,所以用lsl#2处理一下,左移2位相当于乘以4,偏移量乘以4,这应该很好理解的。

我们这个例子找到的就是HandleEINT0,将里面的值读出来,里面放的是HandleEINT0服务函数的地址,这个地址怎么来的?

是在C程序里面设置的。

我们看keyscan.c程序,找到一个voidKeyScan_Test(void)函数,里面有这么一句:

pISR_EINT0=pISR_EINT2=pISR_EINT8_23=(U32)Key_ISR;

这里是安装了3个按键中断服务程序,我们只关注0号中断,也就是

pISR_EINT0=(U32)Key_ISR;

这句话什么意思?

先看看pISR_EINT0的定义,在2440addr.h中定义

#definepISR_EINT0(*(unsigned*)(_ISR_STARTADDRESS+0x20))

看到没有?

_ISR_STARTADDRESS不就是刚才说的那个异常向量的入口地址?

加上一个0x20之后实际上指向的,就是HandleEINT0!

这么说来,上面的意思就是,将Key_ISR处理函数的入口地址,送到HandleEINT0中。

再来看Key_ISR,这是一个典型的服务程序,加了_irq作为编译关键字,告诉编译器,这个函数是中断服务程序

得保存需要的寄存器,免得被破坏。

具体可以参考《ARM体系结构与编程》P283页的描述。

staticvoid__irqKey_ISR(void)

{

.......

}

加上_irq关键字之后,编译器就会处理好所有的保存动作了,并不需要多关心。

但是这个是ARM-CC编译器的关键字,GCC中并没有这个东西,所以GCC处理中断的时候最好还是自己保存一下。

8.初始化NAND

;SetupNANDFlash;

movr5,#NFCONF;0x4e000000

ldrr0,=(7<<12)|(7<<8)|(7<<4)|(0<<0)

strr0,[r5]

1.设定TACLS、TWRPH0、TWRPH1

2.OM[1:

0]==0,NANDFlashboot;

3NANDflashmemoryAddresscycleforauto-booting:

4addresscycle

4.2048Bytes/page

5.16bitbus

6.NANDflashcontrollerenable

当系统处在复位状态时,Nandflash控制器从管脚NCON,GPG13,GPG14,GPG15得到nandflash的一些信息(如pagesize,buswidth等).在上电或系统复位之后,则nandflash控制器将自动加载4KBbootloader代码,之后就是在steppingstone里执行.注意:

在自动启动这个过程中,ECC模块是不发挥作用的,所以前4KBnandflash应当不含biterror.

也就是说,选用了那个nGCS为片选就意味着以这个片选对应的起始地址作为基地址供系统访问,nandflash不是内存器件,你的这个原理图中没有把nandflash的单元直接映射到系统内存中,而是作为io器件由2440的nandlfash控制器来控制,所以这里的片选信号不是由某个nGCS来充当,而是由2440的nandflash控制器的片选引脚充当。

nFCE:

nandflashchipenable.

 NANDFLASH不能够执行程序,本人总结其原因如下:

1.NANDFLASH本身是连接到了控制器上而不是系统总线上。

CPU启动后是要取指令执行的,如果是SROM、NORFLASH等之类的,CPU发个地址就可以取得指令并执行,NANDFLASH不行,因为NANDFLASH是管脚复用,它有自己的一套时序,这样CPU无法取得可以执行的代码,也就不能初始化系统了。

 2.NANDFLASH是顺序存取设备,不能够被随机访问,程序就不能够分支或跳转,这样你如何去设计程序。

u-boot源码不支持从nandflash启动,可是s3c2440支持从nandflash启动,开发板加电后s3c2440将nandflash的前4k(保存有u-boot的部分功能--拷贝功能--把nandflash中的内容拷贝到SDRAM)拷贝到sram(s3c2440芯片内的sram)。

这就需要修改u-boot源码,增加u-boot的功能:

使u-boot在得到执行权后能够将其自身拷贝到开发板上SDRAM中,以便处理器能够执行u-boot。

请参考NAND的工作原理。

9.启动加载

一:

地址空间的分配

1:

s3c2440是32位的,所以可以寻址4GB空间,内存(SDRAM)和端口(特殊寄存器),还有ROM都映射到同一个4G空间里.

2:

开发板上一般都用SDRAM做内存flash(nor、nand)来当做ROM。

其中nandflash没有地址线,一次至少要读一页(512B).其他两个有地址线

3:

nandflash不用来运行代码,只用来存储代码,NORflash,SDRAM可以直接运行代码)

4:

s3c2440总共有8个内存banks

6个内存bank可以当作ROM或者SRAM来使用,留下的2个bank除了当作ROM或者SRAM,还可以用SDRAM(各种内存的读写方式不一样)

7个bank的起始地址是固定的

还有一个灵活的bank的内存地址,并且bank大小也可以改变

5:

s3c2440支持两种启动模式:

NAND和非NAND(这里是norflash)。

具体采用的方式取决于OM0、OM1两个引脚

OM[1:

0]所决定的启动方式

OM[1:

0]=00时,处理器从NANDFlash启动

OM[1:

0]=01时,处理器从16位宽度的ROM启动

OM[1:

0]=10时,处理器从32位宽度的ROM启动。

OM[1:

0]=11时,处理器从TestMode启动。

当从NAND启动时,cpu会自动从NANDflash中读取前4KB的数据放置在片内SRAM里(s3c2440是soc),同时把这段片内SRAM映射到nGCS0片选的空间(即0x00000000)。

cpu是从0x00000000开始执行,也就是NANDflash里的前4KB内容。

因为NANDFLASH连地址线都没有,不能直接把NAND映射到0x00000000,只好使用片内SRAM做一个载体。

通过这个载体把nandflash中大代码复制到RAM(一般是SDRAM)中去执行,当从非NANDflash启动时norflash被映射到0x00000000地址(就是nGCS0,这里就不需要片内SRAM来辅助了,所以片内SRAM的起始地址还是0x40000000).然后cpu从0x00000000开始执行(也就是在Norfalsh中执行)。

Arm的启动都是从0地址开始,所不同的是地址的映射不一样。

在arm开电的时候,要想让arm知道以某种方式(地址映射方式)运行,不可能通过你写的某段程序控制,因为这时候你的程序还没启动,这时候arm会通过引脚的电平来判断。

1当引脚OM0跟OM1有一个是高电平时,这时地址0会映射到外部nGCS0片选的空间,也就是Norflash,程序就会从Norflash中启动,arm直接取Norflash中的指令运行。

2当OM0跟OM1都为低电平,则0地址内部bootbuf(一段4k的SRAM)开始。

系统上电,arm会自动把NANDflash中的前4K内容考到bootbuf(也就是0地址),然后从0地址运行。

这时NANDFlash中的前4K就是启动代码(他的功能就是初始化硬件然后在把NANDFlash中的代码复制到RAM中,再把相应的指针指向该运行的地方)。

为什么会有这两种启动方式,关键还是两种flash的不同特点造成,NOR FLASH容量小,速度快,稳定性好,输入地址,然后给出读写信号即可从数据口得到数据,适合做程序存储器。

NAND FLASH 总容量大,但是读写都需要复杂的时序,更适合做数据存储器。

这种不同就造成了NORflash可以直接连接到arm的总线并且可以运行程序,而NANDflash必须搬移到内存(SDRAM)中运行。

在实际的开发中,一般可以把bootloader烧入到Norflash,程序运行可以通过串口交互,进行一定的操作,比如下载,调试。

这样就很可以很方便的调试你的一些代码。

Norflash中的Bootloader还可以烧录内核到Norflash等等功能。

LDRr0,=_entry和ADRr0,_entry的区别;

前者是在编译的时候按照loadaddress生成的绝对地址,后者反汇编后是相对当前PC寻址,例如在ADS中设置RO地址为0X30000000,那么前者传给r0的值是0x30000000,而后者传给r0的值要视当前PC而定,一般从NGCS0中启动时,传给r0的值就是0。

Image$$RO$$Base等比较古怪的变量是编译器生成的。

RO,RW,ZI这三个段都保存在Flash中,但RW,ZI在Flash中的地址肯定不是程序运行时变量所存储的位置,因此我们的程序在初始化时应该把Flash中的RW,ZI拷贝到RAM的对应位置。

一般情况下,我们可以利用编译器替我们实现这个操作。

比如我们跳转到main()时,使用b  __Main,编译器就会在__Main和Main之间插入一段汇编代码,来替我们完成RW,ZI段的初始化。

如果我们使用b  Main,那么初始化工作要我们自己做。

编译器会生成如下变量告诉我们RO,RW,ZI三个段应该位于什么位置,但是它并没有告诉我们RW,ZI在Flash中存储在什么位置

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2