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