(嵌入式Linux 8 )NOR

NorFlash原理及硬件操作

资料来自:韦东山嵌入式linux

NorFlash硬件连接图:

可以看出数据线有16根,则位宽16位,地址线ADDR1接A0,ADDR2接A1。根据A0的值判断16位数据高低位

UBOOT操作:

1.读数据

md.b 0

2.读ID

根据手册的读取方法:

往地址555H写AAH
往地址2AAH写55H
往地址555H写90H
读0地址得到厂家ID: C2H
读1地址得到设备ID: 22DAH或225BH
退出读ID状态: 给任意地址写F0H

因为地址线的接线方式,555H是写给Norflash接口上的数据,那么在2440发出来的应该是555h<<1,Norflash才能收到555h

所以Uboot应该写入:

往地址AAAH写AAH                      mw.w aaa aa
往地址554写55H                       mw.w 554 55
往地址AAAH写90H                      mw.w aaa 90
读0地址得到厂家ID: C2H               md.w 0 1
读2地址得到设备ID: 22DAH或225BH      md.w 2 1
退出读ID状态:                        mw.w 0 f0

3.Nor有两种规范。如果需要获得norflash的信息(容量大小,电压大小等)1.jedec,通过读取flash的ID与uboot内核中比较获得信息,该方法适用于比较老的norflash。2.cfi模式,直接读取norflash中的CFI信息,其中就包含了flash的各种信息。

进入CFI模式    往55H写入98H
读数据:        读10H得到0051
               读11H得到0052
               读12H得到0059   //51 52 59对应QRY设备名称
               读27H得到容量

2440的A1接到NOR的A0,所以2440发出(555h<<1), NOR才能收到555h这个地址
UBOOT怎么操作?
进入CFI模式    往AAH写入98H            mw.w aa 98
读数据:        读20H得到0051           md.w 20 1
               读22H得到0052           md.w 22 1
               读24H得到0059           md.w 24 1
               读4EH得到容量           md.w 4e 1
               退出CFI模式             mw.w 0 f0

上述md.w 4e 1返回0015h,即2^21Byte,那么flash一共2M

4.写数据。

Nor启动时,norflash接在bank0,基地址0,2440读取Nor中的0地址作为0地址,所以Nor共占有地址2*1024*1024=0x200000,0x200000以外的地址便是内存地址,可以直接读写,但是0x200000以下的地址可直接读,但是不可直接写。写数据步骤:如果源地址中的内容已经被擦除(擦除后是0xff),则:

往地址555H写AAH 
往地址2AAH写55H 
往地址555H写A0H 
往地址PA写PD

2440的A1接到NOR的A0,所以2440发出(555h<<1), NOR才能收到555h这个地址
UBOOT怎么操作?
往地址AAAH写AAH               mw.w aaa aa
往地址554H写55H               mw.w 554 55
往地址AAAH写A0H               mw.w aaa a0
往地址0x100000写1234h         mw.w 100000 1234

如果源地址内容未擦除,则先擦除,后写入:

先擦除
mw.w aaa aa   //aaa对应Sector Erase中的555<<1
mw.w 554 55
mw.w aaa 80mw.w aaa aa
mw.w 554 55
mw.w 100000 30

 再烧写
mw.w aaa aa
mw.w 554 55
mw.w aaa a0
mw.w 100000 5678

 

Nor_编程

根据NorFlash的读取规则,可以实现Nor上的测试:

        * 1. 识别nor flash
        * 2. 擦除nor flash某个扇区
        * 3. 编写某个地址
        * 4. 读某个地址

1. 添加norflash_test测试

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;
		}
    }
}

检测flash名称:

通过CFI模式检测名称QRY,内存大小

进入CFI模式方法:

获取信息:

所以在实现scan_nor_flash功能时,先进入cfi模式,再写入指令.由于地址线2440的A1接flash的A0,所以在2440写入的值需要<<1.完善代码:

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

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

/*  比如  55H 98  进入CFI模式
    *往0+(55H<<1)写入0x98
    *base 基地址  offset偏移地址   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;
}

void nor_cmd(unsigned int offset,unsigned int val)
{
    nor_write_word(NOR_FLASH_BASE,offset,val);
}

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(0x555, 0xaa);    /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(0x555, 0x90);    /* read id */
	vendor = nor_data(0);//厂家ID
	device = nor_data(1);//设备ID
	nor_cmd(0, 0xf0);        /* reset */
    
	nor_cmd(0x55, 0x98);  /* 进入cfi模式 */

    //读取QRY获取设备ID
	str[0] = nor_data(0x10);
	str[1] = nor_data(0x11);
	str[2] = nor_data(0x12);
	str[3] = '\0';
	printf("str = %s\n\r", str);

	/* 打印容量 */
    size = 1<<(nor_data(0x27));//左移1位转换成2的次方
	printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor,device, size, size/(1024*1024));


	/* 打印容量 */
    size = 1<<(nor_dat(0x27));//左移1位转换成2的次方
	printf("nor size = 0x%x, %dM\n\r", size, size/(1024*1024));
	/* 打印各个扇区的起始地址 */

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

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;
		}
    }
}

修改makefile与main后,程序输出str=QRY,读出flash大小2M,读出设备ID等信息

 

接下来打印扇区地址

数据手册中,扇区的打印格式:

     *    erase block region : 里面含有1个或多个block, 它们的大小一样
     * 一个nor flash含有1个或多个region
     * 一个region含有1个或多个block(扇区)

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

所以0000+1表示有几个regin,0040h表示有几个扇区,接下来验证一下:

完善函数:

void do_scan_nor_flash(void)
{
	char str[4];
    int regions, i,j,cnt=0;
	unsigned int size;
    unsigned int region_info_base,block_size,blocks,block_addr;
	/* 打印厂家ID、设备ID */
	nor_cmd(0x55, 0x98);  /* 进入cfi模式 */

    //读取QRY获取设备ID
	str[0] = nor_data(0x10);
	str[1] = nor_data(0x11);
	str[2] = nor_data(0x12);
	str[3] = '\0';
	printf("str = %s\n\r", str);

	/* 打印容量 */
    size = 1<<(nor_data(0x27));//左移1位转换成2的次方
	printf("nor size = 0x%x, %dM\n\r", 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_data(0x2c);//读取regions个数
    region_info_base = 0x2d;//读取第一个region的指令地址
    block_addr=0;//block的扇区地址从0开始
    for(i=0;i<regions;i++)
    {
        blocks=nor_data(region_info_base)+(nor_data(region_info_base+1)<<8)+1;//读取blocks个数
        block_size=256*(nor_data(region_info_base+2)+(nor_data(region_info_base+3)<<8));//读取每个block大小
        region_info_base+=4;//发送的指令地址+4,往后继续读取下一个region

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

打印输出:

由数据手册上的0040h扇区可以计算0040h*256=4000h,所以每个扇区地址相差4000h,与打印结果一致。

 

完善擦除与写入操作

#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;
}

/* offset是基于NOR的角度看到 */
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);
}


void wait_ready(unsigned int addr)
{
	unsigned int val;
	unsigned int pre;

	pre = nor_dat(addr>>1);
	val = nor_dat(addr>>1);
	while ((val & (1<<6)) != (pre & (1<<6)))//bit6是否相等判断擦除或者写入状态是否完成
	{
		pre = val;
		val = nor_dat(addr>>1);		
	}
}


/* 进入NOR FLASH的CFI模式
 * 读取各类信息
 */
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);
}

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

	printf("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);
}

void do_write_nor_flash(void)
{
	unsigned int addr;
	unsigned char str[100];
	int i, j;
	unsigned int val;
	
	/* 获得地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	printf("Enter the string to write: ");
	gets(str);

	printf("writing ...\n\r");

	/* str[0],str[1]==>16bit 
	 * str[2],str[3]==>16bit 
	 */
	i = 0;
	j = 1;
	while (str[i] && str[j])//都不==0
	{
		val = str[i] + (str[j]<<8);
		
		/* 烧写 */
		nor_cmd(0x555, 0xaa);	 /* 解锁 */
		nor_cmd(0x2aa, 0x55); 
		nor_cmd(0x555, 0xa0);	 /* program */
		nor_cmd(addr>>1, val);
		/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
		wait_ready(addr);

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

	val = str[i];
	/* 烧写 */
	nor_cmd(0x555, 0xaa);	 /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(0x555, 0xa0);	 /* program */
	nor_cmd(addr>>1, val);
	/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
	wait_ready(addr);
}
void do_read_nor_flash(void)
{
	unsigned int addr;
	volatile unsigned char *p;
	int i, j;
	unsigned char c;
	unsigned char str[16];
	
	/* 获得地址 */
	printf("Enter the address to read: ");
	addr = get_uint();

	p = (volatile unsigned char *)addr;

	printf("Data : \n\r");
	/* 长度固定为64 */
	for (i = 0; i < 4; i++)
	{
		/* 每行打印16个数据 */
		for (j = 0; j < 16; j++)
		{
			/* 先打印数值 */
			c = *p++;
			str[j] = c;
			printf("%02x ", c);
		}

		printf("   ; ");

		for (j = 0; j < 16; j++)
		{
			/* 后打印字符 */
			if (str[j] < 0x20 || str[j] > 0x7e)  /* 不可视字符 */
				putchar('.');
			else
				putchar(str[j]);
		}
		printf("\n\r");
	}
}

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;
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值