嵌入式入门4(内存控制器和SDRAM)

通常ARM芯片,都包含如下几类接口:html

一、GPIO、门电路前端

这类接口经过操做某些寄存器,来设置对应引脚为输入、输出引脚,以及其引脚的电平。linux

二、协议类接口编程

CPU将数据写入某些地址和寄存器,对应引脚就会发出特定的波形。如:UART、I2C、I2S、SPI等。markdown

三、内存接口app

CPU经过地址能够直接访问这类设备,由于它们都参与CPU的统一编址。如:Nor、SDRAM、DM9000(网卡芯片)等,dom

注意:Nand Flash不参与CPU的统一编址,由于它是直接链接到Nand Flash控制器上的。异步

未命名文件.png

1、内存控制器

S3C2440A芯片内部集成了内存控制器,几乎全部的外设和寄存器都与它相连。CPU只负责发出命令,其他都交给了内存管理器处理。那么内存管理器是如何来管理这些外设的呢?测试

咱们能够查看s3c2440的地址空间分布ui

image.png

  • 一、地址空间被分为8个bank(bank0-bank7),每一个bank对应128M地址空间。
  • 二、每一个bank拥有对应的片选引脚(nGCS0-nGCS7),当引脚为低电平时表示该bank被选通。
  • 三、内存控制器根据不一样的地址范围,发出不一样的片选信号,只有被选中的芯片才会工做,没被选中的芯片就像不存在同样。

一个bank对应128M地址空间,须要27根地址线来表示。

128 M B = 128 1 M B = 2 7 2 20 = 2 27 128MB = 128 * 1MB = 2^7 * 2^{20} = 2^{27}

那么8个bank,意味着外接内存类芯片可访问地址范围最多为1GB(128M*8)。

咱们看到地址空间是0-0x40000000,总共是1G。而s3c2440的cpu是32位的,它理论上可使用的地址能够达到4G,除了用于链接外设的1G地址空间以外,还有一部分是cpu内部寄存器的地址(0x48000000-0x5fffffff),其他的地址没有使用。

2、外接芯片的地址线接法

外接芯片的位宽不一样,则地址线的接法也会不一样。能够参考S3C2440A的第5章 Memory Controller中的ROM Memory Interface Examples。例如:

image.png

ROM/bit CPU发出地址 ROM收到地址 ROM返回的数据 内存控制器返回给CPU的数据
8bit(ROM) 000011 000011 编号3的存储单元中的8数据 编号3的存储单元中的8bit数据
16bit(ROM) 000011 000001 编号1的存储单元中的16数据 根据A0=1,挑出低8bit数据
32bit(ROM) 000011 000000 编号0的存储单元中的32数据 根据A0A1=11,挑出最低8bit数据

3、时序图分析

查看s3c2440的PROGRAMMABLE ACCESS CYCLE

image.png

时序图流程以下:

  • 一、CPU先发出地址信号。
  • 二、通过Tacs时间后再发出片选信号。
  • 三、通过Tcos时间后发出读信号。
  • 四、发出读信号后,数据才有效。
  • 五、读出数据后,再移除释放读信号、片选信号、地址信号。

由咱们的JZ2440电路图可知,Nor Flash为MX29LV160DBTI-70G,而后查看其芯片手册。其交流特性以下:

image.png

image.png

参数 含义
Taa 发出地址数据后多久数据才有效
Tce 发出片选信号后多久数据才有效
Toe 发出读信号后多久数据才有效

4、开始编程

为了简单起见,咱们设置片选信号、读信号、地址信号同时发出,而后保持70ns,再读取数据,这样就知足Nor Flash的时序要求了。

4.一、设置位宽

首先,设置BWSCON[2:1],用于设置bank0的位宽。

image.png

而DW0是只读的,它是经过OM[1:0]引脚来决定的

image.png

查看硬件电路图:

image.png

image.png

发现OM[1]直接接地,而OM[0]的电平,由Nor boot switch决定。咱们经过开关,决定了S3C2440A的启动方式:32bit Nor FlashNand-boot

4.二、配置BANKCON0

咱们Nor Flash接到Bank0,因此须要配置BANKCON0。

image.png

设备上电时,采用晶振作为时钟源,12MHz,Tacc=0x111,14个周期。

14个周期=14/12MHz秒 = 14/12_000_000秒 = 14000/12纳秒 = 1166.6纳秒,远大于Nor Flash要求的70ns。因此当咱们上电时,即便不设置时钟,设备也能正常运行。

而咱们如今HCLK=100MHz:1个周期=10纳秒,Tacc只需设置为0x101,即8个时钟周期,就能知足Nor Flash大于70纳秒的时序要求。

4.三、全部源代码

init.c

#include "s3c2440_soc.h"

void bank0_tacc_set(int val) {
    BANKCON0 = val << 8;
}
复制代码

init.h

#ifndef _INIT_H
#define _INIT_H

void bank0_tacc_set(int val);

#endif
复制代码

led.c

#include "s3c2440_soc.h"

void delay(volatile int d) {
    while (d--);
}

int led_test(void) {
    /* 设置GPFCON让GPF4/5/6配置为输出引脚 */
    GPFCON &= ~((3<<8) | (3<<10) | (3<<12));
    GPFCON |=  ((1<<8) | (1<<10) | (1<<12));

    /* 循环点亮 */
    int i = 4;
    while (1) {
        for(i=4; i<=6; i++) {
            //对数据寄存器4~6位取反
            GPFDAT ^= (1<<i);
            delay(30000);
        }
    }

    return 0;
}
复制代码

main.c

#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"

int main(void) {
    unsigned char c;

    uart0_init();
    puts("Enter the Tacc val: \n\r");

    while(1)
    {
        c = getchar();
        putchar(c);
        if (c >= '0' && c <= '7')
        {
            bank0_tacc_set(c - '0');
            led_test();
        }
        else
        {
            puts("Error, val should between 0~7\n\r");
            puts("Enter the Tacc val: \n\r");
        }
    }
    return 0;
}
复制代码

start.S

.text
.global _start

_start:

    /* 关闭看门狗 */
    ldr r0, =0x53000000
    ldr r1, =0
    str r1, [r0]

    /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
    /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
    ldr r0, =0x4C000000
    ldr r1, =0xFFFFFFFF
    str r1, [r0]

    /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
    ldr r0, =0x4C000014
    ldr r1, =0x5
    str r1, [r0]

    /* 设置CPU工做于异步模式 */
    mrc p15,0,r0,c1,c0,0
    orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
    mcr p15,0,r0,c1,c0,0

    /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
     *  m = MDIV+8 = 92+8=100
     *  p = PDIV+2 = 1+2 = 3
     *  s = SDIV = 1
     *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
     */
    ldr r0, =0x4C000004
    ldr r1, =(92<<12)|(1<<4)|(1<<0)
    str r1, [r0]
    /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
     * 而后CPU工做于新的频率FCLK
     */

    /* nor启动 */
    ldr sp, =0x40000000+4096 

    bl main

halt:
    b halt
复制代码

uart.c

#include "s3c2440_soc.h"


/* 115200,8n1 */
void uart0_init() {
    /* 设置引脚用于串口 */
    /* GPH2,3用于TxD0, RxD0 */
    GPHCON &= ~((3<<4) | (3<<6));
    GPHCON |=  ((2<<4) | (2<<6));

    GPHUP &= ~((1<<2) | (1<<3));  /* 使能内部上拉 */


    /* 设置波特率 */
    /* UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1 * UART clock = 50M * UBRDIVn = (int)( 50000000 / ( 115200 x 16) ) –1 = 26 */
    UCON0 = 0x00000005; /* PCLK,中断/查询模式 */
    UBRDIV0 = 26;

    /* 设置数据格式 */
    ULCON0 = 0x00000003; /* 8n1: 8个数据位, 无较验位, 1个中止位 */
}

int putchar(int c) {
    /* UTRSTAT0 */
    while (!(UTRSTAT0 & (1<<2)));
    /* UTXH0 */
    UTXH0 = (unsigned char)c;
}

int getchar(void) {
    while (!(UTRSTAT0 & (1<<0)));
    return URXH0;
}

int puts(const char *s) {
    while (*s)
    {
        putchar(*s);
        s++;
    }
}
复制代码

uart.h

#ifndef _UART_H
#define _UART_H

void uart0_init();
int putchar(int c);
int getchar(void);
int puts(const char *s);

#endif
复制代码

Markfile

all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o uart.o uart.c
	arm-linux-gcc -c -o init.o init.c
	arm-linux-gcc -c -o main.o main.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o led.o uart.o init.o main.o -o nor.elf
	arm-linux-objcopy -O binary -S nor.elf nor.bin
	arm-linux-objdump -D nor.elf > nor.dis
clean:
	rm *.bin *.o *.elf *.dis
复制代码

编译烧写后,串口输入0~7,其中0~4,因为Tacc小于70ns,因此灯不会闪烁,只有5~7才会闪烁。

5、SDRAM

SDRAM:Synchronous Dynamic Random Access Memory,同步动态随机存储器。同步是指其时钟频率和CPU前端总线的系统时钟相同,而且内部命令的发送与数据的传输都以它为基准;动态是指存储阵列须要不断的刷新来保证数据不丢失;随机是指数据不是线性依次存储,而是自由指定地址进行数据的读写。

通常SDRAM的存储结构以下:

image.png

它由多个表格(Bank)组成,并经过行地址和列地址来进行访问,每一个小格子表示一个存储单元,单元的大小等于SDRAM的位宽。

  • 一、CPU先发出片选信号,选中整个芯片。
  • 二、发出Bank地址,选择哪一个表格(Bank)。
  • 三、而后依次发出行地址和列地址,才能定位到具体的存储单元。

5.一、JZ2440的SDRAM接法

查看JZ2440的SDRAM电路图:

image.png

能够发现:

  • 一、JZ2440外接2片SDRAM,每片提供16位的数据,容量为32M,共计64M,32位。
  • 二、SDRAM接在JZ2440的片选6引脚,也就是说SDRMAM在第六个Bank地址空间中。
  • 三、BA[0:1]分别接在ADDR[25:24]上,用于选中SDRAM里的哪一个Bank。

JZ2440的SDRAM芯片型号是K4S561632N-LC75,查看其芯片手册(K4S561632E):

image.png

发现如下有用信息:

  • 一、每片SDRAM组织形式:4M * 16bit * 4Bank = 32M
  • 二、行地址13条,列地址9条。

参考SDRAM BANK ADDRESS PIN CONNECTION EXAMPLE,能够知道Bank Address为A[25:24],对比JZ2440的SDRAM电路图,确实是BA[0:1]分别接在ADDR[25:24]上。

image.png

如今能够配置SDRAM,来测试SDRAM的读写操做。

5.二、设置BANK6为SDRAM

首先咱们须要设置内存控制器,让Bank6设置为SDRAM,这样内存控制器在发送地址时,才会将其拆分为Bank地址、行地址和列地址。

image.png

BANKCON6[16:15]决定Bank6接入的是哪一种类型的内存芯片,这里咱们设置为SDRAM,即BANKCON6[16:15]=0x11

image.png

因为外接的SDRAM列地址有9条,因此BANKCON6[1:0]=0x01

Trcd,表示内存控制器发出行地址多久后,才能发出列地址。

查看SDRAM芯片手册的交流特性图:

image.png

知道Trcd最小取20ns,用于HLCK=100MHZ,即一个周期须要10ns,使用Trcd至少须要2个周期,即BANKCON6[3:2]=0x00

BANKCON6 = 0x00018001
复制代码

5.三、设置BANK6位宽

image.png

  • 设置BWSCON[25:24]为10,由于JZ2440是由2个16为SDRAM芯片组成的32位SDRAM。
  • BWSCON[26],是否使用WAIT信号,它是内存芯片向CPU发出来的。假设内存芯片很是慢,当CPU发出读写命令后,内存控制器开始驱动引脚,在这些时间里,内存芯片还没准备好数据,此时它能够向CPU发出nWAIT信号,请求CPU再宽限一点时间。
  • BWSCON[27],设置SDRAM是否使用UB/LB引脚,UB/LB分别是存储器高8位,低8位的数据线使能信号。

nBE:Byte Enable,表示读或者写的时候,是否能够经过操做这个引脚,来操做这个Byte。
nWBE:Write Byte Enable,表示写某个字节的时候,是否真正的写进去。

因为咱们内存是32位(4字节)的,若是咱们只想修改某个字节,此时就须要经过nWEB引脚,来决定某个字节会被写入。而读的时候,咱们老是读出4字节数据,而后内存控制器从中挑选出咱们所感兴趣的数据。

BWSCON = 0x02000000
复制代码

5.四、设置SDRAM刷新寄存器

SDRAM不像静态SRAM那么可靠,在使用过程当中,必须不断刷新它,否则数据会丢失。

image.png

  • 设置REFEN = 1,表示开启SDRAM刷新。
  • 设置TREFMD = 0,设置为自动刷新模式。
  • Refresh Counter,刷新周期,查看SDRAM芯片手册,自动其刷新为64ms refresh period (8K Cycle),即刷新周期为:

64 1000 u s 8 1024 = 7.8125 u s \frac{64*1000us}{8*1024} = 7.8125us

而咱们HCLK=100MHz,恰好与实例一致,因此Refresh Count = 1269 = 0x4F5

  • Trp,行地址的充电时间。由SDRAM芯片手册的交流特性图可知,Trp最小为20ns,因此REFERSH[21:20]=0x00
  • Tsrc,Trc = Tsrc + Trp。Trc为65ns,因此Tsrc=65-20=45ns。设置去0x01,即5个周期。

image.png

REFERSH = 0x008404f5
复制代码

5.五、设置Bank寄存器

image.png

  • 设置BANKSIZE[2:0]=001,由于SDRAM是由两个16位的32M芯片组成的32位64M芯片。
  • 设置BANKSIZE[7]=1,容许突发访问,即同时能够连续一次访问多个字节。
  • 设置BANKSIZE[5]=1,可使用SCKE来使能休眠模式。
  • 设置BANKSIZE[4]=1,推荐值。
BANKSIZE= 0x000000b1
复制代码

5.六、设置SDRAM模式寄存器

image.png

除了CL,其他都是固定的。

CAS latency:内存控制器读SDRAM时,先发出Bank地址,再发出行地址,最后发出列地址。还要等一会,数据才好。

SDRAM芯片手册在FEATURES中告诉咱们:CAS latency(2 & 3)。当咱们设置CL后,CPU会发送给SDRAM,SDRAM会将其保存在自身的寄存器中,当SDRAM收到列地址后,等待一段时间后,才会返回数据。

MRSRB6 = 0x00000020
复制代码

5.七、全部代码

init.h

#ifndef _INIT_H
#define _INIT_H

void sdram_init();
int sdram_test();

#endif
复制代码

init.c

#include "s3c2440_soc.h"

void sdram_init() {
    BWSCON = 0x02000000;
    BANKCON6 = 0x00018001;
    REFRESH = 0x008404f5;
    BANKSIZE = 0x000000b1;
    MRSRB6 = 0x00000020;
}

int sdram_test() {
    volatile unsigned char* p = (volatile unsigned char*)0x30000000;
    int i;

    for(i=0; i<1000; i++){
        p[i] = 'A';
    }

    for(i=0; i<1000; i++){
        if(p[i] != 'A'){
            return -1;
        }
    }
    return 0;
}
复制代码

main.c

#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"

int main(void) {
    uart0_init();
    sdram_init();
    if(sdram_test()==0)
    {
        puts("ok\n\r");
        led_test();
    } else {
        puts("error\n\r");
    }
    return 0;
}
复制代码
相关文章
相关标签/搜索