当前位置: 首页 > 工具软件 > knl-flash-ui > 使用案例 >

JZ2440之NOR FLASH简单操作篇

宁锐
2023-12-01

1、显示NOR FLASH的重要信息

NOR FLASH的一些重要信息(如内存大小、SECTOR数、运行电压和定时信息等)都是可以通过进入CFI查询模式来进行读取的;而设备ID、厂家ID等信息是需要在自动选择模式下来读取。
在这里我们通过自动选择模式来读取并显示JZ2440所用NOR FLASH的设备ID、厂家ID;通过CFI查询模式来读取并显示内存大小和每个SECTOR的起始地址。
对于16位的NOR FLASH需要依次向555H 写 AAH、2AAH 写 55H 和 555H 写 90H来进入自动选择模式,随后的一个周期进行读取数据,操作完自动选择模式后必须要RESET,可以向任意地址写F0H来实现RESET;通过向55H 写 98H来进入CFI查询模式以读取需要的数据,操作完CFI查询模式后必须RESET回之前的模式,否则不能执行除CFI查询和RESET之外的任何操作

以下是显示JZ2440上NOR FLASH重要信息的部分代码。

void do_scan_nor_flash(void)
{
	char str[4];
	unsigned int size;		/* 内存容量 */
	int regions, region_info_base;	
	int blocks,block_size, block_addr;
	int i, j, cnt = 0;
	int vendor, device;	/* vendor:厂家ID;device:设备ID */

	/* 打印厂家ID、设备ID */
	nor_cmd(0x555, 0xaa);	/* 解锁 */
	nor_cmd(0x2aa, 0x55);
	nor_cmd(0x555, 0x90);	/* read id */
	
	vendor = nor_dat(0);	/* 厂家ID */
	device = nor_dat(1);	/* 设备ID */
	nor_cmd(0, 0xf0);		/* reset */

	nor_cmd(0x55, 0x98);	/* 进入CFI模式 */
	str[0] = nor_dat(0x10);
	str[1] = nor_dat(0x11);
	str[2] = nor_dat(0x12);
	str[3] = '\0';
	printf("str = %s\n\r", str);	/* Query-unique ASCII string "QRY" */

	/* 打印容量 */
	size = 1 << (nor_dat(0x27));
	printf("vendor = 0x%x, device = 0x%x, size = 0x%x, %dMB\n\r", vendor, device, size, size/(1024*1024));	

	/* 打印各个扇区的起始地址 
	/* Erase block region information:
	 *	前两字节 + 1	:  表示该region中block的数目
	 *	后两字节 * 256	:  表示该region中每个block的大小
	 */
	regions = nor_dat(0x2c);	/* region数 */
	region_info_base = 0x2d;	/* region的基址 */
	block_addr = 0;
	printf("Block/Sector start Address:\n\r");
	for (i = 0; i < regions; i++)
	{
		blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base + 1) << 8);
		block_size = 256 * (nor_dat(region_info_base + 2) + (nor_dat(region_info_base + 3) << 8));
		//printf("\n\rregion %d , blocks = %d, block_size = 0x%08x, block addr = 0x%08x\n\r", i, blocks, block_size, block_addr);
		region_info_base += 4;

		for (j = 0; j < blocks; j++)
		{
			/* 打印每个block的起始地址 */
			printHex(block_addr);
			printf(" ");
			cnt++;
			block_addr += block_size;
			if (cnt % 5 == 0)		/* 每5个block的起始地址打印一行 */
			{
				printf("\n\r");
			}	
		}
	}
	printf("\n\r");
	
	nor_cmd(0, 0xf0);	/* 退出CFI模式 */
}

2、读NOR FLASH

在读NOR FLASH时我们需要先向NOR FLASH发出我们想读的地址,然后以每行打印16byte打印4行的形式来打印NOR FLASH的数据(同时打印数值和字符),具体代码如下。

void do_read_nor_flash(void)
{
	unsigned int addr;
	volatile unsigned char *p;
	int i,j;
	unsigned char str[16];

	/* 获得地址 */
	printf("Enter the address of sector to read: ");
	addr = get_uint();		/* 得到 unsigned int 型数据 */

	p = (volatile unsigned char *)addr;		/* 以字节进行操作 */

	printf("Data: \n\r");
	/* 长度固定为64 */
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 16; j++)
		{
			/* 先打印数值 */
			str[j] = *p++;
			printf("%02x ", str[j]);
		}
		printf("  ;  ");
		for (j = 0; j < 16; j++)
		{
			/* 后打印字符 */
			if (str[j] < 0x20 || str[j] > 0x7e)	/* 不可视字符 */
			{
				putchar('.');
			}
			else
			{
				putchar(str[j]);
			}
		}
		printf("\n\r");	
	}
}

3、擦除NOR FLASH的扇区

NOR FLASH可以直接进行读,但是不能直接进行写,如果要向NOR FLASH中写入数据必须先对相应地址进行擦除。NOR FLASH支持Sector EraseChip Erase,这里我们对扇区进行擦除,实现代码如下。

void do_erase_nor_flash(void)
{
	unsigned int addr;

	/* 获得地址 */
	printf("Enter the address of sector to erase: ");
	addr = get_uint();

	/* 擦除数据 */
	puts("erasing...\n\r");
	nor_cmd(0x555, 0xaa);	/* 解锁 */
	nor_cmd(0x2aa, 0x55);
	nor_cmd(0x555, 0x80);	/* erase sector */

	nor_cmd(0x555, 0xaa);	/* 解锁 */
	nor_cmd(0x2aa, 0x55);
	nor_cmd(addr >> 1, 0x30); /* 发出扇区地址 */
	/* 等待擦除完成 */
	wait_ready(addr);	
}

其中之所以要addr >> 1是因为我们的NOR FLASH地址线的0bit是和CPU地址线的1bit相连的(想了解为何请看我之前的博文S3C2440内存控制器与SDRAM),而我们在nor_cmd()(具体实现看下文)中已经add << 1了,故这里要移回来,这样NOR FLASH操作的地址才会是我们想要的。

4、写NOR FLASH

在对NOR FLASH执行写操作时,我们要先发出想的地址,然后再发出我们要写的数据,以下是实现代码部分。

void do_write_nor_flash(void)
{
	unsigned int addr;
	unsigned char str[100];
	unsigned int val;
	int i, j;

	/* 获得地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	/* 获得数据 */
	printf("Enter the string to write: ");
	gets(str);
	printf("writing...\n\r");

	/* 构造16bit数据  
	 * str[0], str[1]==>16bit
	 * str[2], str[3]==>16bit
	 */
	i = 0;
	j = 1;
	while (str[i] && str[j])
	{
		val = str[i] + (str[j] << 8);

		/* 烧写 */
		nor_cmd(0x555, 0xaa);	/* 解锁 */
		nor_cmd(0x2aa, 0x55);
		nor_cmd(0x555, 0xa0);	/* program */
		nor_cmd(addr>>1, val);
		wait_ready(addr);		/* 等待烧写完成 */

		i += 2;
		j += 2;
		addr += 2;	
	}

	val = str[i];	/* 当while()不满足时,str[i]中依然可能有值,也需要写入 */
	/* 烧写 */
	nor_cmd(0x555, 0xaa);	/* 解锁 */
	nor_cmd(0x2aa, 0x55);
	nor_cmd(0x555, 0xa0);	/* program */
	nor_cmd(addr>>1, val);
	wait_ready(addr);	/* 等待烧写完成 */
}

【声明】
上文中部分依赖函数如下

#define NOR_FLASH_BEASE 0		/* jz2440, nor flash->nGCS0, base addr = 0 */

void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)		/* 写字节, base : 基址;offset : 偏移地址;val : 写的值 */
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	*p = val;	
}

void nor_cmd(unsigned int offset, unsigned int cmd)	/* 写命令 */
{
	nor_write_word(NOR_FLASH_BEASE, offset, cmd);
}

unsigned int nor_read_word(unsigned int base, unsigned int offset)	/* 读字节 */
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	return *p;	
}

unsigned int nor_dat(unsigned int offset)		/* 读数据 */
{
	return nor_read_word(NOR_FLASH_BEASE, offset);
}

void wait_ready(unsigned int addr)		/* 等待操作完成 */
{
	unsigned int pre, val;

	pre = nor_dat(addr>>1);
	val = nor_dat(addr>>1);

	while (val & (1<<6) != pre & (1<<6))	/* 当读到数据的第6bit(Q6)无变化时,操作完成 */
	{
		pre = val;	/* 更新先前值 */
		val = nor_dat(addr>>1);
	}
}

:在编译程序时需加上: -march=armv4, 否则 volatile unsigned short *p = xxx; *p = val; 会被拆分成2个strb操作(这会导致程序出错)。

 类似资料: