硬件原理分析

clipboard.png

引脚功能

CLE高电平ALE低电平时传输命令
CLE低电平ALE高电平时传输地址
ALECLE低电平时传输数据
CLE片选信号低电平有效,因为LDATA信号是多芯片共用的
RnB状态引脚,高电平表示空闲,低电平表示正忙
WE引脚上升沿锁存写入数据
RE下降沿后延迟tREA读出数据

clipboard.png

可以看到WE上升沿通知nand写入数据,RE下降沿后延迟tREA输出数据

直接操作和nand控制器操作对比

clipboard.png

命令列表

clipboard.png

使用uboot体验nand操作

uboot安装参考上一篇文章韦东山一期视频学习笔记-Norflash
####读ID

clipboard.png

1.选中芯片
NFCONT(0X4E000004)bit1置1选中芯片,md.l 4E000004 1读出寄存器值(因为是32bit寄存器)

clipboard.png

可以看到芯片没有选中,mw.l 4E000004 1使能nand flash

clipboard.png

2.发出命令0x90
NFCMMD(0X4E000008)写入0x90,mw.b 4E000008 90b写入1字节)

3.发出地址0x00
NFADDR(0X4E00000C)写入0x00,mw.b 4E00000C 00b写入1字节)

4.读数据
读取NFDATA(0X4E000010)uboot命令md.b 4E000010 1

5.读Device Code
读取NFDATA(0X4E000010)uboot命令md.b 4E000010 1

可以看到读出的数据和芯片手册一致
clipboard.png

6.退出读ID状态Reset命令
mw.b 4E000008 ff

读0地址内容

使用uboot命令nand dump 0可以查看nand内数据

clipboard.png

读操作时序

clipboard.png

发出以下命令进行读0地址测试

mw.l 4E000004 1 //使能nand flash
mw.b 4E000008 00 //发出命令
mw.b 4E00000C 00 //发出地址
mw.b 4E00000C 00 //发出地址
mw.b 4E00000C 00 //发出地址
mw.b 4E00000C 00 //发出地址
md.b 4E000010 1 //读数据
mw.b 4E000008 ff //reset命令

可以看出运行结果和uboot命令直接读取nand flash结果一致
clipboard.png

K9F1208U0M Nand flash储存结构分析(64M)

clipboard.png

1页是528字节(512字节 + 16字节oob)
1块有32

CPU访问Nand地址不会访问到oob部分,对CPU来说是不存在的,一页只有512Byte。oob的存在是为了解决nand数据易错的问题

K9F1208U0M Nand flash时序分析

2440 Nand控制器时序
clipboard.png

K9F1208U0M芯片时序
clipboard.png
clipboard.png

从图中可以看出从CLE/ALE信号发出到WE信号发出的延迟TACLS可以设置为0,WE有效时间TWRPH0最小25nsTWRPH1最小10ns

nand flash控制器初始化配置

NFCONF寄存器

clipboard.png

初始化之后HCLK配置为100M(10ns),根据上面分析的时序寄存器配置为

#define TACLS        0    // 1-clk(0ns) 
#define TWRPH0        6    // 3-clk(25ns)
#define TWRPH1        0    // 1-clk(10ns)  //TACLS+TWRPH0+TWRPH1>=50ns

NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);
NFCONT寄存器

clipboard.png

配置使能NAND flash控制器,暂时关闭片选,开ECC

NFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(1<<1)|(1<<0);    

nand flash ID读取

//读ID
void Nand_Read_ID(){
    unsigned char buf[4];
    
    Nand_Select();
    Nand_CMD(0x90);
    Nand_Addr_Byte(0x00);
    buf[0] = Nand_Data();
    buf[1] = Nand_Data();
    buf[2] = Nand_Data();
    buf[3] = Nand_Data();
    Nand_Deselect();
    
    printf("Byte 1 = 0x%x\n\r", buf[0]);
    printf("Byte 2 = 0x%x\n\r", buf[1]);
    printf("Byte 3 = 0x%x\n\r", buf[2]);
    printf("Byte 4 = 0x%x\n\r", buf[3]);
}

注意头文件寄存器的定义NFDATA需要指向unsigned char *类型

#define     __REG_BYTE(x)                (*(volatile unsigned char *)(x)) 
#define     NFDATA                   __REG_BYTE(0x4E000010)  //NAND flash data    

生成的bin文件有11kb所以需要烧录到norflash运行

nand flash数据读取

clipboard.png
这里的地址信号只针对当前64Mflash。首先发出列地址然后发出page地址,列地址分为上半区256字节和下半区256字节,如果列地址起始地址在0~255使用00命令,列地址起始地址在256~511使用01命令

//读flash
//参数;读取地址 读取储存数组 读取长度
void Nand_Read(unsigned int addr, unsigned char * buf, unsigned int len){
    int i = 0;
    int page, col;
    
    Nand_Select();
    
    while(i < len){//循环读
        page = addr / PAGE_SIZE;//第几页
        col = addr % PAGE_SIZE;//第几列
        
        if(col >= 0 && col <= 255){
            Nand_CMD(0x00);//上半区读命令
        }else if(col >= 256 && col <= 511){
            Nand_CMD(0x01);//下半区读命令
            col = col - 256;    
        }
        Nand_Addr_Byte(col & 0xff);
        Nand_Addr_Byte(page & 0xff);
        Nand_Addr_Byte((page>>8) & 0xff);
        Nand_Addr_Byte((page>>16) & 0xff);
        
        Nand_Wait_Ready();
        
        //连续读
        for(;(col < 256) && (i < len); addr++){
            buf[i++] = Nand_Data();
        }
        if(i == len)
            break;
    }
    Nand_Deselect();
}

nand flash擦除

nand flash擦除是以为单位进行擦除的,1块等于16KB

clipboard.png

擦除函数地址和长度只接受16K整数倍的值,判断addr & 0X3FFF的值0X3FFF等于16 * 1024 - 1二进制为‭0011111111111111‬只有地址&运算为0也就是bit13~bit0为0才运行擦除

//擦除flash 块为单位
//参数;擦除起始地址 擦除长度
int Nand_Erase(unsigned int addr, unsigned int len){
    unsigned int page;
    unsigned char status;
    
    if(addr & (0x3FFF)){
        printf("Nand_Erase addr is not block align\n\r");
        return -1;
    }
    if(len & (0x3FFF)){
        printf("Nand_Erase len is not block align\n\r");
        return -1;
    }
    if(len == 0){
        printf("Nand_Erase len is 0\n\r");
        return -1;        
    }

    Nand_Select();
    while(1){
        
        page = addr / PAGE_SIZE;//擦除地址所在的块
        
        //printf("page = %d\n\r", page);
        
        Nand_CMD(0x60);

        Nand_Addr_Byte(page & 0xff);
        Nand_Addr_Byte((page>>8) & 0xff);
        Nand_Addr_Byte((page>>16) & 0xff);
        
        Nand_CMD(0xD0);

        Nand_Wait_Ready();
        
        //读取状态位
        Nand_CMD(0x70);
        status = Nand_Data();
        printf("status = 0x%x\n\r", status);
        
        len = len - (16 * 1024); //减去1个块的长度
        if(len == 0)
            break;
        addr = addr + (16 * 1024);//指向下一个block
    }
    Nand_Deselect();
    return 0;
}

nand flash写入

clipboard.png

因为列地址只能在A0~A7之间寻址,所以写入的起始地址只能在页的前256字节,

//写flash
//参数;写入地址 写入数组 长度
int Nand_Write(unsigned int addr, unsigned char * buf, unsigned int len){
    int i = 0;
    int page, col;
    
    printf("addr = 0x%x; buf = %s; len = %d\n\r", addr, buf, len);
    
    col = addr % PAGE_SIZE;
    if(col > 255){
        printf("Starting address is greater than 255\n\r");
        return -1;
    }
    
    
    Nand_Select();
    while(1){
        page = addr / PAGE_SIZE;//第几页
        
        col = addr % PAGE_SIZE;//第几列
        
        printf("page = %d; col = %d\n\r", page, col);
        
        Nand_CMD(0x80);
        
        //发出地址
        Nand_Addr_Byte(col & 0xff);
        Nand_Addr_Byte(page & 0xff);
        Nand_Addr_Byte((page>>8) & 0xff);
        Nand_Addr_Byte((page>>16) & 0xff);
        
        //写数据 连续写
        for(;(col < PAGE_SIZE) && (i < len); addr++){
            //buf[i++] = Nand_Data();
            Nand_WriteData(buf[i++]);
        }
        Nand_CMD(0x10);
        Nand_Wait_Ready();
        if(i == len)
            break;        
    }
    Nand_Deselect();
    
    return 0;
}

Nand启动运行

首先需要调整start.S从Nand中复制大于等于bin文件的字节数到SDRAM中,并且要调整makefile顺序保证SDRAMNand初始化函数和拷贝函数在前4k(通过dis文件反汇编查看)

start.S部分复制长度是20 * 1024字节,目的地址0x30000000(SDRAM起始地址)
    mov r0, #0 //Nand地址
    ldr r1, =0x30000000    //目的地址
    ldr r2, =20480 //复制长度
    bl Nand_Copy_to_SDRAM 
复制函数

以字节为单位进行复制,这部分只会运行一次所以没有特别在意效率

//Nand -> SDRAM 复制函数
void Nand_Copy_to_SDRAM(unsigned int source_addr, unsigned char * dst_addr, int len){
    unsigned char buf[64];
    int i;
    int index = 0;
    
    while(1){
        Nand_Read(source_addr, buf, 64);
        
        for(i=0; i<64; i++){
            *dst_addr = buf[i];
            dst_addr++;
            source_addr++;
            index++;
        }
        
        if(index > len)
            break;
    }
} 

完整代码

https://github.com/olichlika/...


Kyseng
1 声望3 粉丝

电子爱好者一枚,利用工作空余时间记录一下学习过程