s3c2440裸机-I2c编程-4-i2c中断服务程序

发布者:心怀感恩最新更新时间:2024-07-04 来源: elecfans关键字:中断服务程序 手机看文章 扫描二维码
随时随地手机看文章

1.顺寻访问(Page Read)

下图的表格,来说明NAND FLASH内部结构,前面2K(0~2047)表示页数据,后边64字节(2048~2111)表示oob。



CPU想读取,第2048个数据,它是哪以一个?

是Page1的第0个字节。CPU使用某个地址访问数据的时候,是在页数据空间来寻址的。

下图为读NAND FLASH的read时序操作:


1.首先需要锁存00命令,nCE、CLE、nWE有效,0x00命令被锁存;

2.此时CLE无效,ALE开始有效,地址被锁存(从NAND FLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址);

3.锁存0x30命令;

4.然后会有一个busy时间段,R/nB为低电平。tRR表示busy状态的持续时间(手册上最小为20ns)。

5.开始锁存数据,nRE使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期(tRC),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据。

下面开始写代码:

当发完命令、地址后再进行读数据前我们知道有一段时间tRR处于busy状态,我们可以通过查询NFSTAT寄存器来确定busy状态有没有结束,是不是已经ready了。


wait_ready函数等待NAND FLASH空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时NAND FLASH是空闲的,我们可以通过该位来判断NAND FLASH是否繁忙。代码如下:


void wait_ready(void)

  {

        while (!(NFSTAT & 1));

  }

nand_read函数为NAND FLASH的读函数,代码如下:


void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)

  {

      int i = 0;

      int page = addr / 2048;

      int col  = addr & (2048 - 1);


      nand_select();


      while (i < len)

      {

          /* 发出00h命令 */

          nand_cmd(00);


          /* 发出地址 */

          /* col addr */

          nand_addr_byte(col & 0xff);

          nand_addr_byte((col>>8) & 0xff);


          /* row/page addr */

          nand_addr_byte(page & 0xff);

          nand_addr_byte((page>>8) & 0xff);

          nand_addr_byte((page>>16) & 0xff);


          /* 发出30h命令 */

          nand_cmd(0x30);


          /* 等待就绪 */

          wait_ready();


          /* 读数据 */

          for (; (col < 2048) && (i < len); col++)

          {

                buf[i++] = nand_data();         

          }


          col = 0;

          page++;

      }


      nand_deselect();  

  }

我们看到每read一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。


软件上如何自动区分是nand启动还是nor启动?


在init.c文件中,加上如下代码,用来判断所使用的FLASH是NOR FLASH还是NAND FLASH。代码如下:


/*我们知道nand启动0地址对应片内SRAM,可以像内存一样的写0地址;nor启动,0地址对应nor,nor不能像内存一样的写地址,

**所以往0地址写入数据成功表示nand启动,写不成功表示nor启动

*/

int isBootFromNorFlash(void)

{

    volatile unsigned int *p = (volatile unsigned int *)0;

    unsigned int val = *p;


    *p = 0x12345678;

    if (*p == 0x12345678)

    {

        /* 写成功, 对应nand启动 */

        *p = val;

        return 0;

    }

    else

    {

        return 1;

    }

}

下面是代码重定位时可以自动区分nand和nor启动,无论是nand启动还是nor启动,都能将程序重定位到sdram中去。


void copy2sdram(void)

  {

        /* 要从lds文件中获得 __code_start, __bss_start

        * 然后从0地址把数据复制到__code_start

    */


    extern int __code_start, __bss_start;


    volatile unsigned int *dest = (volatile unsigned int *)&__code_start;

    volatile unsigned int *end = (volatile unsigned int *)&__bss_start;

    volatile unsigned int *src = (volatile unsigned int *)0;

    unsigned int len = (unsigned int)(&__bss_start) - (unsigned int)(&__code_start);


    if (isBootFromNorFlash())

    {

          while (dest < end)

          {

                *dest++ = *src++;

      }

    }

    else

    {

        nand_init();

        nand_read((unsigned int)src, dest, len);

    }

  }

2.擦除(block erase)

block erase时序图的过程大致如下:

1.首先发送0x60命令

2.发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可)

3.发送0xd0,执行擦除动作

4.然后会有一个busy时间段,R/nB为低电平

5.发送0x70命令,用来读取状态

6.判断NFDATA寄存器的第0位是否擦除成功

代码实现如下:


int nand_erase(unsigned int addr, unsigned int len)

{

    int page = addr / 2048;


    if (addr & (0x1FFFF))

    {

        printf('nand_erase err, addr is not block alignnr');

        return -1;

    }


    if (len & (0x1FFFF))

    {

        printf('nand_erase err, len is not block alignnr');

        return -1;

    }


    nand_select();


    while (1)

    {

        page = addr / 2048;


        nand_cmd(0x60);


        /* page addr */

        nand_addr_byte(page & 0xff);

        nand_addr_byte((page>>8) & 0xff);

        nand_addr_byte((page>>16) & 0xff);


        nand_cmd(0xD0);


        wait_ready();


        nand_cmd(0x70);

        if (nand_data()&0x1)

        {

            printf('nand_erase err, at addr:0x%xnr', addr);

            return -1;

        }


        len -= (128*1024);

        if (len == 0)

            break;

        addr += (128*1024);

    }


    nand_deselect();    

    return 0;

}

3.顺序写(page write)

往NAND FLASH写数据时,只需要把要写的数据复制给NFDATA寄存器即可。代码如下:


void nand_w_data(unsigned char val)

{

    NFDATA = val;

}

page write的写时序图如下:

1.首先发送0x80命令

2.发送地址(5个周期)

3.发送数据

4.发送0x10命令,执行烧写动作

4.然后会有一个busy时间段,R/nB为低电平

5.发送0x70命令,用来读取状态

6.判断NFDATA寄存器的第0位是否烧写成功


void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)

{

    int page = addr / 2048;

    int col  = addr & (2048 - 1);

    int i = 0;


    nand_select();


    while (1)

    {

        nand_cmd(0x80);


        /* 发出地址 */

        /* col addr */

        nand_addr_byte(col & 0xff);

        nand_addr_byte((col>>8) & 0xff);


        /* row/page addr */

        nand_addr_byte(page & 0xff);

        nand_addr_byte((page>>8) & 0xff);

        nand_addr_byte((page>>16) & 0xff);


        /* 发出数据 */

        for (; (col < 2048) && (i < len); col++)  //???还需确认

        {

            nand_w_data(buf[i++]);

        }


        nand_cmd(0x10);

        wait_ready();


        nand_cmd(0x70);

        if (nand_data()&0x1)

        {

            printf('nand_write err, at page:0x%x, addr:0x%xnr', page, page<<11);

            return -1;

        }


        if (i == len)

            break;


        /* 开始下一个循环page */

        col = 0;

        page++; 

    }


    nand_deselect();    

}

我们看到每写一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。


4.测试

void do_erase_nand_flash(void)

{

    unsigned int addr;


    /* 获得地址 */

    printf('Enter the address of sector to erase: ');

    addr = get_uint();


    printf('erasing ...nr');

    nand_erase(addr, 128*1024);

}


void do_read_nand_flash(void)

{

    unsigned int addr;

    volatile unsigned char *p;

    int i, j;

    unsigned char c;

    unsigned char str[16];

    unsigned char buf[64];


    /* 获得地址 */

    printf('Enter the address to read: ');

    addr = get_uint();


    nand_read(addr, buf, 64);

    p = (volatile unsigned char *)buf;


    printf('Data : nr');

    /* 长度固定为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('nr');

    }

}


void do_write_nand_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 ...nr');

    nand_write(addr, str, strlen(str)+1);

[1] [2]
关键字:中断服务程序 引用地址:s3c2440裸机-I2c编程-4-i2c中断服务程序

上一篇:s3c2440裸机-LCD编程-1-LCD硬件原理
下一篇:s3c2440裸机-nandflash编程-3-初始化及识别

推荐阅读最新更新时间:2024-11-09 10:18

s3c2440调试nandflash裸机程序遇到的问题
按照前面sdram的代码,启动代码里面关看门狗、初始化存储控制器(主要是BANK0的Norflash和BANK6的SDRAM)、设置栈到SDRAM的最高地址,text段的数据直接从Norflash里面取。 代码如下: head.S @************************************************************************* @ File:head.S @ 功能:设置SDRAM,将栈设置到SDRAM,然后继续执行 @************************************************************************* .
[单片机]
s3c2440裸机-代码重定位-2-编程实现
代码重定位(2.编程实现代码重定位) 1.引入链接脚本 我们上一节讲述了为什么要重定位代码,那么怎么去重定位代码呢? 上一节我们发现 arm-linux-ld -Ttext 0 -Tdata 0x30000000 这种方式编译出来的bin文件有800多M,这肯定是不行的,那么需要怎么把.data段重定位到sdram呢? 可以通过AT参数指定.data段在编译时的存放位置,我们发现这样指定太不方便了,而且不好确定要放在bin文件的哪个位置。这里就要引入链接脚本,它可以帮我们解决这个不必要的麻烦。 链接脚本格式 格式如下图: 我们来看一个具体的例子: SECTIONS { . = 0x00000000; //表示当前
[单片机]
s3c2440裸机-电阻触摸屏-1-电阻触摸屏原理
一、电阻触摸屏原理 触摸屏包含上下叠合的两个透明层,一般覆盖在lcd表面,两个透明层是由均匀的电阻介质组成,如下图。 当触摸屏表面受到的压力(如通过笔尖或手指进行按压)足够大时,顶层与底层之间的薄膜会产生接触,此时会形成x方向和y方向的坐标。那么x,y坐标的值是怎么得来的呢?本质上就是通过ADC转换得来的。详解如下: 触摸屏的等效电路可以看成如下图: 计算触点的X,Y坐标分为如下两步(见下图): 1.计算Y坐标: 在Y+电极施加驱动电压Vdrive, Y-电极接地,由于上下两层膜形成触点,X+做为触点的引出端,测量得到接触点的电压,触点电压与Vdrive电压之比等于触点Y坐标与屏高度之比。如下图: 2.计算X坐标:
[单片机]
s3c2440裸机-内存控制器1-内存控制器的原理
1.内存接口概念 S3C2440是个片上系统,有GPIO控制器(接有GPIO管脚(GPA-GPH)),有串口控制器 (接有TXD RXD引脚),有memory controller内存控制器,有Nand控制器等... 1.不同类型的控制器: (1)GPIO控制器属于门电路,不涉及到时序,相对简单。 (2)串口控制器属于协议类接口,类似的协议类接口还有iic、iis、spi等。 (3)前面的GPIO/门电路接口、协议类接口,都不会把地址输出到外部设备,仅仅只是将地址写入到相应的控制器。 接下来的内存类接口,会把地址输出到外部,cpu将地址写入内存控制器,内存控制器还需访问外部设备,比如NorFlash、网卡、SDRAM。 2.
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved