(三)内存 SDRAM 驱动实验 (杨铸 130 页)(勉强能懂个大概)
SDRAM 芯片讲解:
地址: 行地址 (A0-A12) 列地址 (A0-A8) 片选信号(BA0 BA1)(L-BANK)(因为SDRAM有 4片)
两片SDRAM 连线唯一区别在 UDQM LDQM
DQM0 ---片1 LDQM
DQM1----片1 UDQM
DQM2----片2 LDQM
DQM3---片2 UDQM
————————————————————————————————————————————————————————————
1、读操作(见杨铸 121)
地址线上送上要读数据的地址自然要有地址才行,(就好比送快递,首先你得确定好要送达的目的地址在哪里一样)
确定地址:CS低电平 即 nSCS0 拉低,选中外接设备 , L—BANK 也选中SDRAM 内部对应 某一片 (这样就定址了),而且同时
RAS 行地址选通信号也处于有效状态,当然这是要写列地址肯定要 行地址稳定才行(需要时间 tRCD=2),
列地址也假装不稳定,需要一个时间延迟(我们称为 CAS 潜伏期),当然并非不稳定,是芯片自身原因。
地址这样一步步确定,就可以读了(怎么不是写啊,因为 WE 无效,不让写啊,所以相当于读了。有效,就是写)
2、预充电操作
要对同一个L-BANK 的另一行进行寻址,自然要把原先一行关掉。重新发行列地址。
关掉原先一行,要预充电(目的是对整个存储体原有信息进行重写,然后再关闭操作行)
即预充电包含两步(重写+关闭原先行),但是预充电不定期,只在读操作执行
3、写操作
和读操作一样,唯一区别是 经过行延迟(tRCD后)输入写命令(WE低电平有效,拉低,就可以写)
在写完最后一个数据后, 延迟 tWR,发送预充电命令。关闭激活页。
等待 tRP 时间后可以进行下一次操作。
4、写操作
5、刷新操作
——————————————————————————————————————————————
以上可以结合 杨铸书籍和s3c2440芯片手册 memory contrl 来分析的,不算难,除了刷新没耐性看,还好
————————————————————————————————————————————————————————————
开始做内存驱动实验:(2.6.8)
本程序文件主要实现了: 关闭看门狗,初始化内存,复制ROM数据到内存中,然后调到内存执行 Xmain 函数,从 xmain 函数返回后, 将 全部 LED 点亮,进入死循环。
; ; 内存初始化实验 ; AREA Init, CODE, READONLY ENTRY start ; close watchdog ldr r0, = 0x53000000 ; 将看门狗控制寄存器地址放入r0 mov r1, #0 str r1, [r0] ; 设置看门狗控制寄存器的值为0 bl initmem ; 跳转到initmem代码段,初始化内存 bl copyall ; 跳转到数据拷贝代码段,将ROM中数据拷贝到内存中 IMPORT xmain ; 引入main.c中的xmain函数
ldr sp, =0x34000000 ; 调用C程序之前先初始化栈指针 ldr lr, =endxmain ; 设置xmain函数的返回地址 ldr pc, =xmain ; 跳转到C程序中的xmain函数的入口处执行 endxmain ldr r0, =0x56000010 ; LED的GPIO接口配置寄存器(只针对QQ2440,MINI2440开发板) ldr r1, =0x00015400 ; GPIO配置数据 str r1, [r0] ; 设置GPIO ldr r0, =0x56000014 ; LED控制寄存器地址 ldr r1, =0x000000e0 ; 全部LED亮 str r1,[r0] loop b loop ; 死循环 copyall IMPORT |Image$$RO$$Base| ; 引入编译器Image$$RO$$Base符号变量 IMPORT |Image$$RW$$Limit| ; 引入编译器Image$$RW$$Limit符号变量 ldr r0, = |Image$$RO$$Base| ; 取得Image$$RO$Base域基址的值 ldr r1, = |Image$$RW$$Limit|; 取得Image$$RW$Base域结束地址的值 ldr r2, =0x0 ; 数据拷贝源地址
copyallloop teq r0,r1 ; 测试是否拷贝完成 beq quitcopyallloop ; 拷贝完成,跳往quitcopyallloop退出 ldr r3, [r2], #4 ; 四字节加载 str r3, [r0], #4 ; 四字节存储 b copyallloop ; 返回继续执行
quitcopyallloop bx lr ; 调用返回 (lr 即存放 PC指针的值,也就是子程序的返回地址)
initmem ; 内存初始化 ldr r0, =0x48000000 ; 加载内存相关寄存器首地址r0 ldr r1, =0x48000034 ; 加载内存相关寄存器尾地址到r1 adr r2, memdata ; 将寄存器配置数据地址段首地址加载到r2 // 这里是相对地址 initmemloop ldr r3, [r2], #4 ; 循环设置存寄存器 str r3, [r0], #4 teq r0, r1 bne initmemloop ; 循环到最后一个寄存器时退出函数
bx lr memdata DCD 0x22000000 ;BWSCON 选中 bank6 bank7 32bit数据输入输出 // 用于分配一片连续的字存储单元并用指定的数据初始化(参考芯片手册, BWSCON 到 MRSRB7 正好13个字,也就是 0x48000000---0x48000034) DCD 0x00000700 ;BANKCON0 DCD 0x00000700 ;BANKCON1 DCD 0x00000700 ;BANKCON2 DCD 0x00000700 ;BANKCON3 DCD 0x00000700 ;BANKCON4 DCD 0x00000700 ;BANKCON5 DCD 0x00018005 ;BANKCON6 // 确定存储芯片类型为 SDRAM RAS到CAS delay 3个时钟周期 列地址 9 bit DCD 0x00018005 ;BANKCON7 // 同上 DCD 0x008e07a3 ;REFRESH // sdram 刷新控制 DCD 0x000000b1 ;BANKSIZE DCD 0x00000030 ;MRSRB6 DCD 0x00000030 ;MRSRB7 END
讲解:http://www.cnblogs.com/idle_man/archive/2010/12/18/1910158.html (跳转到数据拷贝代码段,将ROM中数据拷贝到内存中)// 这里看杨铸 P95 页一目了然
copyall
IMPORT |Image$$RO$$Base| ; 引入编译器Image$$RO$$Base符号变量
IMPORT |Image$$RW$$Limit| ; 引入编译器Image$$RW$$Limit符号变量
ldr r0, = |Image$$RO$$Base| ; 取得Image$$RO$Base域基址的值
ldr r1, = |Image$$RW$$Limit|; 取得Image$$RW$Base域结束地址的值
对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。
在ADS的Debug Settings中有一栏是Linker/ARM Linker,在output选项中有一个RO base选项,下面应该有一个地址,我这里是0x0000 0000(不是每个都一样的),后面的RW base 地址是0x3000 0000,
然后在Options选项中有Image entry point ,是一个初始程序的入口地址,我这里是0x0000 0000 。
|Image$$RO$$Base| = Image entry point = 0x0000 0000 ;表示程序代码存放的起始地址
|Image$$RO$$Limit|=程序代码起始地址+代码长度+1=0x0c100000+Tatal RO size+1
|Image$$RW$$Base| = 0x0c200000 ;由RW base 地址指定
内存初始化也可以通过C语言来实现:
1 #define MEM_CTL_BASE 0x48000000 2 #define MEM_CTL_END 0x48000034 3 unsigned long const mem_cfg_val[]= 4 { 5 0x22000000, //BWSCON // 用于分配一片连续的字存储单元并用指定的数据初始化 6 0x00000700, //BANKCON0 7 0x00000700, //BANKCON1 8 0x00000700, //BANKCON2 9 0x00000700, //BANKCON3 10 0x00000700, //BANKCON4 11 0x00000700, //BANKCON5 12 0x00018005, //BANKCON6 13 0x00018005, //BANKCON7 14 0x008e07a3, //REFRESH 15 0x000000b1, //BANKSIZE 16 0x00000030, //MRSRB6 17 0x00000030, //MRSRB7 18 19 } ; 20 void mem_init(void) 21 { 22 int i=0; 23 unsigned long * p=(unsigned long *)MEM_CTL_BASE; 24 for(:i<(MEM_CTL_END-MEM_CTL_BASE)/4;i++) 25 p[i]=mem_cfg_val[i]; 26 27 28 29 }