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的操作主要有下面几种
- 单纯命令
对0x92000000直接赋予相应的命令,也就是
*0x92000000 = cmd,然后等待系统响应就可以了,有时候系统有响应,比如读取状态,读取ID等,有的没有响应,例如复位命令
对于有响应的命令,,直接等待芯片完成操作之后读取地址线数据就可以了,也就是,
Returnvalue = *(0x90000000)
- 写入操作
说起写入操作,就必须要说一说nand的内部数据分区,首先,nand的数据分区是以块进行的,类似下图
也就是说,一块nandflash的实际容量的计算是块数量*页数量*主数据区域大小+(附加数据区域大小)
但是在实际工程应用中,附加数据区域是不会用来存放用户数据的,主要用来存储数据的ECC效验信息以及坏块信息,部分厂商在芯片出厂的时候会进行一次芯片完整性校验,对出厂就已经存在的坏块,会在第一页和第二页的附加数据区域存放一个数据0xff(nand flash指标只要坏块不超过40就可以买卖)
针对我使用的nand,块数量为1024 页数量为64,主数据区域存放2048byte数据
,所以用户实际可使用的空间为128mBYTE,但是存储设备一般用bit做单位,也就是1GBIT
对nandflash进行读取的时候,写入一个三十二位的地址,这个地址里面包括了几个数据
- 要读写的块是哪一个块
- 要读写的页是哪一页
- 要读写的数据位于一页中的哪一个字节
- 最后合成地址
地址的低16位为数据页内地址,高16位为块和页的地址(注意,这个计算出的地址还得加上基础地址0x90000000)
写入的过程,nandflash一个读出命令分为两个字节,先发送第一个字节的命令,然后写入32位的地址,然后写入读取命令的第二个字节,接下来读取数据(注意,读取数据是以页为单位进行的,当读取到页尾的时候就不能在进行读取了,要从新写入地址)
- 写入操作
Nandflash有一个特性就是擦除必须以块为单位,写入必须以页为单位,同时,写入的过程中他只能将数据从0变成1,不能将数据从1编程0(只有擦除才能将数据从1变成0),所以我们变成的时候必须保证要写的这一页必须是已经擦除过,里面的数据都是0才能保证我们写入的数据被接受,若是没有擦除过的数据,写入数据之后必然会失败
写入的时候同样是32位地址,其中,页内相对地址为0,只需要块地址和页地址,先写入编程命令1,在写入32位地址,在写入编程命令2,接着写入页空间大小的数据,编程完成
以上就是nand的读些过程,当然,lpc1788还需要配置之后才能使用emc的接口,过程如下
- 设置相应的IO口功能,使相应的IO口对应emc
- 打开emc的时钟
- 设置几个重要的参数…总线位宽,总线有效电平,还有一些时间配置参数
片选到写使能的延迟时间
片选到数据输出使能的延时时间
片选到写入的延时
顺序读取的时候每一次读取之间的延时
设置好这些参数之后稍微延时一会,就能进行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 * + 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 |= (<<);//emc地址不移位
//打开emc时钟与端口时钟
LPC_SC->PCONP |= (<<)|(<<);//打开时钟
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(,,);
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,);
//cle ale
PINSEL_ConfigPin(,,);
PINSEL_ConfigPin(,,); PINSEL_ConfigPin(,,);//OE
PINSEL_ConfigPin(,,);//WE
PINSEL_ConfigPin(,,);//CS1 //初始化忙引脚为输入模式
PINSEL_ConfigPin(,,);
P2dir() = ; LPC_EMC->Control = (<<)|(<<);//使能emc并复位存储器映射
LPC_EMC->StaticConfig1 &= ~(<<); //设置总线宽度八位
LPC_EMC->StaticConfig1 |= (<<); //设置读写有效电平,读为低电平
LPC_EMC->StaticWaitWen1 &= ~(<<);
LPC_EMC->StaticWaitWen1 |= (<<);//设置片选到写使能的延时时间
LPC_EMC->StaticWaitOen1 &= ~(<<);
LPC_EMC->StaticWaitOen1 |= (<<);//设置片选到输出使能的延时
LPC_EMC->StaticWaitWr1 &= ~(0x1f<<);
LPC_EMC->StaticWaitWr1 |= (0x1f<<);//设置片选到写入的延时
LPC_EMC->StaticWaitPage1 &= ~(0x1f<<);
LPC_EMC->StaticWaitPage1 |= (0x1f<<);//设置读模式顺序存取延时
LPC_EMC->StaticWaitRd1 &= ~(0x1f<<);
LPC_EMC->StaticWaitRd1 |= (0x1f<<);//设置片选到读取的延时
LPC_EMC->StaticWaitTurn1 &= ~(0x1f<<);
LPC_EMC->StaticWaitTurn1 |= (0x1f<<);//设置总线周转周期 DelayMs();
} //nand复位
void nand_reset()
{
volatile u8 *pCLE; /* Reset NAND FLASH through NAND FLASH command */
pCLE = K9F1G_CLE;
*pCLE = K9FXX_RESET; DelayMs();
return;
} //等待nand不忙
u8 nand_wait_ready()
{
u8 waitTime = ;
while(P2in() == ) /* 等待他为高,说明我们的操作正在进行 */
{
waitTime++;
DelayUs();
if(waitTime > NAND_WAIT_BUSY_MAX_TIME)
return ; }
waitTime = ;
while(!(P2in() == )) /* 等待他为低,说明操作已经完成 */
{
waitTime++;
DelayUs();
if(waitTime > NAND_WAIT_BUSY_MAX_TIME)
return ; }
return ;
} //读取nand id
u32 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 = ; b = *pDATA;//伪读取 无效
b = *pDATA;
c = *pDATA;
d = *pDATA;
e = *pDATA; return ((b << ) | (c << ) | (d << ) | e);
} //读取nand状态
u8 nand_read_status(u32 cmd)
{
volatile u8 *pCLE;
volatile u8 *pDATA;
u8 waitTime = ; //等待时间
u8 StatusData; pCLE = K9F1G_CLE;
pDATA = K9F1G_DATA; *pCLE = K9FXX_READ_STATUS; while ( (*pDATA & 0xC0) != 0xC0 ) //等待芯片ready并且无保护(失败应当有错误处理,暂时没做)
{
waitTime++;
DelayUs();
if(waitTime > NAND_WAIT_BUSY_MAX_TIME)
return ; }
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 ;
else
return ; case K9FXX_READ_1: /* bit 5 and 6, Read busy(0) or ready(1) */
return ;
default:
break;
} return();
} //块擦除nand
u8 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) >> ); /* 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) >> ); /* column address high */ *pALE = (u8)((curAddr & 0x00FF0000) >> ); /* row address low */ *pALE = (u8)((curAddr & 0xFF000000) >> ); /* row address high */ //Not write to spare area for the NandFlash valid block checking
for ( i = ; 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 = ; 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) >> ); /* column address high */ *pALE = (u8)((curRow & 0x00FF0000) >> ); /* row address low */ *pALE = (u8)((curRow & 0xFF000000) >> ); /* row address high */ *pCLE = K9FXX_READ_2; nand_wait_ready(); for ( i = ; i < (NANDFLASH_PAGE_FSIZE - curColumm); i++ )
{
*buffer = *pDATA; buffer++;
if((i + ) >= size)
break;
}
return i;
} //nand读取指定页数据,数据长度不能大于2048-addrinpage
u32 nand_page_read_from_addr(u32 blocknum,u32 pagenum,u32 addrinpage,u8* buffer,u32 readsize)
{
u32 curAddr = ; 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+32
u32 nand_page_read_from_begin(u32 block,u32 page,u8* buffer)
{
return (nand_page_read_from_addr(block, page, , buffer, NANDFLASH_PAGE_FSIZE));
} //nand读取指定页吗全部数据
u8 nand_page_read(u32 blocknum,u32 pagenum,u8* buffer)
{
return (nand_page_read_from_begin(blocknum,pagenum,buffer) != );
} //nand检查,每次检查每个bloak的前两个page,检查最后一个字节是不是0xff
u8 nand_check(void)
{
u32 invailedBlock = ;
u8 temp = ;
u16 i = ;
for(i = ; i < NANDFLASH_NUMOF_BLOCK;i++)
{
nand_page_read_from_addr(i,,,&temp,);
if(temp == 0xff)
{
invailedBlock++;
continue;
}
nand_page_read_from_addr(i,,,&temp,);
if(temp == 0xff)
{
invailedBlock++;
}
}
return (u8)invailedBlock;
}
LPC1788 nand驱动的更多相关文章
- 基于MTD的NAND驱动开发、K9F1G08 2K page、Yaffs2 Files System
转载:http://hi.baidu.com/cui1206/item/1d4119e376132513585dd886 基于MTD的NAND驱动(linux-2.6.22.10内核),目前已可以在该 ...
- MTD中的nand驱动初步分析---面向u-boot
之前提到nand驱动的初始化分析,有一个结构体 struct mtd_info始终贯穿这些代码 再来分析一下这个结构体的基本功能,如何初始化,如何使用 一.分析过程 看看结构体的出现和使用方式 第一次 ...
- u-boot的nand驱动写过程分析
从命令说起,在u-boot输入下列命令: nand write 40008000 0 20000 命令的意思是将内存0x40008000开始的部分写入nand,从nand地址0开始写,写入长度是0x2 ...
- MTD下的Nand驱动
目录 MTD下的Nand驱动 引入 平台设备资源文件 关键数据结构 平台框架 s3c24xx_nand_probe nand_scan s3c2410_nand_add_partition add_m ...
- NAND驱动
NAND FLASH是一个存储芯片 那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A" 问1. 原理图上NAND FLASH和S3C2440之间只有数据线, ...
- nand驱动移植
首先下载nand flash驱动 s3c_nand.c ,此文件包含着nand flash驱动具体的实现,将其复制到drivers/mtd/nand下: s3c_nand.c 下载地址 s3c_nan ...
- Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)
1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...
- 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)
1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...
- 移植nand驱动补缺:make mrproper与make clean以及make distclean,find/grep. makefile
make mrproper与make clean以及make distclean的区别: linux内核源码根目录下面的makefile中有很清晰的解析: useage: “clean”:Remove ...
随机推荐
- Top 100 Best Blogs for iOS Developers
(by JP Zhang | Last updated: Apr 5, 2016 ) 转载自:http://www.softwarehow.com/best-blogs-for-ios-develo ...
- 关于在jsp中的表达式
列子: <%List<F_dd_tourist_info_markup> tourists = (List<F_dd_tourist_info_markup>) requ ...
- AutoTile 自动拼接(四) 学习与实践
今天主要来说下,数据绑定. 之前第一章,我说到 把 资源图 画成格子,你们应该还有印象吧. 那么,当我 知道 格子数据,能否拿到 资源对应的图片呢? 大家先复习一下 第一章,发现很多格子数据 是相同的 ...
- 编译Android各种错误
第一次编译成功,第二次出现Value for 'keystore' is not valid. It must resolve to a single path 打开proj.android\ant. ...
- Python IDLE中实现清屏
首先下载clearwindow.py(点击可直接下载,不能下载的可以右键保存,格式为py结尾)将这个文件放在Python X\Lib\idlelib目录下(X为你的python版本),然后在这个目录下 ...
- for循环与foreach
注意点: for循环时,可以对集合进行操作,但foreach循环中,对集合进行操作会报错: for 循环可以获取下标 如下代码: package com.tt;imp ...
- 【转载】区间DP
http://www.cnblogs.com/zsboy/archive/2013/03/08/2950261.html 博客园 首页 新随笔 联系 订阅 管理 定义区间DP 区间动态规划问题一般 ...
- GB2312转unicode程序(转)
GB2312转unicode程序 #ifndef UNICODE_H #define UNICODE_H #include <string.h> #ifdef __DEFLINUX_ ...
- Web爬去的C#请求发送
public class HttpControler { //post请求发送 private Encoding m_Encoding = Encoding.GetEncoding("gb2 ...
- PAT (Advanced Level) 1040. Longest Symmetric String (25)
暴力. #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ]; ...