JZ2440:norflash

采用的器件是:29lv160dbt1-70g

1. 简介:

norflash 的特点是:

  • NOR Flash 的特点是芯片内执行(XIP ,eXecute In Place),这样应用程序可以直接在Flash闪存内运行,不必再把代码读到系统RAM中。
  • NOR 的传输效率很高,在1~4MB的小容量时具有很高的成本效益,但是很低的写入和擦除速度大大影响到它的性能。
  • 由于擦除NOR器件时是以64~128KB的块进行的,执行一个写入/擦除操作的时间为5s
  • NOR Flash 能够像内存一样读操作,不能像内存写入和擦除

采用的器件:

  • 大小是:2M Bytes
  • 从 norflash 启动,norflash 的起始地址是 0
  • 从 nandflash 启动,norflash 的起始地址是 0x0800 0000

2.
硬件:

给的原理图还是有点瞎的:

这个原理图上型号没有跟实际硬件对应上,其中的几个引脚也是标注“错误”,当然作为批量的东西,找一个通用的元件来代替,本质是没有错误的。

板子上芯片的名字:29lv160dbt1-70g

引脚15:RY/BY# (Ready/Busy output)

引脚47:BYTE# (Selects 8-bit or 16-bit mode)

高电平:16位模式,有效的输入输出引脚是 DQ15-DQ0

低电平:8位模式,有效的输入输出引脚是 DQ7-DQ0,DQ8-DQ14处于三态,DQ15(the DQ15 pin is used as an input for the LSB (A-1) address function. )

从原理图上看到,引脚47是高电平,这个开发板采用的是16位模式。

3. 编程:

3.1 读:系统上电,直接能读

U16 read_en29lv160ab(U32 addr)
{
       return *((volatile U16 *)(addr));
}

3.2 软件复位:

norflash不仅能硬件复位,也能软件复位,思路是向任一地址写入复位命令
0xf0:

void reset_en29lv160ab(void)
{
       *((volatile U16 *)0x0) = 0xf0;
}

norflash 的写和擦除:需要 4 到 6 个周期来完成,每一个周期都要把相应的命令写入 norflash 中的某一个寄存器:

3.3 写操作的过程:

  • 第一个周期是把命令 0xaa 写到地址 0x555 的命令寄存器中
  • 第二个周期是把命令 0x55 写到地址 0x2aa 的命令寄存器中
  • 第三个周期是把命令 0xa0 写到地址 0x555 的命令寄存器中
  • 第四个周期是把数据 写到目的地址中

需要知道的几点:

  • norflash 接到的是s3c2440 的bank0 上,norflash 的基地址为 0x00000000
  • 之所以把norflash 的地址向左移一位,是因为 s3c2440 的addr1 链接到了 norflash 的 a0 上

check_toggle函数的作用是:用于判断这次操作是否正确

它的原理是连续两次读取数据总线上的数据,判断数据总线上的第6位数值(DQ6)是否翻转,如果没有翻转则正确,否则还要判断第5位(DQ5),以确定是否是因为超时而引起的翻转。

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

U8 check_toggle()
{
       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;         //正确
}

3.4 擦除操作的过程:

写操作只能使“1”变为“0”,而只有擦除才能使“0”变为“1”。因此在写之前一定要先擦除。

  • 第一个周期是把命令 0xaa 写到地址 0x555 的命令寄存器中
  • 第二个周期是把命令 0x55 写到地址 0x2aa 的命令寄存器中
  • 第三个周期是把命令 0x80 写到地址 0x555 的命令寄存器中
  • 第四个周期是把命令 0xaa 写到地址 0x555 的命令寄存器中
  • 第五个周期是把命令 0x55 写到地址 0x2aa 的命令寄存器中
  • 第六个周期是把命令 0x30 写到要擦除块的首地址
// 输入参数为擦除块的首地址
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();
}

3.5 读取芯片的 ID:

  • 第一个周期是把命令 0xaa 写到地址 0x555 的命令寄存器中
  • 第二个周期是把命令 0x55 写到地址 0x2aa 的命令寄存器中
  • 第三个周期是把命令 0x90 写到地址 0x555 的命令寄存器中
  • 第四个周期是读地址 0x100 中的内容 得到厂商 ID(0x1c)
  • 第四个周期是读地址 0x01 中的内容 得到设备 ID(0x2249)
//读厂商 ID
U32 get_en29lv160ab_id(void)
{
       U32 temp=0;
       CMD_ADDR0 = 0xaa;
       CMD_ADDR1 = 0x55;
       CMD_ADDR0 = 0x90;
       temp = (*(volatile unsigned short *)(flash_base+ (0x100<<1)))<<16;
       temp |= *(volatile unsigned short *)(flash_base + (1<<1));

       return temp;
}

4. 例子:

下面的程序实现了对一块区域进行擦除,写入,并读出的操作,判断写入的数据是否与读出的数据相同:

CFI:是一个记录芯片信息的接口,可以通过特定的命令来访问这些数据

……   ……
U16 buffer[1024];
char cmd;
……   ……

void test_en29lv160ab(void)
{
       U32 temp;
       U8 sta;
       int i;

       for(i=0;i<1024;i++)
       buffer[i]=2*i+1;

       //读ID
temp = get_en29lv160ab_id();
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0xff000000)>>24);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0x00ff0000)>>16);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0x0000ff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0x000000ff));

reset_en29lv160ab();            //这里一定要复位

delay(100);

       //擦除块33
       sta=en29lv160ab_sector_erase(0xf0000);
if(sta == 0)
       {
              while(!(rUTRSTAT0 & 0x2)) ;
              rUTXH0=0xaf;             //擦除出错
       }
       else
       {
              for(i=0;i<1024;i++)
              {
                     sta = en29lv160ab_program(0xf0000+(i<<1),buffer[i]);            //写
                     if(sta == 0)           //写出错
                     {
                            while(!(rUTRSTAT0 & 0x2));
                            rUTXH0=0xbf;
                            break;
                     }
                     delay(200);
              }

              if(sta == 1)
              {
                     for(i=0;i<1024;i++)
                     {
                            if(read_en29lv160ab(0xf0000+(i<<1))!=buffer[i])            //读出错
                            {
                                   while(!(rUTRSTAT0 & 0x2)) ;
                                   rUTXH0=0xcf;
                                   sta = 3;
                                   break;
                            }
                     }
                     if(sta !=3)             //全部操作都正确
                     {
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=0x66;
                     }
              }
       }
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=0x88;                    //结束
}

//简单测试CFI
void test_en29lv160ab_CFI(void)
{
       U16 temp;

       *((volatile U16 *)(0x55<<1+flash_base))=0x98;               //CFI命令
       temp = (*(volatile unsigned short *)(flash_base+ (0x10<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x11<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x12<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x13<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x14<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x15<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x16<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x17<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x18<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x19<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);

       temp = (*(volatile unsigned short *)(flash_base+ (0x1a<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
}

void __irq uartISR(void)
{
       char ch;
       rSUBSRCPND |= 0x1;
       rSRCPND |= 0x1<<28;
       rINTPND |= 0x1<<28;
       ch=rURXH0;

       switch(ch)
       {
              case 0x11:                     //get ID
                     cmd = 1;
                     break;
              case 0x66:                    //test CFI
                     cmd = 6;
                     break;
              case 0x77:                    //test norflash
                     cmd = 7;
                     break;
       }
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=ch;
}

void Main(void)
{
       U32 temp;
       int i;

//uart0 port
       rGPHCON = 0x00faaa;
rGPHUP  = 0x7ff;

//init uart0
rULCON0 = 0x3;
       rUCON0 = 0x5;
rUFCON0 = 0;
       rUMCON0 = 0;
rUBRDIV0 = 26;
       rSRCPND = (0x1<<19)|(0x1<<28);
       rSUBSRCPND = 0x1;
rINTPND = (0x1<<19)|(0x1<<28);
       rINTSUBMSK = ~(0x1);
       rINTMSK = ~((0x1<<19)|(0x1<<28));
       pISR_UART0 = (U32)uartISR;  

       cmd = 0;
       while(1)
       {
              switch(cmd)
              {
                     case 1:                   //读ID
                            cmd = 0;
                            temp = get_en29lv160ab_id();
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0xff000000)>>24);
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0x00ff0000)>>16);
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0x0000ff00)>>8);
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0x000000ff));
                            reset_en29lv160ab();
                            break;

                     case 0x7:
                            cmd = 0;
                            test_en29lv160ab();
                            break;

                     case 0x6:
                            cmd = 0;
                            test_en29lv160ab_CFI();
                            reset_en29lv160ab();
                            break;
              }
}
}

博客参考:s3c2440对norflash的操作

时间: 2024-08-01 11:34:49

JZ2440:norflash的相关文章

JZ2440:nandflash

本篇文章链接:http://blog.csdn.net/qqliyunpeng/article/details/51180276 芯片的型号:K9F2G08U0C 1. 硬件部分: 1.1 简介: 芯片大小:256M Byte 记忆单元阵列:(256M + 8,192K)bit x 8bit 擦写次数比较少:10 万次 数据保留时间:10 年 8个IO 口进行数据和地址的复用,因此,读写的时候要用到多个周期 几个特殊的引脚: 引脚名字 引脚功能 RE# 读使能,低电平有效 WE# 写使能,上升沿

JZ2440:时钟设置

这一节的目标是对板子上的时钟有一个初步的了解.而且能通过初步设置.为我们接下来的程序做准备. 1. 板子上的基本资源: 板载晶振12M 主时钟源和 USB 时钟源都是晶振 2. 手冊中的相关项(按时钟进入的方向): 2.1 时钟的总体结构: 2.2 OM[ 3:2 ]: 从图中我们能够看到OM[] 起到可选择的作用(效果同三八译码器): 模式 OM[3:2] MPLL状态 UPLL 状态 主时钟源 USB 时钟源 00 开启 开启 晶振 晶振 01 开启 开启 晶振 外部时钟 10 开启 开启

arm:判断是从nand启动还是从norflash启动

//int bBootFrmNORFlash(void) :判断是否从norflash启动. //ret==1 :norflash启动 //ret==0 :nandflash启动.(或者是jlink调试的情况.) //该函数应用范围有限,只针对于bootloader最初的stage1. int bBootFrmNORFlash(void) { volatile unsigned int *pdw = (volatile unsigned int *)0; unsigned int dwVal;

存储:

一 关于NORFLASH的片内执行程序: NORFLASH适合片内执行主要是符合了3个特点:读速度快(CPU的取指令).线性存储.位交换概率小(这两条保证要执行的代码区是连续的准确的).因为可片内执行,所以省去CPU取指令的复制和解压,所以体积可以做的很小.NANDFLASH只保证BLOCK0是好的,它只是不适合而不是不能片内执行.http://blog.chinaunix.net/uid-26404697-id-3152290.html 二 RAM与ROM: 2.1RAM:SRAM(静态RAM

buildroot构建项目(五)--- u-boot 2017.11 适配开发板修改 4 ---- 系统启动初始化之三

一.内存控制器 在关闭了MMU和caches 之后 就进入lowlevel_init 函数,对内存控制器进行初始化.lowlevel_init.S (board\samsung\mini2440) 1.1 内存控制器介绍 前面已经看过这张表格了.从这张表格中可以看处,我们的程序代码,不管使用不使用 NAND Flash 都是从0地址开始启动,只不过使用 Nand flash 需要将前4K代码拷贝进 SRAM中去. 1.2 内存控制器的寄存器 1.2.1 总线宽度和等待控制寄存器 每一个 BANK

s5pv210的启动方式详解(一)

普通的PC机中: BIOS+硬盘来配合启动,见笔记“Linux基础知识->PC机的启动流程分析”. 嵌入式系统中: 可以用来作为启动的介质有:NorFlash.SRAM. 不能用来作为启动介质的有:NandFlash(数据和地址复用,需要发送命令才能读写数据,还要初始化寄存器).DRAM(需要初始化控制寄存器才能使用). 由此可以推出在嵌入式系统中,有如下几种启动方式: 1.NorFlash(存放bootloader)+NandFlash(存放操作系统内核镜像) 这种方式就是从NorFlash中

所谓完整的linux系统包括哪些部分呢?【转】

本文转载自:http://www.eeskill.com/article/index/id/1358.html 简介:三部分:bootloader.linux kernel(linux内核).rootfile(根文件系统). 那么这3部分是怎么相互协作来构成这个系统的呢?各自有什么用呢?三者有什么联系?怎么联系?系统的执行流程又是怎么样的呢?搞清楚这个问题你对整个系统的运行就很清楚了,对于下一步制作这个linux系统就打下了另一个重要的根基. 下面是笔者针对网上bootloader.linuxk

ARM上电启动及Uboot代码分析

注意:由于文档是去年写的,内有多个图片,上传图片很麻烦(需要截图另存插入等等),我把文章的PDF版本上传到了CSDN下载资源中.为了给自己赚点积分,所以标价2分,没有积分的同学可以直接留言跟我要,记得留下邮箱. 以下是文章内容,由于我懒得编辑图片了,所以文章看来会很不爽,强烈推荐点击以上红色链接下载pdf版. 文件编号:DCC01 版本号:1.0 ARM上电启动及Uboot代码分析 部    门:                          作    者:                 

u-boot移植(十二)---代码修改---支持DM9000网卡

一.准备工作 1.1 原理图 CONFIG_DM9000_BASE 片选信号是接在nGCS4引脚,若要确定网卡的基地址,则要根据片选信号的接口去确定. 在三星2440的DATASHEET中memory control这一章的Figure 5-1. S3C2440A Memory Map after Reset 已经说明了片选4的地址,如下: 只要发出的信号在 0x20000000--0x28000000 之间,就会使得片选4引脚变为低电平.所以可以确定我们网卡的基地址为0x20000000. C