单片机
返回首页

S3C2440 开发板实战(2):start.S初认识 + SDRAM配置 + 重定位

2022-07-18 来源:csdn

1、看门狗部分

废话不多说,直接开始配置,首先就是要关闭暂时不使用的看门狗,找到看门狗的寄存器:WTCON,将其第0位置0,即禁用看门狗,即:


# define pWTCON    0x53000000    //WTCON地址

 

ldr    r0,    =pWTCON

mov    r1,    #0x0

str    r1,    [r0]    //关闭看门狗


2、时钟部分

在S3C2440中有三种时钟频率,分别为FCLK, HCLK, PCLK,分别控制不同种类的外设,在时钟树中可以进行查找,这里不做过多赘述,在以后的博客中有应用。


从芯片手册中查找FCLK, HCLK, PCLK 的最高频率,在范围内我们选择设置FCLK = 400MHZ, HCLK = 100MHZ, PCLK = 50MHZ (TF : TH : TP = 1 : 4 : 8)为例进行设置初始化时钟,看芯片手册的时钟树如下图所示:

OSC可以理解为我们的晶振频率,从开发板的原理图上可以看出为12MHZ。


时钟信号通过OM[3 : 2]( 外接引脚 GND )进行时钟源选择,即设置OM[3, 2] = [0, 0],此时刚上电时FCLK频率等于晶振频率,然后进入lock time,此时,CPU暂停工作,在lock time (该段时间是为稳定输出新的FCLK)过后CPU继续工作,CPU时钟的主频率为设置的FCLK。


为保证CPU能够在启动后一定能发送设置的FCLK信号,所以将FCLK设置为最大值:


# define LOCKTIME    0x4C000000 

 

ldr    r0,    =LOCKTIME

ldr    r1,    =0xffffffff

str    r1,    [r0]

先不看控制USB部分(UPLL)直接看MPLL部分。时钟树中时间信号通过锁相环(PLL)生成400MHZ的FCLK。其中关于MPLL寄存器设置方法如下:

然后通过查找数值表(也可以手算,在手册中有公式),由Input Frequency = 12MHZ,Output Frequency  = 400MHZ得到MDIV, PDIV, SDIV的参数值,并写入寄存器中:


# define MPLLCON    0x4C000004    //MPLLCON地址

 

ldr    r0,    =MPLLCON

ldr    r1,    = ((92 << 12) | (1 << 4) | (1 << 0))    // MDIV = 92  PDIV =1  SDIV = 1

str    r1,    [r0]                //写入寄存器

时钟树通过参数HDIVN, PDIVN对FCLK进行分频操作得到可操作进行外设操作的时钟HCLK, PCLK,设置分频系数的寄存器为CLKDIVN: 


# define CLKDIVN    0x4C000014 

ldr    r0,    =CLKDIVN    

ldr    r1,    =((10 << 1) | (1 << 0))    // HCLK = FCLK / 4

                                         // PCLK = HCLK / 2

最后一步由于设置的HDIVN寄存器不等于0,所以需要设置CPU处于异步状态,这里涉及到协处理器的命令(我也不懂哈哈哈),就按照手册中的来编(chao)写就行了。


3、代码重定位

这里先要讲一讲,由于nor flash在运行程序时,是可读不可写的,所以如果程序中有变量储存在 .bin文件中时,该变量不可被改变,即使在程序中对其进行修改。所以我们需要对代码进行重定位到SDRAM中,然后才能对其全局变量进行修改。所以首先应该讲下关于SDRAM的初始化配置:


3.1. SDRAM

由于在start,S程序中只要对SDRAM进行初始化配置,这样就能够对代码进行重定位,所以本章节博客只对SDRANM的初始化程序进行编写以及一些基本操作。


S3C2440中可以支持很多的存储单元,是通过地址对其进行区分的:

内存控制器通过识别不同的地址发出不同的片选信号(0信号低电平有效)。现针对SDRAM分析,2440外设置的是64M的SDRAM,需要对其进行设置寄存器,其SDRAM可以理解为3维的存储空间,bank * row * col,所以大体过程是由CUP发出地址,经过内存控制器转换成row, col, bank信号(为减少pin)


------------------------------------------------------------------------------------------------------------------------------------------------------


既然使用SDRAM就要和CPU说清楚这是个啥类型SDRAM,毕竟人家也是按需办事,所以配置寄存器BWSCON!

ST6:首先来看看芯片手册这么一句话:“nBE[3:0] is the 'AND' signal nWBE[3:0] and nOE”,即nBE是nWBE和nOE的“与”信号,所以nBE是字节选通信号(读+写),nWBE是读字节选通信号。


由于在进行SDRAM操作的时候只需要在写入的时候需要选择比如说32位中的前4位,输出时候把全部的输出(这是韦老师讲的,这块我也不了解,以后填坑把),所以置0.


WS6:wait的使用时因为有些质量比较差的储存芯片的反映速度没那么快,你叫他半天不理你和痴呆似的,需要等待他就秒,就是这个道理。开发板上的SDRAM比较好所以不等待。置0


SW6:由JZ2440是由两片SDRAM组成起来的32位SDRAM,所以应该选择10


一般会把CS7设置成CS6(也是SDRAM)但是没有使用它。


所以设置BWSCON = 0x22000000;


------------------------------------------------------------------------------------------------------------------------------------------------------


接着设置寄存器BANKCONn,和BWSCON一样,同时设置CS6和CS7。

可以看出[4 : 14]位都不需要我们设置!而且MT很清楚就知道应该置“11”,接着就是[0 : 3]位的配置。


Trcd的值是芯片的参数,根据HY57V561620F(L)T(P) 芯片手册:

为了和时钟频率对应,取20ns。即2 CLOCKs,将[3 : 2]置'00'


继续查看芯片手册:


明显Column address number  = 9,所以设置[1 : 0]为“01”。


所以设置寄存器


BANKCON6 = 0X18001;

BANKCON7 = 0X18001;

------------------------------------------------------------------------------------------------------------------------------------------------------


在2440芯片手册往下拉继续配置寄存器(我也觉得麻烦,没办法继续走)。


接着就是REFRESH寄存器,这个寄存器作用就是使SDRAM自动刷新,这是一个内部的自动操作。由于SDRAM要不断进行刷新(Refresh)才能保留住数据(储存体中电容的有效保存期有限),因此它是SDRAM最重要的操作。这么重要的参数,咋们肯定去SDRAM芯片手册中进行查找啦(保有),首先看看寄存器是怎么配置的:


REFEN:使能没得跑置“1”。


TREFMD:选择自刷新(SR)和自动刷新(AR)。对于AR,SDRAM内部有一个行地址生成器(也称刷新计数器)用来自动的依次生成行地址。由于刷新是针对一行中的所有存储体进行,所以无需列寻址。对于SR,在发出AR命令时,将CKE置于无效状态,就进入了SR模式。此时不再依靠系统时钟工作,而是根据内部的时钟进行刷新操作。在SR期间除了CKE之外的所有外部信号都是无效的(无需外部提供刷新指令),只有重新使CKE有效才能退出自刷新模式并进入正常操作状态。


所以我们选择AR模式对其进行数据的刷新。置“0”默认值。


Trp: 废话不多说直接查芯片手册

老道理,取整 2倍CLOCK。[21 : 20]为“00”


Tsrc:这个就比较特殊了。芯片手册没有嘿嘿。再看看描述中的式子。Trc = Tsrc + Trp.那这么说找到Trc就是我们的目标!那又回到最初的起点,开查芯片手册!就在Trp的上边。取整取70ns,大一点没坏处!Tsrc = 70 - 20 = 5*CLOCK ---> [19 : 18] 置“01”。


Refresh Counter :这个就是最重要的刷新频率了。这参数就放在SDRAM芯片手册的前面几面。

果然找到Refresh period = 64 / 8192 ms,带入公式中得到

  frac{64}{8192} * 10^{-3}=frac{left ( 2^{11}-RC+1 right )}{10^{8}}

由于时钟周期为7.8us,且时钟周期为HCLK = 1000MHZ,Refresh = 1269;


综上所述,REFRESH = 0x8404f5。


------------------------------------------------------------------------------------------------------------------------------------------------------


第四个寄存器BANKSIZE,上图!(半夜两点的我已经不知疲倦)

BK76MAP:这个简单,开发板配套的SDRAM容量为256Mb,注意这里是小b,所以容量大小为256/8 = 32MB,由于外设搭载两片SDRAM,所以容量为64MB。置[2 : 0] ='001'


SCKE_EN:断电模式启用 (以后填坑,先开先)


SCLK_EN:推荐值(以后填坑,先开先)


BURST_EN :  启用突发操作。(以后填坑,先开先)


所以BANKSIZE = 0x000000b1


-----------------------------------------------------------------------------------------------------------------------------------------------------------


最后一个寄存器,MRSRBn 

这个寄存器特点:简单!有fixed值就选fixed值!


唯一需要选择的是CL值,一看诶。芯片手册上边查就行了,这个值的意思是由于在读SDRAM时需要发出:bank row col地址,所以需要等一会儿才有数据发回来。所以在芯片手册中可以查到CL=2或者3,这个值设置后回发送至SDRAM中的MR寄存器,以后在2或者3clock时刻返回数据。所以我们设置为2clock 即  =》“010”


所以MRSRB6 = 0x20; MRSRB7 = 0x20; 


-----------------------------------------------------------------------------------------------------------------------------------------------------------


综上所述,对于SDRAM的初始化函数程序如下所示


#include 's3c2440_soc.h'

 

void sdram_init(void)

{   

   BWSCON = 0x22000000;

   BANKCON6 = 0x18001;

   BANKCON7 = 0x18001;

   REFRESH  = 0x8404f5;

   BANKSIZE = 0xb1;

   MRSRB6   = 0x20;

   MRSRB7   = 0x20;

}


3.2. 判断设置是nor flash启动还是nand flash启动

这一段代码主要是基于NOR flash 可读不可写,然而NAND flash是可读可写的特性,所以可以通过对某一地址进行写数据对其进行判断,但如果是NAND falsh启动的话则会破坏内存,所以还需要保护数据


mov    r1,    #0   

ldr    r0,    [r1]    // 保存数据

str    r1,    [r1]    // 写入数据

ldr    r2,    [r1]    // 读出数据

cmp    r1,    r2      // 如果相等 Z = 1

ldr    sp,    =0x40000000 + 4096   //NOR启动

moceq  sp,    #4096   // NAND启动

streq    r0,    [r1]    //恢复数据

但是对于NOR flash启动的u-boot,他就没有管那么多了。反正他要使用NOR启动,在Start.S文件中使用以下代码,区别就是破坏了NAND flash部分代码


ldr sp, =4092

ldr r0, =0x12345678

str r0, [sp]

ldr r1, [sp]

cmp r0, r1

ldrne sp, =0x40000000+4096

bl clock_init


3.3. 代码重定位

首先用XXH查看dis文件(返回编码文件)。(该文件的程序里面包括了初始化的全局变量和未初始化的全局变量。


用VIM打开dis文件,/搜索关键词:Disassembly of section 查找到程序数据类型包括:


.text、.rodata、.data、,bss、.comment等等


其中看源文件的文件大小可以知道 .bss段是不在bin文件的内容中的这个段包括了未初始化的全局变量,.data段是在bin文件范围中包括了已经初始化的全局变量。这里有两种转移代码的方式:①把需要改变的部分移至SDRAM,②把全部程序移至SDRAM。我们选择第二种进行移动(链接脚本比较简洁)。所以应该把.data的部分进行拷贝。


所以这个时候就要使用链接脚本 *.lds文件,通过查阅Using ld The GNU linker有一下源代码格式:


SECTIONS {

...

secname start BLOCK(align)(NOLOAD) : AT (ldadr)

{ contents } >region:phdr =fill

...

}

start: 起始地址


runtime addr:运行时的地址


relocate addr:重定位的地址


AT(ldadr) Load Addr:加载地址  可以省略不写


LoadAddr = runtime addr 如果没有加AT,它的的加载地址就等于链接时的起始地址


以实例来进行说明:


sdram.lds

 

SECTIONS

{

    . = 0x30000000;              //设置当前地址为SDRAM首地址

 

    . = ALIGN(4);                //向四取整

    .text      :

    {

      *(.text)                   //Load Addr = Routime Addr

    }

    . = ALIGN(4);                // 紧接着.text文件排放              

    .rodata : { *(.rodata) }

    . = ALIGN(4);

    .data : { *(.data) }

    . = ALIGN(4);

    __bss_start = .;

    .bss : { *(.bss) *(.COMMON) }

    _end = .;

}

/* 重定位text, rodata, data段整个程序 */

    mov r1, #0

    ldr r2, =_start         /* 第1条指令运行时的地址 */

    ldr r3, =__bss_start    /* bss段的起始地址 */

 

cpy:

    ldr r4, [r1]             //r1 --> r2

    str r4, [r2]

    add r1, r1, #4           //循环4次 (16byte/4byte)

    add r2, r2, #4

    cmp r2, r3

    ble cpy

 

 

    /* 清除BSS段 */

    ldr r1, =__bss_start

    ldr r2, =_end

    mov r3, #0

clean:

    str r3, [r1]

    add r1, r1, #4

    cmp r1, r2

    ble clean

注:


* ldr: 这里我们可以进行ldrb(1byte)或者使用ldr (4byte),由于sdram是16byte的,所以使用ldr函数能够极大的减少访问SDRA的次数:、


①原来:读ldrb执行16次指令,并且访问16次SDRAM;写strb执行16次,并访问16次SDRAM


②现在:读ldr执行4次指令,并且访问8次(我也很迷惑),写str执行4次,并访问4次SDRAM(每次读出四字节)


*写程序:在写程序中会发出地址加上DPM,把感兴趣的对应写入,对应的是SDRAM配置中的写使能


*.bss段:由于bss段数据都是未初始化,都是0,但是转移后的位置可能存的不是0数据,所以应该对其进行初始化置“0”


* 向四取整:由于ldr的操作是四个字节的赋值,但是我们的地址并不是四字节对齐,所以会进行向四取整


实例:


命令存放地址     3 0 0 0 0 0 0 2 ( 2 < 4)


真实存放地址     3 0 0 0 0 0 0 0 


所以应对方法也是对当前地址进行向四取整即:


. = ALIGN(4);

这样命令存放的地址就变为:


实例:


命令存放地址     3 0 0 0 0 0 0 4 ( 4 >= 4)


真实存放地址     3 0 0 0 0 0 0 4


这就是start文件初认识了!


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 家用电源无载自动断电装置的设计与制作

  • 用数字电路CD4069制作的万能遥控轻触开关

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 开关电源的基本组成及工作原理

  • 用NE555制作定时器

  • 带有短路保护系统的5V直流稳压电源电路图

    相关电子头条文章