1. 硬件原理
1.1. Nor Flash简介
NOR Flash是Intel在1988年推出的一款商业性闪存芯片,它需要很长的时间进行抹写,大半生它能够提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域,而且它可以忍受一万次到一百万次擦写,是早期的闪存媒体的基础。
1.2. 处理器连接
从处理器的角度来看,每个地址对应的是一个BYTE的数据单元。而Nor Flash的每个地址有可能对应的是一个BYTE的数据单元,也有可能对应的是一个HALF-WORD的数据单元。所以在硬件设计中,连接ARM处理器和Nor Flash时,必须根据实际情况对地址信号做特别的处理。如果ARM处理器外部扩展的是8-BIT的NOR Flash,数据线和地址线的连接应该如图1所示。从图中我们可以看到,处理器的数据信号D0-D7和Flash的数据信号D0-D7是一一对应连接的,处理器的地址信号A0-An和NOR Flash的地址信号A0-An也是一一对应连接的。
如果ARM处理器外部扩展的是16-BIT的Nor Flash,数据线必须要错位连接。图2给了一个ARM处理器和16-BIT NOR Flash的连接示意图。如图2所示,ARM处理器的数据信号D0-D15和Flash的数据信号D0-D15是一一对应的。而ARM处理器的地址信号和Nor Flash的地址信号是错位连接的,ARM的A0悬空,ARM的A1连接Flash的A0,ARM的A2连接Flash的A1,依次类推。需要错位连接的原因是:ARM处理器的每个地址对应的是一个BYTE的数据单元,而16-BIT的Flash的每个地址对应的是一个HALF-WORD(16-BIT)的数据单元。为了保持匹配,所以必须错位连接。这样,从ARM处理器发送出来的地址信号的最低位A0对16-BIT Flash来说就被屏蔽掉了。
补充说明:
1、一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的Flash的位宽(8-BIT/16-BIT/32-BIT)。这样,处理器才知道在访问的时候如何从Flash正确的读取数据。
2、有些ARM处理器内部可以设置地址的错位。对于支持软件选择地址错位的处理器,在连接16-BIT Flash的时候,硬件上可以不需要把地址线错位。
3、如果处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的。
2. 芯片手册
2.1. 特性
容量2MB
扇区分布
一个16K,两个8K,一个32K,31个64K(Boottom Boot Device
2.2. 引脚描述
2.3. 逻辑图
2.4. 设备总线操作
字节还是半字选择,BYET#引脚高,半字;低,字节。
开机或复位自动进入读状态。
写命令序列,绕过解锁模式Unlock Bypass只需要2个周期,而不是4个周期
编程和擦除需要轮询状态位。
自动选择模式:读ID,扇区组保护,硅扇区。两种方法进入自动选择模式,编程器电压方式,写命令方式。
2.5. CFI
2.6. 指令集
见数据手册
2.7. 读
Nor Flash上电后处于数据读取状态(Reading Array Data)。此状态可以进行正常的读,这和读取SDRAM/SRAM/ROM一样。(要是不一样的话,芯片上电后如何从Nor Flash中读取启动代码)。
2.8. 读ID
一般再对Flash进行操作前都要读取芯片信息比如设备ID号。这样做的主要目的是为了判断自己写的程序是否支持该设备。Nor Flash支持2种方式获取ID号:一种是编程器所用的方法需要高电压(8.5V-12.5V);另一种方法就是所谓的in-system方法,就是在系统中通过Nor Flash的命令寄存器来完成。本文中只对in-system方法进行说明,此时需要切换到自动选择(Autoselect Command),这要通过发送命令来完成。注意:进入自动选择(Autoselect Command)模式后需要发送复位命令才能回到数据读取状态(Reading Array Data)。
2.9. 擦除
在完成信息获取后一般就要擦除数据。Nor Flash支持扇区擦除(Sector Erase)和整片擦除(Chip Erase),这2种模式都有对应的命令序列,在完成擦除命令后会自动返回到数据读取(Reading Array Data)状态,在返回前可查询编程的状态。
2.10. 编程
完成擦除后就需要对芯片进行写入操作也就是编程,这就需要进入编程(Program)状态。在完成编程命令后会自动返回到数据读取(Reading Array Data)状态,在返回前可查询编程的状态,注意:编程前一定要先擦除.因为编程只能将'1'改写为'0',通过擦写可以将数据全部擦写为'1'。
2.11. 等待操作
Nor Flash提供几个数据位来确定一个写操作的状态,它们分别是:DQ2、DQ3、DQ5、DQ6、DQ7、and RY/BY#。如上图所示。其中DQ7,RY/BY#引脚,和DQ6中的每一个都提供了一种方法来判断一个编程或者擦除操作是否已经完成或正在进行中。实际编程中只需要使用其中的一种。
DQ7:Data# Polling bit,DQ7在编程时的状态变化。在编程过程中从正在编程的地址中读出的数据的DQ7为要写入数据的补码。比如写入的数据为0x0000,及输入的DQ7为'0',则在编程中读出的数据为'1';当编程完成时读出的数据又变回输入的数据即'0'。在擦除过程中DQ7输出为'0';擦除完成后输出为'1';注意读取的地址必须是擦除范围内的地址。
RY/BY#:高电平表示'就绪',低电平表示'忙'。
DQ6:轮转位1(Toggle Bit 1)。
在编程和擦除期间,读任意地址都会导致DQ6的轮转(0,1间相互变换)当操作完成后,DQ6停止转换。
DQ2:轮转位2(Toggle Bit 2)。当某个扇区被选中擦除时,读有效地址(地址都在擦除的扇区范围内)会导致DQ2的轮转。
注意:DQ2只能判断一个特定的扇区是否被选中擦除。但不能区分这个快是否正在擦除中或者正处于擦除暂停状态。相比之下,DQ6可以区分Nor Flash是否处于擦除中或者擦除状态,但不能区分哪个快被选中擦除。因此需要这2个位来确定扇区和模式状态信息。
DQ5:超时位(Exceeded Timing Limits),当编程或擦除操作超过了一个特定内部脉冲计数是DQ5=1,这表明操作失败。当编程时把'0'改为'1'就会导致DQ5=1,因为只有擦除擦做才能把'0'改为'1'。当错误发生后需要执行复位命令(见图1-1)才能返回到读数据状态。
DQ3:(扇区擦除计时位)Sector Erase Timer,只在扇区擦除指令时起作用。当擦除指令真正开始工作是DQ3=1,此时输入的命令(除擦除暂停命令外)都被忽略。DQ3=0,是可以添加附加的扇区用于多扇区擦除。
以上讲了这些状态为,实际只需要使用几个就行,比较简单的就是选择DQ5,DQ6/DQ2。
3. mini2440电路图
4. S3C2440寄存器
/*******************************************************************
* Copyright (C),2011-2012, XXX.
* FileName: nand.h
* Author:HuangYinqing
* Version:1.0
* Date::2012-04-22
* Description:nor flash驱动.
* Function List:
* History:
******************************************************************/
#ifndef __NOR_H__
#define __NOR_H__
/*nor flash调试等级*/
#define DBG_NOR_LEVEL 1
#define NOR_TYPE_S29AL016J 0x00012249
/*nor flash信息*/
#define NOR_MAIN_SECT_SIZE (64*1024) //==1页2k
#define NOR_SIZE (2*1024*1024) //==容量256M
#define NOR_FLASH_BASE 0x00000000 //==nor基地址0
/*命令地址*/
#define NOR_CMD_ADDR1 (*(volatile unsigned short *)(NOR_FLASH_BASE + (0x00000555 << 1)))
#define NOR_CMD_ADDR2 (*(volatile unsigned short *)(NOR_FLASH_BASE + (0x000002AA << 1)))
/*操作命令*/
#define NOR_CMD_UNLOCK1 0x000000AA
#define NOR_CMD_UNLOCK2 0x00000055
#define NOR_CMD_RESET 0xF0
#define NOR_CMD_AUTOSELECT 0x90
#define NOR_CMD_PROGRAM 0xA0
#define NOR_CMD_ERASE_SETUP 0x80
#define NOR_CMD_CHIP_ERASE 0x30
#define NOR_CMD_SECTOR_ERASE 0x10
/*函数*/
void NorTest(void);
void NorInit(void);
#endif
nor.c
/*******************************************************************
* Copyright (C),2011-2012, XXX.
* FileName: nor.h
* Author:HuangYinqing
* Version:1.0
* Date::2012-04-22
* Description:nor flashS29AL160J-70TFI02驱动.
* Function List:
* History:
******************************************************************/
#include "common.h"
#include "core.h"
#include "nor.h"
/********************************************************************
函数功能:nor flash复位。
入口参数:无。
返 回:无。
备 注:无。
********************************************************************/
void NorReset(void)
{
*( (volatile U16*)0x0 ) = NOR_CMD_RESET;
}
/********************************************************************
函数功能:nor flash读ID。
入口参数:无。
返 回:设备ID。
备 注:无。
********************************************************************/
unsigned long NorReadID(unsigned long *pucIDBuffer)
{
U32 ulFlashID = 0x0;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
NOR_CMD_ADDR1 = NOR_CMD_AUTOSELECT;
ulFlashID = (*(volatile unsigned short *)(NOR_FLASH_BASE+ (0x100<<1))) << 16;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
NOR_CMD_ADDR1 = NOR_CMD_AUTOSELECT;
ulFlashID |= (*(volatile unsigned short *)(NOR_FLASH_BASE+ (0x101<<1)));
*pucIDBuffer = ulFlashID;
// *pucIDBuffer = (*(volatile unsigned short *)(NOR_FLASH_BASE+ (0x0<<1))) << 16;
// *pucIDBuffer |= (*(volatile unsigned short *)(NOR_FLASH_BASE+ (0x1<<1)));
return ERR_SUCCESS;
}
/********************************************************************
函数功能:读2个字节。
入口参数:
ulAddr:地址
pucBuffer:缓冲区
返 回:成功:读取字节数
备 注:无。
********************************************************************/
U32 NorReadByte(U32 ulAddr, U16 *punData)
{
*punData = *( (volatile U16*)(ulAddr) );
return ERR_SUCCESS;
}
/********************************************************************
函数功能:写nor flash一页数据。(大页)
入口参数:
ulPageNum:页索引
pucBuffer:缓冲区
返 回:成功:读取字节数
备 注:无。
********************************************************************/
U32 NorWriteByte(U32 ulAddr, U16 unData )
{
unsigned short unResult;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
NOR_CMD_ADDR1 = NOR_CMD_PROGRAM;
*( (volatile U16*)(ulAddr) ) = unData;
do
{
udelay(1);
unResult = *(unsigned short *)ulAddr;
if( (unResult & 0x80) == (unData & 0x80) )
{
break;
}
}while (1);
return ERR_SUCCESS;
}
/********************************************************************
函数功能:擦除扇区。
入口参数:
ulBlock:块索引
返 回:成功:0;其他:出错。
备 注:无。
********************************************************************/
int NorEraseSector(U32 ulSectAddr)
{
unsigned short unResult;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
NOR_CMD_ADDR1 = NOR_CMD_ERASE_SETUP;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
*( (volatile U16*)(ulSectAddr) ) = NOR_CMD_SECTOR_ERASE;
do
{
mdelay(5);
unResult = *(unsigned short *)ulSectAddr;
if( unResult & 0x80)
{
break;
}
}while (1);
return ERR_SUCCESS;
}
/********************************************************************
函数功能:擦除整块芯片。
入口参数:
ulBlock:块索引
返 回:成功:0;其他:出错。
备 注:无。
********************************************************************/
int NorEraseChip(void)
{
unsigned short unResult;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
NOR_CMD_ADDR1 = NOR_CMD_ERASE_SETUP;
NOR_CMD_ADDR1 = NOR_CMD_UNLOCK1;
NOR_CMD_ADDR2 = NOR_CMD_UNLOCK2;
NOR_CMD_ADDR1 = NOR_CMD_CHIP_ERASE;
do
{
mdelay(5);
unResult = *( (volatile U16*)(0x0) );
if( unResult & 0x80)
{
break;
}
}while (1);
return ERR_SUCCESS;
}
/********************************************************************
函数功能:nor flash初始化。
入口参数:无。
返 回:无。
备 注:无。
********************************************************************/
void NorInit(void)
{
unsigned long ulFlashID;
DbgPrintX( DBG_NOR_LEVEL, "\rnor flash Init\n");
NorReadID( &ulFlashID );
DbgPrintX( DBG_NOR_LEVEL, "\rnor flash ID=%x\n", ulFlashID);
NorReset();
udelay(100);
}
/********************************************************************
函数功能:测试nor flash。
入口参数:无。
返 回:无。
备 注:无。
********************************************************************/
void NorTest(void)
{
int ret;
unsigned char data[4][1024];
memset(data, 0x86, 4*1024);
NorEraseChip();
#if 0
ret = NorErase(2);
if(ret != ERR_SUCCESS)
{
DbgPrintX( DBG_NAND_LEVEL, "\rnor erase err=%2x\n",ret);
return;
}
ret = NorWrite( 0x50000, 4*1024, (unsigned char *)data );
if(ret != ERR_SUCCESS)
{
DbgPrintX( DBG_NAND_LEVEL, "\rnor write err=%2x\n",ret);
return;
}
ret = NorRead(0x50000, 4*1024, (unsigned char *)0x31000000);
if(ret != ERR_SUCCESS)
{
DbgPrintX( DBG_NAND_LEVEL, "\rnor read err=%2x\n",ret);
return;
}
#endif
}