【韦东山旧1期学习笔记】10.S3C2440 Nor Flash实验(二)

Flash的两种接口

Flash存储器接口有两个标准:CFI和JEDEC。下面我们简要介绍一下它们。

JEDEC接口

        JEDEC(Joint Electron Device Engineering Council)即电子元件工业联合会。JEDEC是由生产厂商们制定的国际性协议,主要为计算机内存制定。工业标准的内存通常指的是符合JEDEC标准的一组内存。
        对于Flash来说,JEDEC用来帮助程序读取Flash的制造商ID和设备ID。之后根据这两个ID在linux内核的drivers/mtd/chips/jedec_probe.c文件的jedec_table数组中查找,如果数组中没有这款芯片的描述,则除非我们修改内核源码,手动添加该器件的各种参数,否则就无法使用该款Flash芯片。可见JEDEC接口非常的不方便。
在这里插入图片描述
在这里插入图片描述
        虽然Flash Memory应用越来越广泛,但由于生产Flash Memory的半导体制造商众多,不同厂商Flash Memory产品的操作命令集和电气参数又千差万别,这给Flash Memory的开发设计人员和OEM制造商带来许多不便。为了对现有的Flash Memory的产品进行升级或使用其它公司的Flash Memory产品替换,必须对原有的程序代码和硬件结构进行修改。

CFI接口

        为解决上述原因所引发的问题,迫切需要Flash Memory制造商提出一个公共的标准解决方案,在这样的背景下,公共闪存接口(Common Flash Interface),简称CFI 诞生了,CFI是一个公开的标准的从Flash Memory器件中读取数据的接口。它可以使系统软件查询已安装的Flash Memory器件的各种参数,包括器件阵列结构参数、电气和时间参数以及器件支持的功能等。利用CFI可以不用修改系统软件就可以用新型的和改进的产品代替旧版本的产品。例如:如果新型的Flash Memory的擦除时间只有旧版本的一半,系统软件只要通过CFI读取新器件的擦除时间等参数,修改一下定时器的时间参数即可。
当然,如果芯片不支持CFI接口,就就只能使用JEDEC接口了。

S3C2440的Nor Flash

在这里插入图片描述
我的开发板使用的Nor Flash芯片为MX29LV160DBTI-70G。这是一款16位的Nor Flash芯片,容量为2MB。
在这里插入图片描述
由外接电路可以看到,正因为这是一片16bit的芯片,所以我们将LADDR0悬空,LADDR1接到了Nor Flash的A0引脚上。

启动方式

特别需要注意,如果选择nand方式启动,那么BANK0对应的就是SRAM,这样就无法访问到Nor Flash芯片了。所以我们必须将程序烧写到Nor Flash芯片上,并以Nor方式启动,才可以进行Nor Flash实验。

Nor Flash 操作实验

初始化时序

由上面的电路原理图可知Nor Flash的片选信号接的是nGCS0,即对应BANK0,所以当选用非Nand方式启动时,BANK0的地址空间对应的就是Nor Flash。

BWSCON寄存器

首先BWSCON 总线宽度和等待状态控制寄存器的BANK0相关的位是由硬件OM[1:0]来决定的。选择Nor方式启动时,OM[1:0]是0b01,表示BANK0对应的芯片位宽是16位。
在这里插入图片描述
需要说明的是,在使用完JEDEC命令之后,需要发出reset命令,使得芯片恢复到正常模式,否则之后读出Flash上存储的数据会失败。

S3C2440 NorFlash读写时序

在这里插入图片描述

BANKCON0寄存器

在这里插入图片描述
所以我们需要设置的就只有BANK控制寄存器BANKCON0了。
根据上一篇博客(【韦东山旧1期学习笔记】10.S3C2440 Nor Flash实验(一))的Nor Flash时序设置实验,我们对BANKCON0进行如下设置:

BANKCON0 = (0x5 << 8);

MX29LV160DBTI Nor Flash命令操作表

在这里插入图片描述

命令地址必须偏移

上面我们得知S3C2440A的LADDR1要与Nor Flash芯片的A0连接,所以如果想让Nor Flash看到地址0x555,那么我们就必须左移一位后访问,即发送Nor命令时需要将命令地址左移1位。此时要注意的是NorFlash片内地址0x555对应S3C2440A的地址为(0x555 << 1)。故发送命令代码如下所示:

void nor_jedec_cmd(unsigned int norAddr, unsigned short val) {
	//由于Nor的A0接的是SoC的LADDR1
	//所以需要将SoC发出的地址 偏移1位才能使Nor Flash看到
	volatile unsigned short* ptr = (volatile unsigned short*)(norAddr << 1);
	*ptr = val;
}

数据地址不能偏移

有些人始终都没有弄明白为什么对于命令地址需要偏移,但是对于数据地址却不能偏移。想要弄清这个问题,需要转换视角。
在这里插入图片描述

  • 对于命令地址,需要站在Nor Flash芯片的角度来看待。Nor Flash芯片需要在其地址引脚上感知到0x555这个命令地址,它才能通过数据引脚DQ[15:0]来正确解析该命令。此时的地址是Nor Flash认定的地址,而不是CPU认定的地址,所以对于CPU来说此时需要左移1位;
  • 对于数据地址,比如当我们要擦除扇区时指定的扇区地址,或者是写入数据是指定的数据地址,此时的这个地址是我们CPU认为的地址,当CPU希望读取从0开始的第三字节的数据时,CPU发出的地址是0x2;由于MX29LV160DBTI是一款16bit的芯片,所以对于Nor Flash芯片来说,从0开始的第三字节的数据的地址是0x1(因为Nor Flash芯片0x0地址上存储了第一、二两字节的数据,同理,0x1地址上存储了第三、四字节的数据)。所以此时我们不能偏移数据地址。

理解了上面两点内容,下面的Nor Flash编程就迎刃而解了。还要注意的一点是因为这一款Nor Flash芯片是16bit的,所以它是以16bit为最小单位进行读写操作的。

读取ID和Nor Flash数据

我们首先通过JEDEC接口来读取Nor Flash的ID,同时由于Nor读取的时候可以像内存一样访问,所以顺手实现了nor_dump函数。如下所示:
nor.c

void nor_jedec_cmd(unsigned int norAddr, unsigned short val) {
	//由于Nor的A0接的是SoC的LADDR1
	//所以需要将SoC发出的地址 偏移1位才能使Nor Flash看到
	volatile unsigned short* ptr = (volatile unsigned short*)(norAddr << 1);
	*ptr = val;
}

void nor_jedec_reset(void) {
	nor_jedec_cmd(0, 0xF0);
}

unsigned short nor_jedec_readJedecData(unsigned int norAddr){
	volatile unsigned short* ptr = (volatile unsigned short*)(norAddr << 1);
	return *ptr;
}

unsigned short nor_jedec_readManifactureID(void) {
	unsigned short manifID = 0;
	nor_jedec_reset();
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	nor_jedec_cmd(0x555, 0x90);
	manifID = nor_jedec_readJedecData(0x100);
	//在使用完JEDEC命令之后,需要发出reset命令
	//使得芯片恢复到正常模式,否则之后读出Flash上存储的数据会失败
	nor_jedec_reset();
	return manifID;
}

unsigned short nor_jedec_readDeviceID(void) {
	unsigned short devID = 0;
	nor_jedec_reset();
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	nor_jedec_cmd(0x555, 0x90);
	devID = nor_jedec_readJedecData(0x101);
	//在使用完JEDEC命令之后,需要发出reset命令
	//使得芯片恢复到正常模式,否则之后读出Flash上存储的数据会失败
	nor_jedec_reset();
	return devID;
}

void nor_jedec_readID(void) {
	unsigned short manifID = nor_jedec_readManifactureID();
	unsigned short devID = nor_jedec_readDeviceID();
	printf("Nor Flash ID: %x-%x\n\r", manifID, devID);
	//在使用完JEDEC命令之后,需要发出reset命令
	//使得芯片恢复到正常模式,否则之后读出Flash上存储的数据会失败
	nor_jedec_reset();
}


//显示从nor芯片中addrInNor地址开始的len字节的数据
void nor_jedec_dump(unsigned char* addrInNor, unsigned int len) {
	volatile unsigned char* norDataPtr = (volatile unsigned char*)addrInNor;
	//Nor Flash芯片MX29LV160DBTI,容量为2MB
	if(len >= (0x1 << 21)) {
		printf("length is too big.Nothing is done.\n\r");
		return;
	}

	unsigned int idx = 0;
	unsigned int lineIdx = 0;
	for(idx = 0; idx < len;) {
//每一行显示的字节个数
#define BYTES_PER_LINE 16
		//打印本行对应的nor flash数据地址
		printf("%08x\t", norDataPtr + idx);
		for(lineIdx = 0; (lineIdx < BYTES_PER_LINE) && (idx + lineIdx < len) ; lineIdx++){
			printf("%02x ", norDataPtr[idx + lineIdx]);
		}
		//该行的字符数不足BYTES_PER_LINE个,用空格补齐
		while(lineIdx < BYTES_PER_LINE) {
			printf("%2c ", ' ');
			lineIdx++;
		}
		printf("\t|");
		char curCh = 0;
		for(lineIdx = 0; (lineIdx < BYTES_PER_LINE) && (idx + lineIdx < len) ; lineIdx++){
			curCh = norDataPtr[idx + lineIdx];
			//如果是可打印字符,则打印出来
			//如果不可打印,则使用.代替
			//Ascii码的取值范围是[0,127],十进制,闭区间
			//其中,[32,126]是可打印字符,其余是不可打印字符
			if(curCh >= 32 && curCh <= 126) {
				printf("%c", norDataPtr[idx + lineIdx]);
			} else {
				printf(".");
			}
		}
		
		printf("|\n\r");
		idx += BYTES_PER_LINE;
	}
}

main.c

//main.c
#include "myprintf.h"
#include "nand.h"
#include "nor.h"
#include "uart.h"

char gWriteDataBuffer[2048];

void test_nor();

int main(void) {
	uart0_init();
	printf("%s\n\r", "Nor Flash Test.");

	test_nor();
	return 0;
}



void test_nor(){
	unsigned char op;
	unsigned int norAddr = 0, dataLen = 0;
	nand_init();

	while(1) {
		printf("Now testing nor flash...\n\r");
		printf("----> s/S: Scan nor Flash Chip ID.\n\r");
		printf("----> r/R: Read Data From nor Flash.\n\r");
		printf("----> w/W: Write Data to nor Flash.\n\r");
		printf("----> e: Erase Sector From nor Flash.\n\r");
		printf("----> E: Erase Nor Flash Chip.\n\r");
		printf("----> q/q: Quit the test.\n\r");
		printf("Please select one operation: ");
		op = uart0_getc();
		uart0_putc(op);
		printf("\n\r");
		switch(op){
			case 'w':
			case 'W':
				printf("Please input Nor Address: ");
				scanf("%x\r", &norAddr);
				printf("0x%x\n\r", norAddr);
				printf("Please input the string to write: ");
				scanf("%s\r", gWriteDataBuffer);
				printf("%s\n\r", gWriteDataBuffer);
				nor_jedec_writeData(norAddr, gWriteDataBuffer, strlen(gWriteDataBuffer));
				printf("Write Completed.\n\r");		
				break;
			case 's':
			case 'S':
				nor_jedec_readID();
				nor_cfi_readDeviceDetails();
				break;
			case 'r':
			case 'R':
				printf("Please input Nor Address: ");
				scanf("%x\r", &norAddr);
				printf("0x%x\n\r", norAddr);
				printf("Please input the bytes to read: ");
				scanf("%d\r", &dataLen);
				printf("%d\n\r", dataLen);
				nor_jedec_dump((unsigned char*)norAddr, dataLen);
				break;
			case 'e':
				printf("Please input Nor Sector Address: ");
				scanf("%x\r", &norAddr);
				printf("0x%x\n\r", norAddr);
				nor_jedec_sectorErase(norAddr);
				break;
			case 'E':
				nor_jedec_chipErase();
				break;
			case 'q':
			case 'Q':
				printf("Test is done. Bye.\n\r");
				return;
			default:
				//do nothing.
				printf("Invalid input.Please try again.\n\r");
				break;
		}
		printf("\n\r");
	}
}


void nand_Dump(unsigned char* addrInNand, unsigned int len);
void test_nand(){
	unsigned char op;
	unsigned int nandAddr = 0, dataLen = 0;
	unsigned int blockAddr = 0;
	unsigned int flag = 0;
	nand_init();

	while(1) {
		printf("Now testing nand flash...\n\r");
		printf("----> s/S: Scan Nand Flash Chip ID.\n\r");
		printf("----> r/R: Read Data From Nand Flash.\n\r");
		printf("----> w/W: Write Data to Nand Flash.\n\r");
		printf("----> e/E: Erase Data From Nand Flash.\n\r");
		printf("----> c/C: Chech Specified Block is Bad or Not.\n\r");
		printf("----> q/q: Quit the test.\n\r");
		printf("Please select one operation: ");
		op = uart0_getc();
		uart0_putc(op);
		printf("\n\r");
		switch(op){
			case 'c':
			case 'C':
				printf("Please input Nand Address: ");
				scanf("%x\r", &nandAddr);
				printf("0x%x\n\r", nandAddr);
				nand_selectChip();
				flag = nand_BadBlockCheck(nandAddr);
				nand_deSelectChip();
				if(flag){
					printf("Block contains nand address 0x%08x is Bad Block!\n\r", nandAddr);
				}else {
					printf("Block contains nand address 0x%08x is Good.\n\r", nandAddr);
				}
				break;
			case 'w':
			case 'W':
				printf("Please input Nand Address: ");
				scanf("%x\r", &nandAddr);
				printf("0x%x\n\r", nandAddr);
				printf("Please input the string to write: ");
				scanf("%s\r", gWriteDataBuffer);
				printf("%s\n\r", gWriteDataBuffer);
				nand_Write((unsigned char*)nandAddr, gWriteDataBuffer, strlen(gWriteDataBuffer));
				break;
			case 's':
			case 'S':
				nand_ReadID();
				break;
			case 'r':
			case 'R':
				printf("Please input Nand Address: ");
				scanf("%x\r", &nandAddr);
				printf("0x%x\n\r", nandAddr);
				printf("Please input the bytes to read: ");
				scanf("%d\r", &dataLen);
				printf("%d\n\r", dataLen);
				nand_Dump((unsigned char*)nandAddr, dataLen);
				break;
			case 'e':
			case 'E':
				printf("Please input Nand Address to erase: ");
				scanf("%x\r", &nandAddr);
				printf("0x%x\n\r", nandAddr);
				printf("Erasing...\n\r");
				nand_Erase((unsigned char*)nandAddr, K9F2G08U0C_BLOCK_SIZE);
				printf("Erase Done.\n\r");
				break;
			case 'q':
			case 'Q':
				printf("Test is done. Bye.\n\r");
				return;
			default:
				//do nothing.
				printf("Invalid input.Please try again.\n\r");
				break;
		}
		printf("\n\r");
	}
}

编译并烧写至Nor Flash中,并以Nor Flash方式启动,观察串口如下所示:
在这里插入图片描述
使用linux工具hexdump观察BIN文件的数值如下所示:
在这里插入图片描述
可见,通过JEDEC接口我们读取到的制造商ID是0xC2,设备ID是0x2249,与数据手册吻合。

读取芯片性能数据

对于芯片工作电压、容量大小、时序要求、分区大小等信息,我们则需要通过CFI接口(COMMON FLASH MEMORY INTERFACE)来进行读取。
通过上面的JEDEC接口可知,我们需要通过JEDEC接口来进入CFI模式。我们向0x55地址写入0x98命令,即可进入CFI模式。之后读取0x10、0x11、0x12这三个地址,如果分别得到’Q’、‘R’、'Y’这三个字符对应的ASCII,则表明进入成功。
下面是四种CFI命令详图。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果使用CFI接口,需要配合CFI publication 100文档来解析数据。我们找到设备结构信息相关的地方,截图如下所示:
在这里插入图片描述
通过CFI publication 100规范我们可以看到,关于可擦除区域中包含的扇区个数,CFI读出的数据是0,当并不是说该区域中没有扇区,而是含有一个扇区:bit 15~0 = y; (y+1)表示该区域中可擦除扇区的个数。而高两个字节bit31~16=x; (x*256)表示该区域中每个扇区的大小。
CFI读取信息代码如下所示:

int nor_cfi_enter(void){
	int status = 0;
	nor_jedec_cmd(0x55, 0x98);
	char cfiFlags[3];
	cfiFlags[0] = nor_jedec_readJedecData(0x10);
	cfiFlags[1] = nor_jedec_readJedecData(0x11);
	cfiFlags[2] = nor_jedec_readJedecData(0x12);

	if('Q' == cfiFlags[0] &&
		'R' == cfiFlags[1] &&
		'Y' == cfiFlags[2]) {
		printf("\n\rEnter CFI Success.\n\r");
	}else {
		nor_jedec_reset();
		printf("\n\rEnter CFI Failed.\n\r");
		status = -1;
	}
	return status;
}

struct norRegionInfo {
	unsigned int sectorCount;
	unsigned int sectorSize; 
};

struct norRegionInfo gRegionInfo[8];

void nor_cfi_readDeviceDetails(void) {
	unsigned int capacity = 1;
	unsigned int idx = 0;
	nor_jedec_reset();
	if(nor_cfi_enter()) {
		//进入CFI模式失败
		return;
	}
	capacity <<= nor_jedec_readJedecData(0x27);
	printf("MX29LV160DBTI Capacity: %d MB\n\r", capacity / (1024*1024));
	//设备可擦出分区数量
	unsigned int eraseRegionCount = 0;
	eraseRegionCount = nor_jedec_readJedecData(0x2C);
	printf("Number of erase regions within device: %d\n\r", eraseRegionCount);
	unsigned int regionInfoBaseAddr = 0x2D;
	for(idx = 0; idx < eraseRegionCount; idx++){
		gRegionInfo[idx].sectorCount = nor_jedec_readJedecData(regionInfoBaseAddr++);
		gRegionInfo[idx].sectorCount |= (nor_jedec_readJedecData(regionInfoBaseAddr++) << 8);
		gRegionInfo[idx].sectorCount += 1;

		gRegionInfo[idx].sectorSize = nor_jedec_readJedecData(regionInfoBaseAddr++);
		gRegionInfo[idx].sectorSize |= (nor_jedec_readJedecData(regionInfoBaseAddr++) << 8);
		gRegionInfo[idx].sectorSize *= 256;
	}

	//打印出各个扇区的起始地址
	printf("%d region within Nor Flash.\n\r", eraseRegionCount);
	for(idx = 0; idx < eraseRegionCount; idx++) {
		printf("\tRegion%d: %2d sector (%2dKB)\n\r", idx, gRegionInfo[idx].sectorCount), gRegionInfo[idx].sectorSize/1024;
	}
	printf("Sector Start Addresses:\n\r");
	unsigned sectorIdx = 0;
	unsigned baseAddr = 0;
	unsigned itemCount = 0;
	for(idx = 0; idx < eraseRegionCount; idx++) {
		for(sectorIdx = 0; sectorIdx < gRegionInfo[idx].sectorCount; sectorIdx++) {
			printf("0x%08x ", baseAddr);
			baseAddr += gRegionInfo[idx].sectorSize;
			//输出5个地址后换行
			itemCount++;
			if(itemCount == 5){
				printf("\n\r");
				itemCount = 0;
			}
		}
		
	}
	//退出CFI模式
	nor_jedec_reset();
}

编译完烧写至Nor Flash中,并以Nor方式启动,观察串口输出如下所示:
在这里插入图片描述
我们可以和OpenJTAG工具中关于Nor Flash的扫描信息对比,发现读取正确。OpenJTAG读取的信息如下图所示:
在这里插入图片描述

擦除扇区、芯片 和 写入数据

在这里插入图片描述

判断操作是否完成

Toggle法

在这里插入图片描述
它的原理是连续两次读取数据总线上的数据,判断数据总线上的第6位数值(DQ6)是否翻转,如果没有翻转则正确,否则还要判断第5位(DQ5),以确定是否是因为超时而引起的翻转。

void nor_jedec_waitReady(unsigned int addr) {
	volatile unsigned char* ptr = (volatile unsigned char*)addr;
	unsigned char preVal = *ptr;
	unsigned char curVal = *ptr;
	while((preVal & (1 << 6)) != (curVal & (1 << 6))) {
		//当第6比特位不再变化时,表明设备就绪
		if(curVal & (1 << 5)) {
			//这是由超时引起的翻转
			printf("Timeout! Operation Failed.\n\r");
			break;
		}
		preVal = curVal;
		curVal = *ptr;
	}
}

标志位简介

NorFlash 提供几个数据位来确定一个写操作的状态,它们分别是: 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可以区分NorFlash是否处于擦除中或者擦除状态,但不能区分哪个快被选中擦除。因此需要这2个位来确定扇区和模式状态信息。

DQ5: 超时位(Exceeded Timing Limits),当编程或擦除操作超过了一个特定内部脉冲计数是DQ5=1;这表明操作失败。当编程时把’0’改为’1’就会导致DQ5=1,因为只有擦除擦做才能把’0’改为’1’。当错误发生后需要执行复位命令才能返回到读数据状态。

DQ3: (扇区擦除计时位)Sector Erase Timer,只在扇区擦除指令时起作用。当擦除指令真正开始工作是DQ3=1,此时输入的命令(除擦除暂停命令外)都被忽略。DQ3=0,是可以添加附加的扇区用于多扇区擦除。

关于写入数据

对于Flash芯片来说,写入又叫编程。编程前一定要先擦除。因为编程只能将’1’改写为’0’,而擦除则是将扇区或者整个芯片全部擦写为’1’。

擦除、写入相关代码

void nor_jedec_chipErase(void) {
	nor_jedec_reset();
	printf("Ready to Erase Nor Flash Chip...\n\r");
	printf("Erasing...\n\r");
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	nor_jedec_cmd(0x555, 0x80);
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	nor_jedec_cmd(0x555, 0x10);
	nor_jedec_waitReady(0);	
	printf("Chip Erase completed.\n\r");
}

void nor_jedec_sectorErase(unsigned int sectorAddr) {
	printf("Ready to Erase Nor Flash Sector 0x%x.\n\r", sectorAddr);
	printf("Erasing...\n\r");
	nor_jedec_reset();
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	nor_jedec_cmd(0x555, 0x80);
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	
	//第六个周期是把命令0x30写入要擦除块的首地址内
	*(volatile unsigned short*)sectorAddr = 0x30;
	nor_jedec_waitReady(sectorAddr);
	printf("Erase Sector 0x%x completed.\n\r", sectorAddr);
}

void nor_jedec_writeData(unsigned int addr, unsigned char* data, unsigned int len) {
	unsigned int idx = 0;
	volatile unsigned short* sData = data;
	unsigned extraByteFlag = len & 1;//待写入的数据是否是奇数字节

	for(idx = 0; idx < ((len - extraByteFlag)>>1); idx++) {
		nor_jedec_write2Byte(addr, sData[idx]);
		addr += 2;
	}
	if(extraByteFlag){
		//最后只剩一个字节的数据
		//但是16bit的Nor Flash一次需要写入两字节的数据
		unsigned short extraData = data[len-1];
		nor_jedec_write2Byte(addr, extraData);
	}
}

void nor_jedec_write2Byte(unsigned int addr, unsigned short val) {
	nor_jedec_reset();
	nor_jedec_cmd(0x555, 0xAA);
	nor_jedec_cmd(0x2AA, 0x55);
	nor_jedec_cmd(0x555, 0xA0);
	*(volatile unsigned short*)addr = val;
	
	nor_jedec_waitReady(addr);
}

写入实验效果图

在这里插入图片描述

擦除扇区效果图

在这里插入图片描述

擦除整个芯片效果图

在这里插入图片描述

发布了26 篇原创文章 · 获赞 2 · 访问量 1068

猜你喜欢

转载自blog.csdn.net/BakerTheGreat/article/details/104383715