文章目录
1.读芯片ID
1.1 读芯片ID时序
简化为4个步骤:
- 1.使能片选
- 2.写命令0x90
- 3.写地址0x00
- 4.读ID信息
/* 识别NAND FLASH */
void scan_nand_flash(void)
{
int i;
//保存读取ID信息的数组
unsigned char id_info[5] = {
0};
nand_enable_cs();//使能CS
nand_write_cmd(0x90);
nand_write_addr(0x00);
for(i = 0;i < 5;i++){
id_info[i] = nand_read_data();
}
nand_disable_cs();//禁止CS
printf("Maker Code: 0x%x\r\n",id_info[0]);
printf("Device Code: 0x%x\r\n",id_info[1]);
printf("3rd cycle: 0x%x\n\r",id_info[2]);
printf("Page size: %d KB\n\r",1 << (id_info[3] & 0x03));//页大小与id_info[3]最低2位有关
printf("Block size: %d KB\n\r",64 << ((id_info[3] >> 4) & 0x03));//块大小与id_info[3]第4、5位有关
printf("5th cycle: 0x%x\n\r",id_info[4]);
1.2 由ID数据获得芯片参数
- ID信息的第4字节为
0x95
- 页大小与id_info[3]最低2位有关,可得页大小为:
2KB
- 块大小与id_info[3]第4、5位有关,可得块大小为:
128KB
注意:如果此时烧写到Nand Flash,并从Nand Flash启动程序是不会成功的,因为这个bin文件大小已经超过了4K,且现在还没有实现nand flash的读函数。
2.读数据
目标:实现从NAND FLASH中启动,重定位所有数据至SDRAM,并实现读取芯片ID数据
2.1 NAND内部结构分析
- OOB:out of bank(在bank之外)
由上图可得: - 1Page = 2KB + 64B
- 1Block = 64 * Pages = 128KB + 4KB
- 1Device = 2048 * Blocks = 256MB + 8MB
- OOB区的作用:因为nand的缺点是会发生
“位反转”
,为了解决这个问题,nand中的OOB区,用于校验数据区的数据是否发生错误,当有错误时,可以恢复数据。(其本身不存储数据) - 因为OOB中并不存放数据,只是用于校验数据区是否发生错误,因此当CPU读取Nand Flash第2048个数据,该数据为
Page1中的第0个byte
2.2 地址序列与时序
- 由地址序列可以看出:发出地址信号共需5个周期,前2个周期发出列地址(Column Address),后3个周期发出行地址(Row Address)
- 地址线序列有一些位是没有用到的,其目的也是以后兼容更大芯片的nand falsh
Nand Flash内部结构展开大致如下:
2.3 读数据流程
- 1.发出片选信号
- 2.发出0x00命令
- 3.发送5个周期的地址(两个列地址,三个行地址(page))
- 4.再发送0x30命令
- 5.等待就绪
- 6.读数据
- 7.禁止片选
2.4 转换所读地址的列与页
将输入的地址addr转换为:
- 列地址(Col Address)
- page是定位到哪一个页,col变量定位的就是在这个页的偏移量(在这个页上的第几列0~2047)
- 行地址(页)
- 因为读取数据的时候是一次性读出一页,因此当给出地址addr之后,每一页的数据大小是2K,因此我们可以根据地址知道我们读取的数据是哪一个页
int col = addr % 2048;//列地址 addr &(2048-1);
int page = addr / 2048;//行地址(相当于页地址)
2.5 NAND等待就绪
- 当NFSATA[0] = 0时,表示正忙
- 当NFSATA[0] = 1时,表示就绪
/* 等待NAND就绪 */
void nand_wait_ready(void)
{
while(!(NFSTAT & 0x01));//当NFSATA[0] = 1时,表示就绪
}
2.6 读取数据函数
/* NAND FLASH读取数据
* param:读取的地址、存放的地址、读取的长度
*/
void read_nand_data(unsigned int addr,unsigned char *buf,unsigned int len)
{
int i = 0;
/*page是定位到哪一个页,col变量定位的就是在这个
*页的偏移量(在这个页上的第几列0~2047)
*/
int col = addr % 2048;//列地址 addr &(2048-1);
/* 因为读取数据的时候是一次性读出一页,因此当给出
* 地址addr之后,每一页的数据大小是2K,因此我们可以
* 根据地址知道我们读取的数据是哪一个页
*/
int page = addr / 2048;//行地址(相当于页地址)