norflash 兼具dram和nandflash优点: 掉电数据不丢失,可以随机 读,也就是说可以直接执行程序。但有个缺点就是造价比较高,一般只是小容量使用。
以前用mini2440板子用的是SST39VF1601,具体操作过程可见前面博文。这里是EN29LV160AB,跟之前的略有不同。
对norflash的操作主要就是读、写、擦除和识别等。EN29LV160AB的数据宽度可以是8位字节型,也可以是16位的字型,它由EN29LV160AB的某一引脚配置实现的。
在这里我们选择字型。
对norflash的读操作比较简单,系统上电后会自动进入读模式,而且也不需要额外的命令来实现读操作,就像访问dram一样cpu直接发出读指令 就可以了。下面的函数实现了读操作:
U16 read_en29lv160ab(U32 addr) { return *((volatile U16 *)(addr)); }
norflash不仅能够实现硬件复位,而且可以实现软件复位。软件复位的操作是向任一地址写入复位命令0xF0。下面的函数实现了软件复位:
void reset_en29lv160ab(void) { *((volatile U16 *)0x0) = 0xf0; }
norflash的擦除操作和写操作要稍微复杂一些,它们需要4个或6个周期来完成,每一个周期都要把相应的命令写入norflash中的某一命令寄存器中。写操作的过程为第一
个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0xA0再写入地址为0x555命令寄存
器中,第四个周期为真正地把要写入的数据写入到norflash的地址中。下面的函数实现了写操作,其中该函数的两个输入参数分别为要写入的数据和地址,为了方便,我们事
先定义好命令寄存器:
#define flash_base 0x00000000 #define CMD_ADDR0 *((volatile U16 *)((0x555<<1)+flash_base)) #define CMD_ADDR1 *((volatile U16 *)((0x2aa<<1)+flash_base))
U8 en29lv160ab_program(U32 addr, U16 dat) { CMD_ADDR0 = 0xaa; CMD_ADDR1 = 0x55; CMD_ADDR0 = 0xa0; *((volatile U16 *)(addr)) = dat; return check_toggle(); }
由于我们是把 norflash 连接到了 s3c2440 的bank 0上,因此norflash中的地址相对于s3c2440来说基址为0x00000000。而之所以又把norflash中的地址向左移一位(即乘
以2),是因为我们是把 s3c2440 的 ADDR1 连接到了 norflash 的A0上的缘故。
在该函数中,我们还调用了check_toggle函数,它的作用是用于判断这次操作是否正确,它的原型为:
U8 check_toggle(void) { volatile U16 newtoggle,oldtoggle; oldtoggle = *((volatile U16 *)0x0); while(1) { newtoggle = *((volatile U16 *)0x0); if((oldtoggle & 0x40)==(newtoggle & 0x40)) break; if(newtoggle & 0x20) //DQ5 { oldtoggle = *((volatile U16 *)0x0); newtoggle = *((volatile U16 *)0x0); if((oldtoggle & 0x40)==(newtoggle & 0x40)) break; else return 0; //错误 } oldtoggle = newtoggle; } return 1; //正确 }
它的原理是连续两次读取数据总线上的数据,判断数据总线上的第6位数值(DQ6)是否翻转,如果没有翻转则正确,否则还要判断第5位(DQ5),以确定是否是因为超时而引起的翻转。
norflash也需要擦除操作,写操作只能使“1”变为“0”,而只有擦除才能使“0”变为“1”---这点与nandflash一致。因此在写之前一定要先擦除。擦除分为块擦除和整片擦除。块擦
除的过程为第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0x80再写入地址为0x555命令寄存器中,第四个周期是把命令0xAA写入地址为0x555的命令寄存器中,第五个周期是把命令0x55再写入地址为0x2AA命令寄存器中,第六个周期是把命令0x30
写入要擦除块的首地址内。
注意因为这里选择字型,所以上图的命令序列应该选择word一栏。
下面的函数为块擦除,其中输入参数为要擦除块的首地址:
U8 en29lv160ab_sector_erase(U32 section_addr) { CMD_ADDR0 = 0xaa; CMD_ADDR1 = 0x55; CMD_ADDR0 = 0x80; CMD_ADDR0 = 0xaa; CMD_ADDR1 = 0x55; *((volatile U16 *)(section_addr)) = 0x30; return check_toggle(); }
下面链接给出一个我调试通过的例子,实现 norflash的 擦除,写,读操作。注意当操作norflash的时候 一定要选择开发板从norflash启动。由于这个比较特殊所以开始的汇编
代码设置 栈的时候一定 要选择合适的,刚开始还是设置成适用于nandflash 的4K位置
ldr sp, =1024*4 @ stack pointer point to 4K显然从norflash启动的时候 4K还映射着norflash ,是无法作为栈使用的,当然程序错误了,憋了很久才找到远因。
由于这个比较特殊,所以就没有把这些代码集成到norflash里面,直接是一个独立的裸机程序。
https://www.dropbox.com/sh/4ljok09eyqb5ke6/AAC4pzHtEUCYQjSlyLr8Jt6ka?dl=0