第十五课(2)Nor FLash编程_识别

1、 进入CFI模式

#include "my_printf.h"
#include "string_utils.h"

#define NOR_FLASH_BASE  0  /* jz2440, nor-->cs0, base addr = 0 */

/* 比如:   55H 98 
 * 本意是: 往(0 + (0x55)<<1)写入0x98
 */
void nor_write_word(unsigned int base, unsigned int offset, unsigned int 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_BASE, 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_BASE, offset);
}


/* 进入NOR FLASH的CFI模式
 * 读取各类信息
 */
void do_scan_nor_flash(void)
{
	char str[4];
	
	/* 打印厂家ID、设备ID */
	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);

	/* 打印容量 */

	/* 打印各个扇区的起始地址 */
}

void do_erase_nor_flash(void)
{
}

void do_write_nor_flash(void)
{
}
void do_read_nor_flash(void)
{
}

void nor_flash_test(void)
{
	char c;

	while (1)
	{
		/* 打印菜单, 供我们选择测试内容 */
		printf("[s] Scan nor flash\n\r");
		printf("[e] Erase nor flash\n\r");
		printf("[w] Write nor flash\n\r");
		printf("[r] Read nor flash\n\r");
		printf("[q] quit\n\r");
		printf("Enter selection: ");

		c = getchar();
		printf("%c\n\r", c);

		/* 测试内容:
		 * 1. 识别nor flash
		 * 2. 擦除nor flash某个扇区
		 * 3. 编写某个地址
		 * 4. 读某个地址
		 */
		switch (c)		 
		{
			case 'q':
			case 'Q':
				return;
				break;
				
			case 's':
			case 'S':
				do_scan_nor_flash();
				break;

			case 'e':
			case 'E':
				do_erase_nor_flash();
				break;

			case 'w':
			case 'W':
				do_write_nor_flash();
				break;

			case 'r':
			case 'R':
				do_read_nor_flash();
				break;
			default:
				break;
		}
	}
}

注意:
虽然传入参数val是int类型,但是指针是short类型,说明指针只存放低16bit的数据。

void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	*p = val;
}

编译出错:

[email protected]:/work/uart/test$ make
arm-linux-gcc -c -o nor_flash.o nor_flash.c
arm-linux-gcc -c -o my_printf.o my_printf.c
arm-linux-gcc -c -o string_utils.o string_utils.c
arm-linux-gcc -c -o lib1funcs.o lib1funcs.S
#arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o exception.o interrupt.o timer.o nor_flash.o my_printf.o string_utils.o lib1funcs.o -o sdram.elf
my_printf.o: In function `out_num':
my_printf.c:(.text+0x120): undefined reference to `__aeabi_uidivmod'
my_printf.c:(.text+0x158): undefined reference to `__aeabi_uidiv'
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1

解决方法:https://blog.csdn.net/qq_40674996/article/details/105589523

运行结果:

[s] Scan nor flash
[e] Erase nor flash
[w] Write nor flash
[r] Read nor flash
[q] quit
Enter selection: s
str = QRY

说明确实进入了CFI模式

2.、打印出Nor的内存大小

在这里插入图片描述
15的10进制为21,221 = 2M
221 相当于1<<21

void do_scan_nor_flash(void)
{
	char str[4];
	unsigned int size;
	
	/* 打印厂家ID、设备ID */
	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);

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

	/* 打印各个扇区的起始地址 */

	/* 退出CFI模式 */
	nor_cmd(0, 0xf0);
}

记住,要退出CFI模式,不然就读取不了Nor上烧写的程序。

3、打印各个扇区的起始地址

MX29LV160DBTI-70G(NOR FLASH).pdf 的芯片手册 扇区为0,不太合理。
第十五课(2)Nor FLash编程_识别_第1张图片
MX29LV800BBTC.pdf 的手册提及 参考refer to the CFI publication 100
第十五课(2)Nor FLash编程_识别_第2张图片
我们打开百度搜索“CFI publication 100”:
https://wenku.baidu.com/view/cd1c1e22482fb4daa58d4b42.html
第十五课(2)Nor FLash编程_识别_第3张图片
表格的意思,从2D地址开始,每个地址4字节, 前16bit(2D2E) + 1表示多少个block,后16bit(2F30) *256表示block的大小。

代码:

void do_scan_nor_flash(void)
{
	char str[4];
	unsigned int size;
	int regions, i;
	int region_info_base;
	int block_addr, blocks, block_size, j;
	int cnt;

	int vendor, device;
	
	/* 打印厂家ID、设备ID */
	nor_cmd(0x555, 0xaa);    /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(0x555, 0x90);    /* read id */
	vendor = nor_dat(0);
	device = nor_dat(1);
	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);

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

	/* 打印各个扇区的起始地址 */
	/* 名词解释:
	 *erase block region : 里面含有1个或多个block, 它们的大小一样
	 * 一个nor flash含有1个或多个region
	 * 一个region含有1个或多个block(扇区)

	 * Erase block region information:
	 *    前2字节+1    : 表示该region有多少个block 
	 *    后2字节*256  : 表示block的大小
	 */

	regions = nor_dat(0x2c);
	region_info_base = 0x2d;
	block_addr = 0;
	printf("Block/Sector start Address:\n\r");
	cnt = 0;
	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));
		region_info_base += 4;

//		printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);

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

运行结果:

[s] Scan nor flash
[e] Erase nor flash
[w] Write nor flash
[r] Read nor flash
[q] quit
Enter selection: s
str = QRY
size = 0x200000 2M
Block/Sector start Address:
0x00000000 0x00004000 0x00006000 0x00008000 0x00010000
0x00020000 0x00030000 0x00040000 0x00050000 0x00060000
0x00070000 0x00080000 0x00090000 0x000A0000 0x000B0000
0x000C0000 0x000D0000 0x000E0000 0x000F0000 0x00100000
0x00110000 0x00120000 0x00130000 0x00140000 0x00150000
0x00160000 0x00170000 0x00180000 0x00190000 0x001A0000
0x001B0000 0x001C0000 0x001D0000 0x001E0000 0x001F0000

4、完善代码,打印设备ID 、厂家ID

	/* 打印厂家ID、设备ID */
	nor_cmd(0x555, 0xaa);    /* 解锁 */
	nor_cmd(0x2aa, 0x55); 	 /* 解锁 */
	nor_cmd(0x555, 0x90);    /* read id */
	vendor = nor_dat(0);
	device = nor_dat(1);
	nor_cmd(0, 0xf0);        /* reset:退出读ID状态 */
	
	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);

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

运行结果:
第十五课(2)Nor FLash编程_识别_第4张图片
查看反汇编文件,可以看出,读出的是Nor上的数据,而不是芯片的ID。

解决方法:
主函数:
main函数代码如下所示。把timer中断去掉,否则: 测试NOR Flash时进入CFI等模式时, 如果发生了中断,cpu必定读NOR Flash,那么读不到正确的指令,导致程序崩溃。

int main(void)
{
	led_init();
	key_eint_init();   /* 初始化按键, 设为中断源 */
	//timer_init();
	
	puts("\n\rg_A = ");
	printHex(g_A);
	puts("\n\r");

	nor_flash_test();
	
	return 0;
}

再次编译,ID仍然不对, 多执行几次,程序也没有死机。
查看反汇编文件:
第十五课(2)Nor FLash编程_识别_第5张图片
我们的nor_write_word是想一次性将val的两字节写入指针指向的地址:

void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	*p = val;
}

但是反汇编,分两次写入:
第十五课(2)Nor FLash编程_识别_第6张图片
在必应上搜英文比较好: 搜索"volatile unsigned short ldrb ldrh"
(英语不好,都不知道哪个网页是要的答案.)
找到一个网页说是加上 --march
我们参考下uboot,看下有什么参数供我们选择:
在configmk文件里搜索到:
在这里插入图片描述
因此我们在makefile文件里加上:

all: start.o led.o uart.o init.o main.o exception.o interrupt.o timer.o nor_flash.o my_printf.o string_utils.o lib1funcs.o
	#arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf
	arm-linux-ld -T sdram.lds $^ -o sdram.elf
	arm-linux-objcopy -O binary -S sdram.elf sdram.bin
	arm-linux-objdump -D sdram.elf > sdram.dis
clean:
	rm *.bin *.o *.elf *.dis
	
%.o : %.c
	arm-linux-gcc -march=armv4 -c -o $@ $<

%.o : %.S
	arm-linux-gcc -march=armv4 -c -o $@ $<	

可以看到运行结果正常,并且查看下反汇编文件是strh
第十五课(2)Nor FLash编程_识别_第7张图片

在编译程序时加上:-march=armv4
否则,
volatile unsigned short *p = XXX;
*p = val; //会被差分成2个strb操作

你可能感兴趣的:(ARM裸机)