LPC1788 nand驱动

发布者:数字冲浪最新更新时间:2017-01-13 来源: eefocus关键字:LPC1788  nand驱动 手机看文章 扫描二维码
随时随地手机看文章

Lpc

1788自带有emc接口用于驱动nandflash,norflash,sdram设备,对于nandflash驱动因为配置简单,时序也简单

         首先,针对nandflash而言应当在系统中有三个地址,分别是数据读写地址,命令读写地址以及地址设置地址,这三个地址都需要更具电路图设置,电路图如下

 

根据这张图可以看到,CLE地址线也就是命令锁存线为高的时候,地址为命令地址,ALE也就是地址锁存线为高,地址为地址锁存线,当CLE和ALE都为低但是CE选中的时候地址为数据地址,那CS1代表的数据地址是多少呢,需要根据1788的地址分配来判断

 

当CS1选中的时候系统总线在0X90000000,因为A24 A25的存在,不难分析出,0x91000000为地址锁存使能,0x92000000为命令锁存使能,(因为没有其他的地址线连接,所以,其他的地址位都设置为0),

对nandflash的操作主要有下面几种

  1. 单纯命令

对0x92000000直接赋予相应的命令,也就是

*0x92000000 = cmd,然后等待系统响应就可以了,有时候系统有响应,比如读取状态,读取ID等,有的没有响应,例如复位命令

对于有响应的命令,,直接等待芯片完成操作之后读取地址线数据就可以了,也就是,

Returnvalue = *(0x90000000)

 

  1. 写入操作

说起写入操作,就必须要说一说nand的内部数据分区,首先,nand的数据分区是以块进行的,类似下图

 

也就是说,一块nandflash的实际容量的计算是块数量*页数量*主数据区域大小+(附加数据区域大小)

 

但是在实际工程应用中,附加数据区域是不会用来存放用户数据的,主要用来存储数据的ECC效验信息以及坏块信息,部分厂商在芯片出厂的时候会进行一次芯片完整性校验,对出厂就已经存在的坏块,会在第一页和第二页的附加数据区域存放一个数据0xff(nand flash指标只要坏块不超过40就可以买卖)

 

针对我使用的nand,块数量为1024 页数量为64,主数据区域存放2048byte数据

,所以用户实际可使用的空间为128mBYTE,但是存储设备一般用bit做单位,也就是1GBIT

 

对nandflash进行读取的时候,写入一个三十二位的地址,这个地址里面包括了几个数据

  1. 要读写的块是哪一个块

  2. 要读写的页是哪一页

  3. 要读写的数据位于一页中的哪一个字节

  4. 最后合成地址

地址的低16位为数据页内地址,高16位为块和页的地址(注意,这个计算出的地址还得加上基础地址0x90000000)

                   写入的过程,nandflash一个读出命令分为两个字节,先发送第一个字节的命令,然后写入32位的地址,然后写入读取命令的第二个字节,接下来读取数据(注意,读取数据是以页为单位进行的,当读取到页尾的时候就不能在进行读取了,要从新写入地址)

 

  1. 写入操作

Nandflash有一个特性就是擦除必须以块为单位,写入必须以页为单位,同时,写入的过程中他只能将数据从0变成1,不能将数据从1编程0(只有擦除才能将数据从1变成0),所以我们变成的时候必须保证要写的这一页必须是已经擦除过,里面的数据都是0才能保证我们写入的数据被接受,若是没有擦除过的数据,写入数据之后必然会失败

写入的时候同样是32位地址,其中,页内相对地址为0,只需要块地址和页地址,先写入编程命令1,在写入32位地址,在写入编程命令2,接着写入页空间大小的数据,编程完成

 

 

以上就是nand的读些过程,当然,lpc1788还需要配置之后才能使用emc的接口,过程如下

  1. 设置相应的IO口功能,使相应的IO口对应emc

  2. 打开emc的时钟

  3. 设置几个重要的参数…总线位宽,总线有效电平,还有一些时间配置参数

片选到写使能的延迟时间

片选到数据输出使能的延时时间

片选到写入的延时

顺序读取的时候每一次读取之间的延时

设置好这些参数之后稍微延时一会,就能进行nand的操作了

代码如下


#ifndef __NANDFLASH_H_#define __NANDFLASH_H_#include "common.h"#include "debugserial.h"#include "delay.h"//nand flash 使用cs1//reday/busy  p2.21//ale p5.1//cle p5.0//we  p4.25//oe  p4.24 
//ce  p4.27//命令地址#define K9F1G_CLE   ((volatile uint8_t *)0x92000000)    //命令锁存使能地址#define K9F1G_ALE    ((volatile uint8_t *)0x91000000)    //地址锁存使能地址#define K9F1G_DATA  ((volatile uint8_t *)0x90000000)    //数据地址//nand 命令列表#define K9FXX_READ_1                0x00    //读数据第一个周期命令#define K9FXX_READ_2                0x30    //读数据第二个周期命令#define K9FXX_READ_COPYBACK_1        0x00    //读取数据用于copyback#define K9FXX_READ_COPYBACK_2        0x35    //copy back指令2#define K9FXX_READ_ID               0x90    //读取ID#define K9FXX_RESET                 0xFF    //复位#define K9FXX_BLOCK_PROGRAM_1       0x80    //页编程第一周期指令#define K9FXX_BLOCK_PROGRAM_2       0x10    //页编程第二周期地址#define K9FXX_BLOCK_ERASE_1         0x60    //块擦除第一周期命令#define K9FXX_BLOCK_ERASE_2         0xD0    //块擦除第二周期命令#define K9FXX_READ_STATUS           0x70    //读状态 70H//芯片ID#define K9FXX_ID                    0xF1009540    /* Byte 3 and 2 only *///nand状态#define K9FXX_BUSY                  (1 << 6)#define K9FXX_OK                    (1 << 0)//相关定义#define NANDFLASH_BASE_ADDR            0x00000000    //起始地址#define NANDFLASH_NUMOF_BLOCK        1024        //nand一共拥有的块数量#define NANDFLASH_RW_PAGE_SIZE        2048        // 每页2048个存储字节#define NANDFLASH_SPARE_SIZE        64             //每页64个spare的备用空间(用于页数据的ECC校验和存放)#define NANDFLASH_PAGE_PER_BLOCK    64            //一个块的中page数量#define NANDFLASH_PAGE_FSIZE        (NANDFLASH_RW_PAGE_SIZE + NANDFLASH_SPARE_SIZE)//每一页的实际大小(byte)#define NANDFLASH_BLOCK_RWSIZE    (NANDFLASH_RW_PAGE_SIZE * NANDFLASH_PAGE_PER_BLOCK)//每一页可读取的空间#define NANDFLASH_BLOCK_FSIZE    (NANDFLASH_PAGE_FSIZE * NANDFLASH_PAGE_PER_BLOCK)//实际上每一页的空间//可读取空间 = 2048(一页存储字节)*64(页数量)*1024(块数量) = 128MB//实际空间 = (2048+64)*64 * 1024 = 132 MB#define NANDFLASH_INVALIDBLOCK_CHECK_COLUMM            (2048)//支持块校验数#define NAND_WAIT_BUSY_MAX_TIME                        100            //MS 等待响应时间void nand_init(void);void nand_reset(void);

u8 nand_wait_ready(void);

u32 nand_read_id(void);

u8 nand_read_status(u32 cmd);

u8 nand_block_erase(u32 block_num);

u8 nand_page_program(u32 blocknum,u32 pagenum,u8* buffer);

u8 nand_check(void);

u8 nand_page_read(u32 pagenum,u32 blocknum,u8* buffer);

u32 nand_page_read_from_begin(u32 block,u32 page,u8* buffer);

u32 nand_page_read_from_addr(u32 blocknum,u32 pagenum,u32 addrinpage,u8* buffer,u32 readsize);

u32 nand_read_form_addr(u32 addrInWholeNand,u8* buffer,u32 size);#endif#include "nandflash.h"/*********************************************************************//**
 * 获取相应的io配置寄存器指针
 **********************************************************************/static u32 * PIN_GetPointer(u8 portnum, u8 pinnum)
{
    u32 *pPIN = NULL;
    pPIN = (u32 *)(LPC_IOCON_BASE + ((portnum * 32 + pinnum)*sizeof(u32)));    return pPIN;
}/*********************************************************************//**
 *配置IO口功能
 **********************************************************************/static void PINSEL_ConfigPin ( u8 portnum, u8 pinnum, u8 funcnum)
{
    u32 *pPIN = NULL;
    pPIN = PIN_GetPointer(portnum, pinnum);    *pPIN &= 0x00000007;//Clear function bits
    *pPIN |= funcnum;
}//初始化nand接口,主要是初始化IO口以及初始化EMC外设void nand_init()
{
    LPC_SC->SCS |= (1<<0);//emc地址不移位    //打开emc时钟与端口时钟
    LPC_SC->PCONP |= (1<<15)|(1<<11);//打开时钟
    LPC_SC->EMCDLYCTL   = 0x00001010;//延时时间初始化
    LPC_EMC->Control     = 0x00000001;//emc使能
    LPC_EMC->Config      = 0x00000000;//emc配置清零,小端模式    
   //我们选用的nand使用的外部引脚主要有   //P3.0-P3.7 P5.0 P5.1 P4.24 P4.25 P4.31 P2.21 
   //配置IO寄存器    
    //d0--d7
    PINSEL_ConfigPin(3,0,1);
    PINSEL_ConfigPin(3,1,1);
    PINSEL_ConfigPin(3,2,1);
    PINSEL_ConfigPin(3,3,1);
    PINSEL_ConfigPin(3,4,1);
    PINSEL_ConfigPin(3,5,1);
    PINSEL_ConfigPin(3,6,1);
    PINSEL_ConfigPin(3,7,1);    //cle ale
    PINSEL_ConfigPin(5,0,1);
    PINSEL_ConfigPin(5,1,1);
    
    PINSEL_ConfigPin(4,24,1);//OE
    PINSEL_ConfigPin(4,25,1);//WE
    PINSEL_ConfigPin(4,31,1);//CS1    
    //初始化忙引脚为输入模式
    PINSEL_ConfigPin(2,21,0);
    P2dir(21) = 0;

    LPC_EMC->Control = (1<<0)|(1<<1);//使能emc并复位存储器映射
    LPC_EMC->StaticConfig1 &= ~(3<<0);    //设置总线宽度八位
    LPC_EMC->StaticConfig1 |= (1<<7);    //设置读写有效电平,读为低电平
    LPC_EMC->StaticWaitWen1 &= ~(7<<0);
    LPC_EMC->StaticWaitWen1 |= (2<<0);//设置片选到写使能的延时时间
    LPC_EMC->StaticWaitOen1 &= ~(7<<0);
    LPC_EMC->StaticWaitOen1 |= (2<<0);//设置片选到输出使能的延时
    LPC_EMC->StaticWaitWr1 &= ~(0x1f<<0);
    LPC_EMC->StaticWaitWr1 |= (0x1f<<0);//设置片选到写入的延时
    LPC_EMC->StaticWaitPage1 &= ~(0x1f<<0);
    LPC_EMC->StaticWaitPage1 |= (0x1f<<0);//设置读模式顺序存取延时
    LPC_EMC->StaticWaitRd1 &= ~(0x1f<<0);
    LPC_EMC->StaticWaitRd1 |= (0x1f<<0);//设置片选到读取的延时
    LPC_EMC->StaticWaitTurn1 &= ~(0x1f<<0);
    LPC_EMC->StaticWaitTurn1 |= (0x1f<<0);//设置总线周转周期    
    DelayMs(2);
}//nand复位void nand_reset()
{    volatile u8 *pCLE;    /* Reset NAND FLASH  through NAND FLASH command */
    pCLE = K9F1G_CLE;    *pCLE = K9FXX_RESET;

    DelayMs(2);    return;
}//等待nand不忙u8 nand_wait_ready()
{
    u8 waitTime = 0;    while(P2in(21) == 1)        /* 等待他为高,说明我们的操作正在进行 */
    {
        waitTime++;
        DelayUs(1);        if(waitTime > NAND_WAIT_BUSY_MAX_TIME)            return 1;
        
    }
    waitTime = 0;    while(!(P2in(21) == 1))    /* 等待他为低,说明操作已经完成 */
    {
        waitTime++;
        DelayUs(1);        if(waitTime > NAND_WAIT_BUSY_MAX_TIME)            return 1;
        
    }    return 0;
}//读取nand idu32 nand_read_id()
{
    u8 b, c, d, e;    volatile u8 *pCLE;    volatile u8 *pALE;    volatile u8 *pDATA;

    pCLE  = K9F1G_CLE;
    pALE  = K9F1G_ALE;
    pDATA = K9F1G_DATA;    *pCLE = K9FXX_READ_ID;    *pALE = 0;

    b = *pDATA;//伪读取 无效
    b = *pDATA;
    c = *pDATA;
    d = *pDATA;
    e = *pDATA;    
    return ((b << 24) | (c << 16) | (d << 8) | e);
}//读取nand状态u8 nand_read_status(u32 cmd)
{    volatile u8 *pCLE;    volatile u8 *pDATA;
    u8 waitTime = 0;    //等待时间    u8 StatusData;

    pCLE  = K9F1G_CLE;
    pDATA = K9F1G_DATA;    *pCLE = K9FXX_READ_STATUS;    while ( (*pDATA & 0xC0) != 0xC0 )    //等待芯片ready并且无保护(失败应当有错误处理,暂时没做)    {
        waitTime++;
        DelayUs(1);        if(waitTime > NAND_WAIT_BUSY_MAX_TIME)            return 1;
        
    }
    StatusData = *pDATA;    switch (cmd)
    {        case K9FXX_BLOCK_PROGRAM_1://编程或擦除的第二个指令
        case K9FXX_BLOCK_ERASE_1:              if (StatusData & 0x01)    /* Erase/Program failure(1) or pass(0) */
                return 1;              else
                return 0;            
        case K9FXX_READ_1:                /* bit 5 and 6, Read busy(0) or ready(1) */
              return 0;        default:              break;
    }    return(1);
}//块擦除nandu8 nand_block_erase(u32 block_num)
{    volatile u8 *pCLE;    volatile u8 *pALE;
    u32 rowAddr;

    pCLE  = K9F1G_CLE;
    pALE  = K9F1G_ALE;    //计算地址
    rowAddr = (NANDFLASH_BASE_ADDR + block_num * NANDFLASH_BLOCK_FSIZE);//得到块基地址
    rowAddr = rowAddr - (rowAddr % NANDFLASH_BLOCK_FSIZE);//得到块偏移地址(页地址)

    *pCLE = K9FXX_BLOCK_ERASE_1;    *pALE = (u8)(rowAddr & 0x00FF);            /* column address low */

    *pALE = (u8)((rowAddr & 0xFF00) >> 8);    /* column address high */

    *pCLE = K9FXX_BLOCK_ERASE_2;//擦除数据
    nand_wait_ready();    return(nand_read_status(K9FXX_BLOCK_ERASE_1));
}//每次至少写入2048字节 nand指定页编程 注意,编程之前需要先擦除的u8 nand_page_program(u32 blocknum,u32 pagenum,u8* buffer)
{    volatile u8 *pCLE;    volatile u8 *pALE;    volatile u8 *pDATA;
    u32 i, curAddr, curColumm;

    pCLE  = K9F1G_CLE;
    pALE  = K9F1G_ALE;
    pDATA = K9F1G_DATA;

    curAddr = NANDFLASH_BASE_ADDR + blocknum * NANDFLASH_BLOCK_FSIZE                                + pagenum * NANDFLASH_PAGE_FSIZE;

    curColumm = curAddr % NANDFLASH_PAGE_FSIZE;
    curAddr -= curColumm;//得到具体地址

    *pCLE = K9FXX_BLOCK_PROGRAM_1;//编程命令1

    *pALE =  (u8)(curColumm & 0x000000FF);        /* column address low */

    *pALE = (u8)((curColumm & 0x00000F00) >> 8);    /* column address high */

    *pALE = (u8)((curAddr & 0x00FF0000) >> 16);    /* row address low */

    *pALE = (u8)((curAddr & 0xFF000000) >> 24);    /* row address high */

    //Not write to spare area for the NandFlash valid block checking
    for ( i = 0; i < NANDFLASH_RW_PAGE_SIZE; i++ )
    {        *pDATA = *buffer++;
    }    *pCLE = K9FXX_BLOCK_PROGRAM_2;//编程命令2
    nand_wait_ready();    return( nand_read_status( K9FXX_BLOCK_PROGRAM_1 ) );//读取编程结果}//nand读取任意地址(慎用) 这个地址包含了附加数据地址 返回实际读取数据量u32 nand_read_form_addr(u32 addrInWholeNand,u8* buffer,u32 size)
{    volatile u8 *pCLE;    volatile u8 *pALE;    volatile u8 *pDATA;
    u32 i, curColumm, curRow;

    i = 0;

    pCLE  = K9F1G_CLE;
    pALE  = K9F1G_ALE;
    pDATA = K9F1G_DATA;

    curColumm = addrInWholeNand % NANDFLASH_PAGE_FSIZE;
    curRow = addrInWholeNand - curColumm;    *pCLE = K9FXX_READ_1;    *pALE = (u8)(curColumm & 0x000000FF);            /* column address low */

    *pALE = (u8)((curColumm & 0x00000F00) >> 8);        /* column address high */

    *pALE = (u8)((curRow & 0x00FF0000) >> 16);    /* row address low */

    *pALE = (u8)((curRow & 0xFF000000) >> 24);    /* row address high */

    *pCLE = K9FXX_READ_2;

    nand_wait_ready();    for ( i = 0; i < (NANDFLASH_PAGE_FSIZE - curColumm); i++ )
    {        *buffer = *pDATA;

        buffer++;        if((i + 1) >= size)            break;
    }    return i;
}//nand读取指定页数据,数据长度不能大于2048-addrinpageu32 nand_page_read_from_addr(u32 blocknum,u32 pagenum,u32 addrinpage,u8* buffer,u32 readsize)
{
    u32 curAddr = 0;

    curAddr += NANDFLASH_BASE_ADDR + blocknum * NANDFLASH_BLOCK_FSIZE;

    curAddr += pagenum * NANDFLASH_PAGE_FSIZE;

    curAddr += addrinpage;    return (nand_read_form_addr(curAddr, buffer, readsize));
}//nand读取指定的页码全部数据,数据数量为 2048+32u32 nand_page_read_from_begin(u32 block,u32 page,u8* buffer)
{    return (nand_page_read_from_addr(block, page, 0, buffer, NANDFLASH_PAGE_FSIZE));
}//nand读取指定页吗全部数据u8 nand_page_read(u32 blocknum,u32 pagenum,u8* buffer)
{    return (nand_page_read_from_begin(blocknum,pagenum,buffer) != 0);
}//nand检查,每次检查每个bloak的前两个page,检查最后一个字节是不是0xffu8 nand_check(void)
{
    u32 invailedBlock = 0;
    u8 temp = 0;
    u16 i = 0;    for(i = 0; i < NANDFLASH_NUMOF_BLOCK;i++)
    {
        nand_page_read_from_addr(i,0,2048,&temp,1);        if(temp == 0xff)
        {
            invailedBlock++;            continue;
        }
        nand_page_read_from_addr(i,1,2048,&temp,1);        if(temp == 0xff)
        {
            invailedBlock++;
        }
    }    return (u8)invailedBlock;
}


关键字:LPC1788  nand驱动 引用地址:LPC1788 nand驱动

上一篇:lPC1788的串口通讯
下一篇:lpc1788IO口模拟IIC

推荐阅读最新更新时间:2024-03-16 15:30

LPC1788---串口设置
LPC1788学习之串口学习-寄存器操作 接收数据使用中断接收---发送暂时是直接发送--简单的寄存器操作-记下不忘记! #include uart_lpc1788.h uint32_t Receive_Sign; //接收数据下标--接收数据个数 uint8_t Receive_Buf ; //接收数组 /*-----------------------串口0初始化------------------*/ void Uart0_Init(uint32_t clk, uint32_t baudrate) { uint32_t tmp; uint16_t divisorInt; LPC_IOCON-
[单片机]
LPC1788启动代码分析
在Keil uVision4中新建一个基于NXP1788的工程后,会提示添加启动汇编代码startup_LPC177x_8x.S。该文件进行从汇编到C语言运行环境的初始化工作。 ;/***************************************************************************** ; * @file: startup_LPC177x_8x.s ; * @purpose: CMSIS Cortex-M3 Core Device Startup File ; * for the NXP LPC177x_8x Device Series ; * @version:
[单片机]
<font color='red'>LPC1788</font>启动代码分析
恩智浦推出LPC1788微控制器
   恩智浦半导体NXP Semiconductors N.V.近日发布了LPC1788微控制器,这是业界首款采用ARM® Cortex™-M3技术且集成LCD控制器的MCU,目前已批量上市。LPC178x系列拥有最高96KB片上SRAM以及32位外接存储器接口,帮助客户轻松实现低成本、高质量的图像应用。LPC178x系列支持众多图像显示面板,是工业自动化、销售网点和医疗诊断应用的理想选择。   恩智浦微控制器产品线市场总监Jan Jaap Bezemer表示:“人机接口技术的最新发展正引入更多的LCD显示技术到 工业、零售和医疗领域。集成LCD控制器的LPC178x Cortex-M3微控制器极具价格竞争性,为嵌入式系统设计人员提
[工业控制]
Cortex-M3 (NXP LPC1788)之EEPROM存储器
EEPROM是一种非易失性存储器,主要用于存储相对少量的数据,如存储一些系统的配置信息。通过系统的EEPROM控制模块可以轻松的进行EERPOM的存储控制。 要正确使用EPPROM需要配置掉电寄存器EEPWRDWN确定EEPROM的工作模式,配置EEPROM时钟分频器寄存器,使EPPROM工作在375KHZ。下面对EPPROM的读和写数据进行介绍。 EEPROM存储器的访问有三种操作方式:读、写、擦除/编程。对EPPROM中写数据分成两个单独的操作:写和擦除/编程。第一步写操作并不是真正把数据写入EPPROM的存储介质中,而只是更新被称作 页寄存器 的临时数据寄存器。只有执行下一步 擦除/编程 操作才会真正
[单片机]
Cortex-M3 (NXP <font color='red'>LPC1788</font>)之EEPROM存储器
LPC1788实现10ms单位的计数器
利用LPC1788的定时器0实现10ms单位的计数器,即每10ms计数增加1. 初始化定时器0 init_timer(1,599990); //10ms为单位 599990=10(60000000/1000-1),LPC1788工作在120MHZ,经PLL分频后的外设时钟PeripheralClock为60MHZ 函数原型: uint32_t init_timer ( uint8_t timer_num, uint32_t TimerInterval ) { timer0_counter = 0; LPC_SC- PCONP |= (0x01 1); LPC_TIM0- MR0 = TimerInter
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved