历史上的今天
今天是:2025年10月28日(星期二)
2022年10月28日 | 【JZ2440笔记】裸机实验使用NandFlash
2022-10-28 来源:csdn
一、前言
S3C2440芯片内部没有ROM可以放用户代码,所以用户代码需要被保存在外部的存储器当中,如果是NorFlash的话可以直接在NorFlash中运行程序,但是NorFlash比较贵,一般都用NandFlash作为存储介质,以SDRAM为代码的运行空间。JZ2440开发板上有一颗256MB容量的NandFlash芯片,记录下学习过程,代码是开发板自带的例程。
二、实验目标
在SRAM运行程序关闭看门狗,初始化SDRAM,初始化NandFlash控制器,随后将NandFlash块1中的main.c部分代码拷贝到SDRAM中运行,看到开发板上3个LED显示流水灯效果。
三、资源分析

NandFlash型号K9F2G08U0C,SLC三星颗粒,基本参数如下:

Page大小为2K+64Byte,意思是一个Page前2KB使用来存数据的,后面的64字节用来存ECC(错误纠正码)或者自定义的冗余数据。NandFlash由于其物理特性,一般都会有坏块的,而且NandFlash里面存的数据不像SRAM那样稳定,可能会出现位翻转现象,所以需要加入一些校验码(ECC)来纠正这些错误。其实因为这颗NandFlash是SLC的缘故,相比于MLC或者TLC的NandFlash来说稳定性高得多,一般环境条件下也是很少出现位翻转现象的。一个Page虽然真实是有2K+64Byte的数据量,但是一般是只使用2K的,当然如果非要用上后面的64Byte数据也是可以的,那样就用不了ECC了,这个东西只是建议用2K而已,具体想怎么个用法还是看自己的。
Block(块)大小为128K+4K,意思是一个Block有64个Page,写入的最小单位是Page,擦除的最小单位是Block。全盘有2048个Block。
NandFlash接口比SDRAM的简单,地址总线和数据总线是共用的。
大页模式下,发送地址时有5个周期,发送5个字节的地址。如下:

前两个字节是列地址,*L表示强制为0,所以列地址实际用了12个位来寻址,可以寻址0到4096的范围,是用来寻址Page内的字节偏移的,恰好够寻址2KB + 64Byte的数据量。
后三个字节是行地址,*L表示强制为0,所以行地址实际用了17个位来寻址,可以寻址0到128K的范围,是用来寻址Page序号的,64个Page X 2048个Block = 128K个Page,所以刚好17个位够用。
往S3C2440的几个NandFlash专用寄存器读写数据,NandFlash控制器就会将这些数据以命令或者数据的形式操作IO引脚控制NandFlash。如下:

四、程序代码
程序分为6个文件,分别如下:
head.S:启动文件,完成相关初始化。
init.c:初始化相关的C函数代码。
nand.c:NandFlash初始化及相关操作代码。
main.c:跑流水灯的代码,需要被拷贝到SDRAM中运行。
nand.lds:链接脚本,划分程序段。
Makefile:编译代码。
这些代码文件内容分别如下:
head.S
@******************************************************************************
@ File:head.s
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@******************************************************************************
.text
.global _start
_start:
@函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
ldr sp, =4096 @设置堆栈
bl disable_watch_dog @关WATCH DOG
bl memsetup @初始化SDRAM
bl nand_init @初始化NAND Flash
@将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
@nand_read_ll函数需要3个参数:
ldr r0, =0x30000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址
mov r1, #4096 @2. 源地址 = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处
mov r2, #2048 @3. 复制长度= 2048(bytes),对于本实验的main.c,这是足够了
bl nand_read @调用C函数nand_read
ldr sp, =0x34000000 @设置栈
ldr lr, =halt_loop @设置返回地址
ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
halt_loop:
b halt_loop
init.c
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000)
/* SDRAM regisers */
#define MEM_CTL_BASE 0x48000000
void disable_watch_dog();
void memsetup();
/*上电后,WATCH DOG默认是开着的,要把它关掉 */
void disable_watch_dog()
{
WTCON = 0;
}
/* 设置控制SDRAM的13个寄存器 */
void memsetup()
{
int i = 0;
unsigned long *p = (unsigned long *)MEM_CTL_BASE;
/* SDRAM 13个寄存器的值 */
unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
0x00000700, //BANKCON0
0x00000700, //BANKCON1
0x00000700, //BANKCON2
0x00000700, //BANKCON3
0x00000700, //BANKCON4
0x00000700, //BANKCON5
0x00018005, //BANKCON6
0x00018005, //BANKCON7
0x008C07A3, //REFRESH
0x000000B1, //BANKSIZE
0x00000030, //MRSRB6
0x00000030, //MRSRB7
};
for(; i < 13; i++)
p[i] = mem_cfg_val[i];
}
其实SDRAM的初始化就配置BANK6相关的几个寄存器就可以了,例程这里配多了。
nand.c
#define LARGER_NAND_PAGE
#define GSTATUS1 (*(volatile unsigned int *)0x560000B0)
#define BUSY 1
#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
#define NAND_SECTOR_SIZE_LP 2048
#define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1)
typedef unsigned int S3C24X0_REG32;
/* NAND FLASH (see S3C2410 manual chapter 6) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFECC;
} S3C2410_NAND;
/* NAND FLASH (see S3C2440 manual chapter 6, www.100ask.net) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECCD0;
S3C24X0_REG32 NFMECCD1;
S3C24X0_REG32 NFSECCD;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSBLK;
S3C24X0_REG32 NFEBLK;
} S3C2440_NAND;
typedef struct {
void (*nand_reset)(void);
void (*wait_idle)(void);
void (*nand_select_chip)(void);
void (*nand_deselect_chip)(void);
void (*write_cmd)(int cmd);
void (*write_addr)(unsigned int addr);
unsigned char (*read_data)(void);
}t_nand_chip;
static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;
static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
static t_nand_chip nand_chip;
/* 供外部调用的函数 */
void nand_init(void);
void nand_read(unsigned char *buf, unsigned long start_addr, int size);
/* NAND Flash操作的总入口, 它们将调用S3C2410或S3C2440的相应函数 */
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static unsigned char read_data(void);
/* S3C2410的NAND Flash处理函数 */
static void s3c2410_nand_reset(void);
static void s3c2410_wait_idle(void);
static void s3c2410_nand_select_chip(void);
static void s3c2410_nand_deselect_chip(void);
static void s3c2410_write_cmd(int cmd);
static void s3c2410_write_addr(unsigned int addr);
static unsigned char s3c2410_read_data();
/* S3C2440的NAND Flash处理函数 */
static void s3c2440_nand_reset(void);
static void s3c2440_wait_idle(void);
static void s3c2440_nand_select_chip(void);
static void s3c2440_nand_deselect_chip(void);
史海拾趣
|
一、输入阻抗 输入阻抗是指一个电路输入端的等效阻抗。在输入端上加上一个电压源U,测量输入端的电流I,则输入阻抗Rin就是U/I。你可以把输入端想象成一个电阻的两端,这个电阻的阻值,就是输入阻抗。 输入阻抗跟一个普通的电抗元件没什么两样 ...… 查看全部问答> |
|
在移植完uboot1.1.6后,除了网络功能其它都没问题了 可是现在ping不通,也就不能使用tftp 板子上是DM9000AEP芯片,网友说是uboot自带驱动与AEP并不兼容 请问一下如何修改驱动以使DM9000AEP 芯片正常工作? 谢谢,祝好!… 查看全部问答> |
|
各位大哥: 最近在做i2c的设备驱动程序。在网上找了些资料。针对自己的设备写了一个驱动程序。但编译不过去。 提示variable \'ds2746_driver\' has initializer but incomplete type. unknow field \'o ...… 查看全部问答> |
|
市场调研公司公布2006年全球半导体供应商排名,ST稳坐第五 在iSuppli研究的250家半导体公司中,有190家或76%在2006年实现成长。其中,128家的销售额实现了两位数的增长。在五大半导体供应商中,除了英特尔以外,2006年都超过了半导体市场的平均增长率,至少增长了11%。… 查看全部问答> |
|
DS18B20_writeonechar(0x33); for(b=0;b<8;b++) { a= DS18B20_readonechar(); } &nb ...… 查看全部问答> |
|
ULN2003A中COM端为啥可以悬空?可以接高电平吗?接高电平时可以接多大的电压?COM端与各输出端之间的反向二极管起什么作用?当COM端悬空时,该二极管还能发挥作用吗?… 查看全部问答> |




