Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1184902
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-02 11:49:28

def.h文件

点击(此处)折叠或打开

  1. #ifndef __DEF_H__
  2. #define __DEF_H__

  3. #define U32 unsigned int
  4. #define U16 unsigned short
  5. #define S32 int
  6. #define S16 short int
  7. #define U8 unsigned char
  8. #define    S8 char

  9. #endif
head.S文件

点击(此处)折叠或打开

  1. @*************************************************************************
  2. @ File:step1.S bootloader第1阶段的代码
  3. @ 功能:关watch_dog,初始化clock,初始化SDRAM,初始化NAND Flash,代码重定位到SDRAM
  4. @ 然后跳到SDRAM,继续执行第2阶段的代码
  5. @*************************************************************************

  6. .text @text表示是代码段
  7. .global _start
  8. _start: /*_start在这里表示.text段的链接地址。对应于:链接脚本中指定的.text段的起始地址=0x31000000*/
  9.                                             @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
  10.             ldr sp, =0x34000000 @设置栈
  11.             bl disable_watch_dog @关WATCH DOG
  12.             bl clock_init @设置MPLL,改变FCLK、HCLK、PCLK
  13.             bl memsetup @初始化SDRAM
  14.             @bl init_uart @初始化UART,如果代码比较大,这个函数不一定会在4k内,所以在main函数中调用
  15.             bl nand_init @初始化NAND Flash

  16.                                                                                         @ 复制nand中4k后的代码到SDRAM中
  17.                         ldr r0, =_start                                    @ 1. 目标地址 = 0x31000000,这是在SDRAM中的链接地址
  18.                         ldr r1, =0x0                                        @ 2. 源地址 = 0,运行地址在SDRAM中的代码保存在NAND Flash 0地址开始处,或者norflash的0地址处
  19.                         ldr r2, =__bss_start                        @ 3. 复制长度
  20.                         sub r2, r2, r0
  21.                  bl CopyCode2SDRAM @ 调用C函数CopyCode2SDRAM
  22.                 
  23.                     bl clean_bss         @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段

  24.             ldr lr, =my_loop @设置返回地址
  25.             ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
  26. my_loop:
  27.             b my_loop
  28.             


  29. /*
  30. 关于是否可用 @bl init_uart 的问题
  31. makefile文件中 是 objs := head.o init.o setup.o nand.o leds.o uart.o 时,在head.S文件中 必须使用 : @bl init_uart,在main函数中调用 init_uart
  32. makefile文件中 是 objs := head.o uart.o init.o setup.o nand.o leds.o 时,在head.S文件中 可以使用 : bl init_uart
  33. 原因 :
  34. 本程序生成的 nand.bin有6.6K左右,而bl的跳转范围是4K,
  35. 生成目标文件链接时,会按照 makefile 中 给定的.o文件的顺序来链接所有.o文件,所以head.o和uart.o的距离可能超过4K。
  36. 改变 makefile 中 给定的.o文件的顺序,head.o和uart.o的距离又可能在4K以内。
  37. 注意 :
  38. 1,head.S中,使用bl调用的函数所在的.o文件,在makefile中必须和head.o很近(<4K),pc指定的main函数的地址,可以很远(>4K)
  39. 比较远的函数,可以在main函数中去调用,比如 : init_uart();
  40. 2,对于必须在 head.S中 使用bl调用的,比如 : bl CopyCode2SDRAM。如果距离 head.o 很远(>4K)
  41. 就必须把 CopyCode2SDRAM 函数所在的文件拆开来,本程序没有超过4K,所以nand中相关函数都在一个文件中。
  42. */
init.c文件

点击(此处)折叠或打开

  1. #include "init.h"

  2. /*
  3.  * 关闭WATCHDOG,否则CPU会不断重启
  4.  */
  5. void disable_watch_dog(void)
  6. {
  7.     WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
  8. }

  9. /*
  10.  * 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
  11.  * 有如下计算公式:
  12.  * S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
  13.  * 其中: m = MDIV + 8 = 92+8, p = PDIV + 2 = 1+2, s = SDIV = 2
  14.  * 对于本开发板,Fin = 12MHz
  15.  * 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:2:4,
  16.  * FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
  17.  */
  18. void clock_init(void)
  19. {
  20.     // LOCKTIME = 0x00ffffff; // 使用默认值即可
  21.     CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
  22.     /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
  23. __asm__(
  24.     "mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
  25.     "orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
  26.     "mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
  27.     );

  28.     MPLLCON = S3C2440_MPLL_200MHZ;
  29.     /* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
  30.     并不保证你的任何设置都是合适的。所以,如果想要工作在200MHz,还是按照vivi的推荐值((0x5c<<12)|(0x01<<4)|(0x02))即可。
  31.     */
  32. }

  33. /*
  34. s3c2440的bank6和bank7支持128M B,注意,单位是 Byte.
  35. TQ2440原理图的HY57V561620FTP是 4bank * 4M * 16bit,256M,注意,单位是 bit,也就是 32M Byte.
  36. U6+U7 : 共64M
  37. */
  38. void memsetup(void)
  39. {
  40.     volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

  41.     // 可生成”位置无关的代码”,使得这个函数可以在被复制到
  42.     // SDRAM之前就可以在steppingstone中运行
  43.     // 存储控制器13个寄存器的值
  44.     p[0] = 0x22011110; //BWSCON
  45.     p[1] = 0x00000700; //BANKCON0
  46.     p[2] = 0x00000700; //BANKCON1
  47.     p[3] = 0x00000700; //BANKCON2
  48.     p[4] = 0x00000700; //BANKCON3
  49.     p[5] = 0x00000700; //BANKCON4
  50.     p[6] = 0x00000700; //BANKCON5
  51.     p[7] = 0x00018005; //BANKCON6
  52.     p[8] = 0x00018005; //BANKCON7
  53.     
  54.                                             // REFRESH,
  55.                                             // HCLK=12MHz: 0x008C07A3,
  56.                                             // HCLK=100MHz: 0x008C04F4
  57.     p[9] = 0x008C04F4;
  58.     p[10] = 0x000000B1; //BANKSIZE
  59.     p[11] = 0x00000030; //MRSRB6
  60.     p[12] = 0x00000030; //MRSRB7
  61. }

  62. void clean_bss(void)
  63. {
  64.     extern int __bss_start, __bss_end; /*这2个在链接脚本中定义的变量*/
  65.     int *p = &__bss_start;
  66.     
  67.     for (; p < &__bss_end; p++)
  68.         *p = 0;
  69. }
init.h文件

点击(此处)折叠或打开

  1. #ifndef _INIT_H_
  2. #define _INIT_H_

  3. /**************************LED相关寄存器*******************************/
  4. #define GPBCON (*(volatile unsigned long *)0x56000010)
  5. #define GPBDAT (*(volatile unsigned long *)0x56000014)
  6. #define GPBUP (*(volatile unsigned long *)0x56000018)
  7. /*注意:这个程序没有mmu,所以寄存器的地址是物理地址*/

  8. #define GPB5_OUT (1<<(5*2))
  9. #define GPB6_OUT (1<<(6*2))
  10. #define GPB7_OUT (1<<(7*2))
  11. #define GPB8_OUT (1<<(8*2))

  12. #define GPB5_ON (~(1<<5))
  13. #define GPB6_ON (~(1<<6))
  14. #define GPB7_ON (~(1<<7))
  15. #define GPB8_ON (~(1<<8))

  16. #define GPB5_OFF (1<<5)
  17. #define GPB6_OFF (1<<6)
  18. #define GPB7_OFF (1<<7)
  19. #define GPB8_OFF (1<<8)
  20. /*********************************************************/

  21. /* WATCHDOG寄存器 */
  22. #define WTCON (*(volatile unsigned long *)0x53000000)
  23. /* SDRAM寄存器 */
  24. #define     MEM_CTL_BASE        0x48000000


  25. /*系统时钟相关寄存器*/
  26. #define    MPLLCON        (*(volatile unsigned long *)0x4c000004)
  27. #define    CLKDIVN        (*(volatile unsigned long *)0x4c000014)
  28. #define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))

  29. void disable_watch_dog();
  30. void clock_init(void);
  31. void memsetup();
  32. void clean_bss(void);

  33. #endif
led.c文件

点击(此处)折叠或打开

  1. #include "def.h"
  2. #include "init.h"
  3. #include "nand.h"
  4. #include "uart.h"
  5. #include "setup.h"

  6. unsigned char nand_test();

  7. /*
  8. *功能:延时函数
  9. */
  10. static void wait(volatile unsigned int num)
  11. {
  12.     unsigned int i = 0;
  13.     unsigned int j = 0;
  14.     for(i=0; i<1000; i++)
  15.         for(j=0; j<num; j++);
  16. }

  17. int main()
  18. {
  19.     icache_enable();
  20.     init_uart();
  21.     
  22.     uart_sendString("\n\n");
  23.     uart_sendString("My nandtest start:\n\n");
  24.     
  25.     nand_test();

  26.   GPBCON = (GPB5_OUT | GPB6_OUT | GPB7_OUT | GPB8_OUT);
  27.   GPBUP = 0x1e0;
  28.   
  29.   while(1)
  30.   {
  31.       GPBDAT = (GPB5_ON & GPB6_ON & GPB7_ON & GPB8_ON);
  32.       wait(1000);
  33.       GPBDAT = (GPB5_OFF | GPB6_OFF | GPB7_OFF | GPB8_OFF);
  34.       wait(1000);
  35.   }

  36.     return 0;
  37. }



  38. unsigned char nand_test()
  39. {
  40.         unsigned char buf[128];
  41.         unsigned int i = 0;
  42.         unsigned char ch;
  43.       
  44.         uart_sendString("1*********************************\n");
  45.         ch = nand_read_random(1, 0);
  46.         uart_sendByte(':');
  47.         uart_sendByte_hex((unsigned long)ch);
  48.         uart_sendByte('\n');
  49.         ch = nand_read_random(1, 1);
  50.         uart_sendByte(':');
  51.         uart_sendByte_hex((unsigned long)ch);
  52.         uart_sendByte('\n');
  53.         ch = nand_read_random(1, 2);
  54.         uart_sendByte(':');
  55.         uart_sendByte_hex((unsigned long)ch);
  56.         uart_sendByte('\n');
  57.         ch = nand_read_random(1, 3);
  58.         uart_sendByte(':');
  59.         uart_sendByte_hex((unsigned long)ch);
  60.         uart_sendByte('\n');
  61.         
  62.         ch = nand_read_random(1, 4);
  63.         uart_sendByte(':');
  64.         uart_sendByte_hex((unsigned long)ch);
  65.         uart_sendByte('\n');
  66.         ch = nand_read_random(1, 5);
  67.         uart_sendByte(':');
  68.         uart_sendByte_hex((unsigned long)ch);
  69.         uart_sendByte('\n');
  70.         ch = nand_read_random(1, 6);
  71.         uart_sendByte(':');
  72.         uart_sendByte_hex((unsigned long)ch);
  73.         uart_sendByte('\n');
  74.         ch = nand_read_random(1, 7);
  75.         uart_sendByte(':');
  76.         uart_sendByte_hex((unsigned long)ch);
  77.         uart_sendByte('\n');

  78.         uart_sendString("1.1******\n");
  79.       uart_sendString("******\n");
  80.       uart_sendString("******\n");

  81.         ch = nand_read_random(1, 2040);
  82.         uart_sendByte(':');
  83.         uart_sendByte_hex((unsigned long)ch);
  84.         uart_sendByte('\n');
  85.         ch = nand_read_random(1, 2041);
  86.         uart_sendByte(':');
  87.         uart_sendByte_hex((unsigned long)ch);
  88.         uart_sendByte('\n');
  89.         ch = nand_read_random(1, 2042);
  90.         uart_sendByte(':');
  91.         uart_sendByte_hex((unsigned long)ch);
  92.         uart_sendByte('\n');
  93.         ch = nand_read_random(1, 2043);
  94.         uart_sendByte(':');
  95.         uart_sendByte_hex((unsigned long)ch);
  96.         uart_sendByte('\n');
  97.         
  98.         ch = nand_read_random(1, 2044);
  99.         uart_sendByte(':');
  100.         uart_sendByte_hex((unsigned long)ch);
  101.         uart_sendByte('\n');
  102.         ch = nand_read_random(1, 2045);
  103.         uart_sendByte(':');
  104.         uart_sendByte_hex((unsigned long)ch);
  105.         uart_sendByte('\n');
  106.         ch = nand_read_random(1, 2046);
  107.         uart_sendByte(':');
  108.         uart_sendByte_hex((unsigned long)ch);
  109.         uart_sendByte('\n');
  110.         ch = nand_read_random(1, 2047);
  111.         uart_sendByte(':');
  112.         uart_sendByte_hex((unsigned long)ch);
  113.         uart_sendByte('\n');
  114.         
  115.         uart_sendString("1.2******\n");
  116.       uart_sendString("******\n");
  117.       uart_sendString("******\n");
  118.       
  119.       ch = nand_read_random(1, 2048);
  120.         uart_sendByte(':');
  121.         uart_sendByte_hex((unsigned long)ch);
  122.         uart_sendByte('\n');
  123.         ch = nand_read_random(1, 2049);
  124.         uart_sendByte(':');
  125.         uart_sendByte_hex((unsigned long)ch);
  126.         uart_sendByte('\n');
  127.         ch = nand_read_random(1, 2050);
  128.         uart_sendByte(':');
  129.         uart_sendByte_hex((unsigned long)ch);
  130.         uart_sendByte('\n');
  131.         ch = nand_read_random(1, 2051);
  132.         uart_sendByte(':');
  133.         uart_sendByte_hex((unsigned long)ch);
  134.         uart_sendByte('\n');
  135.         
  136.         uart_sendString("1.3******\n");
  137.       uart_sendString("******\n");
  138.       uart_sendString("******\n");
  139.       
  140.       nand_write_random(2, 2048, 0xAA);
  141.       nand_write_random(2, 2049, 0xBB);
  142.       nand_write_random(2, 2050, 0xCC);
  143.       nand_write_random(2, 2051, 0xDD);

  144.       ch = nand_read_random(2, 2048);
  145.         uart_sendByte(':');
  146.         uart_sendByte_hex((unsigned long)ch);
  147.         uart_sendByte('\n');
  148.         ch = nand_read_random(2, 2049);
  149.         uart_sendByte(':');
  150.         uart_sendByte_hex((unsigned long)ch);
  151.         uart_sendByte('\n');
  152.         ch = nand_read_random(2, 2050);
  153.         uart_sendByte(':');
  154.         uart_sendByte_hex((unsigned long)ch);
  155.         uart_sendByte('\n');
  156.         ch = nand_read_random(2, 2051);
  157.         uart_sendByte(':');
  158.         uart_sendByte_hex((unsigned long)ch);
  159.         uart_sendByte('\n');

  160.         uart_sendString("1.4******\n");
  161.       uart_sendString("******\n");
  162.       uart_sendString("******\n");

  163.       nand_write_random(1, 2048, 0xDD);
  164.       nand_write_random(1, 2049, 0xCC);
  165.       nand_write_random(1, 2050, 0xBB);
  166.       nand_write_random(1, 2051, 0xAA);

  167.       ch = nand_read_random(1, 2048);
  168.         uart_sendByte(':');
  169.         uart_sendByte_hex((unsigned long)ch);
  170.         uart_sendByte('\n');
  171.         ch = nand_read_random(1, 2049);
  172.         uart_sendByte(':');
  173.         uart_sendByte_hex((unsigned long)ch);
  174.         uart_sendByte('\n');
  175.         ch = nand_read_random(1, 2050);
  176.         uart_sendByte(':');
  177.         uart_sendByte_hex((unsigned long)ch);
  178.         uart_sendByte('\n');
  179.         ch = nand_read_random(1, 2051);
  180.         uart_sendByte(':');
  181.         uart_sendByte_hex((unsigned long)ch);
  182.         uart_sendByte('\n');
  183.       uart_sendString("1*********************************\n");


  184.       nand_read2(buf, 2048, 16); //注意:本程序是从nand的4096地址开始放第2部分代码的
  185.         uart_sendString("2*********************************\n");
  186.     for(i=0; i<16; i++)
  187.     {
  188.             uart_sendByte(':');
  189.             uart_sendByte_hex((unsigned long)buf[i]);
  190.             uart_sendByte('\n');
  191.       }
  192.       uart_sendString("2*********************************\n");

  193.       nand_read2(buf, 2048+2032, 16); //注意:本程序是从nand的4096地址开始放第2部分代码的
  194.         uart_sendString("3*********************************\n");
  195.     for(i=0; i<16; i++)
  196.     {
  197.             uart_sendByte(':');
  198.             uart_sendByte_hex((unsigned long)buf[i]);
  199.             uart_sendByte('\n');
  200.       }
  201.       uart_sendString("3*********************************\n");
  202.       

  203. /*    
  204.       nand_read2(buf, 4096+2035, 16); //注意:本程序是从nand的4099地址开始放第2部分代码的
  205.         uart_sendString("5*********************************\n");
  206.     for(i=0; i<16; i++)
  207.     {
  208.             uart_sendByte(':');
  209.             uart_sendByte_hex((unsigned long)buf[i]);
  210.             uart_sendByte('\n');
  211.       }
  212.       uart_sendString("5*********************************\n");
  213.       

  214.         nand_read2(buf, 4096+2035, 13+64+16); //注意:本程序是从nand的4099地址开始放第2部分代码的
  215.         uart_sendString("6*********************************\n");
  216.     for(i=0; i<13+64+16; i++)
  217.     {
  218.             uart_sendByte(':');
  219.             uart_sendByte_hex((unsigned long)buf[i]);
  220.             uart_sendByte('\n');
  221.       }
  222.       uart_sendString("6*********************************\n");
  223. */

  224.       return 0x66;
  225. }
makefile文件

点击(此处)折叠或打开

  1. objs := head.o init.o setup.o nand.o leds.o uart.o

  2. nand.bin: $(objs)
  3.     arm-linux-ld -Tnand.lds -o nand_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
  4.     arm-linux-objcopy -O binary -S nand_elf $@
  5.     arm-linux-objdump -D -m arm nand_elf > nand.dis

  6. %.o:%.c
  7.     arm-linux-gcc -Wall -O2 -c -o $@ $<

  8. %.o:%.S
  9.     arm-linux-gcc -Wall -O2 -c -o $@ $<

  10. clean:
  11.     rm -f nand.bin nand_elf nand.dis *.o
nand.c文件

点击(此处)折叠或打开

  1. #include "nand.h"
  2. #include "def.h"
  3. #include "uart.h"

  4. /*
  5. TQ2440开发板使用的nandflash芯片是 : K9F2G08U0A
  6. */

  7. /*
  8. TQ2440开发板使用的norflash芯片是EN29LV160AB-70TCP
  9. *函数功能:判断是nand启动,还是nor启动
  10. *返回值:0:nand启动,1:nor启动
  11. */
  12. int isBootFromNorFlash(void)
  13. {
  14.     volatile int *p = (volatile int *)0;
  15.     int val;

  16.     val = *p;                    /*先把0地址的值,保存到中间变量val*/
  17.     *p = 0x12345678;    /*试图向0地址赋值*/
  18.     if(*p == 0x12345678) /*向0地址写数据成功*/
  19.     {
  20.         /* 写成功, 是nand启动 */
  21.         *p = val;
  22.         return 0;
  23.     }
  24.     else    /*向0地址写数据失败*/
  25.     {
  26.         /* NOR不能像内存一样写 */
  27.         return 1;
  28.     }
  29. }

  30. /*
  31. *功能:重启
  32. */
  33. void nand_reset()
  34. {
  35.     /*每操作NandFlash之前必须先片选*/
  36.     NF_CE_L();
  37.     /*发送命令之前要先清除RB*/
  38.     NF_CLEAR_RB();
  39.     /*向NandFlash写命令:即发送复位命令*/
  40.     NF_CMD(CMD_RESET);
  41.     /*注意:命令执行期间,NandFlash存储器忙,所以一直要等待为1:不忙*/
  42.     NF_WAIT_RB();
  43.     /*关闭片选*/
  44.     NF_CE_H();
  45. }

  46. /*
  47. *功能:NANDFLASH初始化
  48. *注意:1,本程序使用的是ARM9 2440处理器,需要设置 NFCONF,NFCONT这2个寄存器
  49. * 2,NandFlash的芯片是K9F2G08U0A。由芯片手册可知:
  50. * 容量:(256M+8M)字节,后面的8M叫OOB空间,坏块管理会用到它。
  51. * 8bit
  52. * 2K个块,每个块有64个page页
  53. * 一个page页 =(2K+64)字节
  54. * 所以,一个块 = 64页 = 64*(2K+64)字节 =(128K+2K)字节
  55. * 一个Nand器件 = 2K个块 = 2K*64个page页 = 128K页
  56. * 一个Nand器件 = 128K页 = 128K*(2K+64)字节 = (256M+8M)字节
  57. * 3,片内内存+SDRAM+网卡+寄存器:是由CPU统一编址的,由CPU发出地址。
  58. * 但是NandFlash不是由CPU统一编址的,所以NandFlash的0地址和片内内存的0地址,不是一回事。
  59. * 4,NandFlash中的OOB不参与编址,读取NandFlash上的2049这个地址,不是读取的0页上的地址,是1页上的。
  60. * 5,NandFlash控制器的各个控制引脚的时序,不需要程序员来驱动这些引脚,
  61. * 程序员只需要设置相关寄存器之后,控制器会自动发出控制信号。
  62. */
  63. void nand_init()
  64. {
  65.     //char ret;
  66.     //char ch;
  67.     /*NANDFLASH配置寄存器 : NFCONF
  68.     [13:12]=01:时序图中CLE,ALE持续值设置,1个HCLK时钟周期,如HCLK是100Mhz,则,1个HCLK时钟周期=10ns。
  69.     [10:8]=011:时序图中TWRPH0持续值设置,3个HCLK时钟周期
  70.     [6:4]=100:时序图中TWRPH1持续值设置,0个HCLK时钟周期
  71.     */
  72.     rNFCONF = ((TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4));
  73.     /*NANDFLASH控制寄存器 : NFCONT
  74.     其他位基本都是初始值或者不需要设置
  75.     [1]=1:禁止片选,CE引脚输出为1
  76.     [0]=1:MODE位,使能NandFlash控制器,命令使能CLE引脚设置为1
  77.     */
  78.     //rNFCONT = (1<<4)|(1<<1)|(1<<0);
  79.     rNFCONT =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
  80.     //非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区(OOB区)ECC,使能nandflash片选及控制器
  81.     
  82.     nand_reset();
  83.     
  84.     /* 下面串口打印函数不能用了,因为连接脚本将nand.o放在4k内,uart.o在4k外,不能调用了
  85.     uart_sendByte('\n');
  86.     uart_sendByte('w');
  87.     uart_sendByte('x');
  88.     uart_sendByte('c');
  89.     uart_sendByte('\n');
  90.     
  91.     ret = nand_readId();
  92.     uart_sendByte('\n');
  93.     uart_sendString("nand_readId = ");
  94.     uart_sendByte_hex(ret);
  95.     uart_sendByte('\n');
  96.     
  97.     uart_sendString("please input:\n");
  98.     ch = uart_getch();
  99.     uart_sendString("your input is:");
  100.     uart_sendByte(ch);
  101.     uart_sendByte('\n');
  102.     */
  103.     
  104.     return ;
  105. }


  106. /*功能:读取芯片的ID号
  107. *注意:查看NandFlash的相关配置是否正确,可以去检查芯片的ID号。如果ID读取正确了,则说明配置正确了。
  108. *第1个周期:厂商编号 : 0xEC
  109. *第2个周期:设备编号:相同型号的芯片,有相同的ID号 : 0xDA,这个必须正确
  110. *第3个周期:内部芯片编号 : 0x10
  111. *第4个周期:页的大小 : 0x95
  112. *第5个周期:一个page另外带的融错空间的大小 : 0x44
  113. */
  114. unsigned char nand_readId()
  115. {
  116.     int i;
  117.     unsigned char cyc1;
  118.     unsigned char cyc2;
  119.     unsigned char cyc3;
  120.     unsigned char cyc4;
  121.     unsigned char cyc5;
  122.     //片选
  123.     NF_CE_L();
  124.     //清除RB
  125.     NF_CLEAR_RB();
  126.     //发送读ID命令
  127.     NF_CMD(CMD_READID);
  128.     //发送地址值:需要读取这个地址上的值,读ID号,规定就是这个地址,看的时序图,只需发送一个地址.
  129.     NF_ADDR(0x0);
  130.     for(i=0; i<1000; i++);
  131.     //从nandflash中读取数据:读5次,看时序图
  132.     //注意:每次只能读取8位数据,保存在数据寄存器中。
  133.     cyc1 = NF_RDDATA8();
  134.     for(i=0; i<100; i++);
  135.     cyc2 = NF_RDDATA8();
  136.     for(i=0; i<100; i++);
  137.     cyc3 = NF_RDDATA8();
  138.     for(i=0; i<100; i++);
  139.     cyc4 = NF_RDDATA8();
  140.     for(i=0; i<100; i++);
  141.     cyc5 = NF_RDDATA8();
  142.     for(i=0; i<100; i++);
  143.     
  144.     //等待命令执行完毕
  145.     NF_WAIT_RB();
  146.     //关闭片选
  147.     NF_CE_H();
  148.     
  149.     return cyc2;
  150. }


  151. /*功能 : 发出地址
  152. *注意: 这个地址,不是页编号,是具体的读取NandFlash的起始地址,包括页内地址
  153. */
  154. void write_addr(unsigned long addr)
  155. {
  156.         //写地址(要读取数据的起始地址) :
  157.         //包括 :(页内)地址,是低位地址,共A0~A11,在具体的page页内寻址。一个page页=(2K+64)字节
  158.         //()地址,是高位地址,共A12~A28,可以寻址到page页。一个Nand器件=2K*64个page页 = 128K页
  159.         // 高位地址中,A12~A17,可以寻址到block块下面的page页。一个block块=64个page页
  160.         // 高位地址中,A18~A28,可以寻址到block块。一个Nand器件=2K个block块
  161.         //要写全部的5个周期的地址
  162.     
  163.         //32位的page_number : 低29位有效 :
  164.         //| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16| 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  165.      // 对应最后3个地址周期的页内地址的高11位 :
  166.         //|A28|A27|A26|A25|A24|A23|A22|A21|A20|A19|A18|A17|A16|A15|A14|A13|A12|A11|A10|A09|A08|A07|A06|A05|A04|A03|A02|A01|A00|
  167.         //29位在5个地址周期中应是如下的分布 :
  168.         //| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
  169.         //| L | L | L | L | L | L | L | A28 | //L表示低电平,即为0
  170.         //| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
  171.         //| A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 |
  172.         //| L | L | L | L | A11 | A10 | A9 | A8 |
  173.         //| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
  174.     
  175.     unsigned long i;
  176.     unsigned long col, page;

  177.         col = addr & (2048 -1); //截取低位 : 取2048的原因:本程序使用的nand芯片的一个page页是2048字节
  178.         page = addr / 2048; //截取高位 : 第page页,从第0页开始

  179.     //因为第11位已经丢弃了,所以A11肯定是0,所以这里其实只能寻址2047以内的页的main区,
  180.     //不可能寻址到2047以后的页内spare区,超过2047的地址,已经表示下一页的地址了。
  181.     NF_ADDR(col & 0xFF); //(页内)地址 A0~A7
  182.     for(i=0; i<100; i++);
  183.         NF_ADDR((col >> 8) & 0x0F);     //(页内)地址 A8~A11
  184.         for(i=0; i<100; i++);
  185.         
  186.         NF_ADDR(page & 0xFF); //()地址 A12~A19
  187.         for(i=0; i<100; i++);
  188.         NF_ADDR((page >> 8) & 0xFF); //()地址 A20~A27
  189.         for(i=0; i<100; i++);
  190.         NF_ADDR((page >> 16) & 0x01); //()地址 A28
  191.         for(i=0; i<100; i++);
  192. }

  193. /*
  194. *功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
  195. * 数据方向:(Nand的start_addr->SDRAM的buf)
  196. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  197. * start_addr : 如果要确保正确使用本函数来传输数据,这里的地址:不是页编号,但是必须每一个page页的开始地址,即页边界地址,即地址要2K对齐。
  198. * size : 复制的字节数,最好是页空间整数倍。即使不指定为页的整数倍,也会读取2K整数倍的空间
  199. *注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
  200. * 2,如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,
  201. * 在发出读命令的两个周期之间,发出要读取的页地址,然后读取数据即可
  202. * 3,如果nand起始地址不是页对齐,则使用下面的nand_read2函数。
  203. * 使用本函数,nand起始地址一定要页对齐
  204. * 4,本函数的读操作是以页为单位进行的,size即使不是页的整数倍,也会读取整数倍的页数据。
  205. * 5,如果这里的起始地址,非页对齐的地址,可能会导致我的程序重启(应该是读指令异常)
  206. */
  207. void nand_read(unsigned char *buf, unsigned long start_addr, int size)
  208. {
  209.         unsigned long i = 0, j = 0;
  210.         //1. 选中,片选
  211.         NF_CE_L();
  212.         
  213.         for(i=start_addr; i < (start_addr + size);)
  214.         {
  215.                 //清除RB
  216.                 NF_CLEAR_RB();
  217.                 // 2. 发出读命令00h,读NandFlash时的第1个命令
  218.               //0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
  219.                 //会自动满足时序图的要求,不再需要人工参与了。
  220.                 //这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
  221.                 NF_CMD(CMD_READ1);
  222.                 //3. 发出地址,写地址 (分5步发出)
  223.                 write_addr(i);
  224.                 //4. 发出读命令30h,读NandFlash时的第2个命令
  225.                 NF_CMD(CMD_READ2);
  226.                 //注意 : 因为这里没有继续发出页内随意读命令,所以当i=2048,2049的时候,
  227.                 //上面发地址的函数write_addr(i);经过处理,已经跳过第0页了,是要读取第1页的第1,2个字节。
  228.                 
  229.                 //5. 判断状态,等待NandFlash不忙
  230.                 NF_WAIT_RB();
  231.                 
  232.                 //6. 读数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
  233.                 //注意 : uboot的命令 : nand dump 0 //从nand的0地址开始,1次读取1页的数据。
  234.                 for(j=0; j<2048; j++, i++)
  235.                 {
  236.                     //1次从nandflash的8位IO口上读取8位数据。
  237.                     //注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
  238.                     //前一次读操作未完成,是不会启动下一次读操作的。
  239.                     *buf = NF_RDDATA8();
  240.                     buf++;
  241.                 }
  242.                 uart_sendString("#"); //2个nand read函数:目的是:main之后才初始化uart,可以使用这个带打印#的函数
  243.         }
  244.         //7. 取消选中,关闭片选
  245.         NF_CE_H();
  246. }


  247. /*
  248. *功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
  249. * 数据方向:(Nand的start_addr->SDRAM的buf)
  250. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  251. * start_addr : 如果要确保正确使用本函数来传输数据,这里的地址:不是页编号,
  252. * 但是必须每一个page页的开始地址,即页边界地址,即地址要2K对齐。
  253. * size : 复制的字节数,最好是页空间整数倍。即使不指定为页的整数倍,也会读取2K整数倍的空间
  254. *注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
  255. * 2,如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,
  256. * 在发出读命令的两个周期之间,发出要读取的页地址,然后读取数据即可
  257. * 3,如果nand起始地址不是页对齐,则使用下面的nand_read2函数。
  258. * 使用本函数,nand起始地址一定要页对齐
  259. * 4,本函数的读操作是以页为单位进行的,size即使不是页的整数倍,也会读取整数倍的页数据。
  260. * 5,如果这里的起始地址,非页对齐的地址,可能会导致我的程序重启(应该是读指令异常)
  261. */
  262. void nand_read_1(unsigned char *buf, unsigned long start_addr, int size)
  263. {
  264.         unsigned long i = 0, j = 0;
  265.         //1. 选中,片选
  266.         NF_CE_L();

  267.         for(i=start_addr; i < (start_addr + size);)
  268.         {
  269.                 //清除RB
  270.                 NF_CLEAR_RB();
  271.                 // 2. 发出读命令00h,读NandFlash时的第1个命令
  272.               //0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
  273.                 //会自动满足时序图的要求,不再需要人工参与了。
  274.                 //这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
  275.                 NF_CMD(CMD_READ1);
  276.                 //3. 发出地址,写地址 (分5步发出)
  277.                 write_addr(i);
  278.                 //4. 发出读命令30h,读NandFlash时的第2个命令
  279.                 NF_CMD(CMD_READ2);
  280.                 //注意 : 因为这里没有继续发出页内随意读命令,所以当i=2048,2049的时候,
  281.                 //上面发地址的函数write_addr(i);经过处理,已经跳过第0页了,是要读取第1页的第1,2个字节。
  282.                 
  283.                 //5. 判断状态,等待NandFlash不忙
  284.                 NF_WAIT_RB();

  285.                 //6. 读数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
  286.                 //注意 : uboot的命令 : nand dump 0 //从nand的0地址开始,1次读取1页的数据。
  287.                 for(j=0; j<2048; j++, i++)
  288.                 {
  289.                     //1次从nandflash的8位IO口上读取8位数据。
  290.                     //注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
  291.                     //前一次读操作未完成,是不会启动下一次读操作的。
  292.                     *buf = NF_RDDATA8();
  293.                     buf++;
  294.                 }
  295.         }
  296.         //关闭片选
  297.         NF_CE_H();
  298.         
  299. }


  300. /*
  301. *    buf : 目标地址 = 0x30000000,这是SDRAM的起始地址
  302. *    start_addr : 源地址 = 4096,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处
  303. * size : 复制长度 = 600K,对于本实验,这是足够了,没有图片的话,16K足够了,有图片了,得300K
  304. */
  305. int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size)
  306. {
  307.     unsigned long i = 0;
  308.     
  309.     //norflash启动开发板
  310.     if(isBootFromNorFlash())
  311.     {
  312.         while (i < size)
  313.         {
  314.             buf[i] = *((char *)start_addr);
  315.             i++;
  316.             start_addr++;
  317.         }
  318.     }
  319.     else //nandflash启动开发板
  320.     {
  321.         nand_read_1(buf, start_addr, size);
  322.     }
  323.     
  324.   return 0;
  325. }


  326. /*
  327. *功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
  328. * 数据方向:(Nand的start_addr->SDRAM的buf)
  329. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  330. * start_addr : 不是页编号,是具体的页内地址,可以不是每一个page页的开始地址,
  331. * 即可以是非页边界地址,即地址没有2K对齐的要求。当然地址是页对齐也会支持
  332. * size : 复制的字节数,可以是任意字节数。
  333. * 这个大小仅仅只包括页内main区的空间,不包括页内spare区的空间。
  334. *注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
  335. * 2,如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,
  336. * 在发出读命令的两个周期之间,发出要读取的页地址,然后读取数据即可
  337. * 3,如果nand起始地址不是页对齐,则使用下面的nand_read2函数。
  338. * 当然,页对齐的地址,也可以使用本函数。
  339. * 4,本函数已经测试通过,可以使用,功能很强大,完全兼容上面的nand_read函数。
  340. */
  341. unsigned int nand_read2(unsigned char * buf, unsigned long start_addr, int size)
  342. {
  343.         unsigned long i = 0, j = 0;
  344.         unsigned long page_num = 0; //读取的页page的数量
  345.         unsigned long page_yu_num = 0; //不足一页page的字节数
  346.         unsigned long start_yu_num = 0; //start_addr起始地址 到 本页末尾地址 的个数。

  347.         page_num = size/2048; //+1=读取的页page的数量
  348.         page_yu_num = size & (2048 -1); //不足一页page的字节数
  349.         start_yu_num = 2048-(start_addr & (2048 -1)); //这个非对齐的页的起始地址 到 本页末尾地址 的个数。

  350.         //1. 选中,片选
  351.         NF_CE_L();
  352.         
  353.         for(i=start_addr; i < (start_addr + size);)
  354.         {
  355.                 //清除RB
  356.                 NF_CLEAR_RB();
  357.                 // 2. 发出读命令00h,读NandFlash时的第1个命令
  358.                 NF_CMD(CMD_READ1);
  359.                 //3. 发出地址,写地址 (分5步发出)
  360.                 write_addr(i);
  361.                 //4. 发出读命令30h,读NandFlash时的第2个命令
  362.                 NF_CMD(CMD_READ2);
  363.                 //5. 判断状态,等待NandFlash不忙
  364.                 NF_WAIT_RB();
  365.                 //6. 读数据
  366.                 if((i & (2048 -1)) == 0) //起始地址:是页对齐的地址
  367.                 {
  368.                         if(page_num > 0) //剩下需要读取的字节数>1页page
  369.                         {
  370.                                 for(j=0; j<2048; j++, i++) //读取一页
  371.                                 {
  372.                                     *buf = NF_RDDATA8();
  373.                                     buf++;
  374.                                 }
  375.                                 page_num--; //每读一页,则少一页
  376.                         }
  377.                         else //剩下需要读取的字节数<1页page
  378.                         {
  379.                                 for(j=0; j<page_yu_num; j++, i++) //读取不足一页的数据
  380.                                 {
  381.                                         *buf = NF_RDDATA8();
  382.                                         buf++;
  383.                                 }
  384.                         }
  385.                 }
  386.                 else //起始地址:非页对齐的地址
  387.                 {
  388.                         //start_yu_num:是 这个非对齐的页的起始地址 到 本页末尾地址 的个数。
  389.                         for(j=0; j<start_yu_num; j++, i++) //小于2048也行
  390.                         {
  391.                                 *buf = NF_RDDATA8();
  392.                                 buf++;
  393.                         }
  394.                 }
  395.         }
  396.         //7. 取消选中,关闭片选
  397.         NF_CE_H();
  398.         
  399.         return 0;
  400. }


  401. /*
  402. *函数功能 : 页内地址随意读,可以读取页内2047以内的main区地址,也可以读取页内2047以外的64字节的spare区的地址。
  403. *函数参数 : page_number : 是页编号,是第几个页。从第9页开始。
  404. * addr : 页内地址,这个地址的范围一定是: <=2K+后面的64字节的
  405. *注意 : 1, 页读和页写,一般是从页的首地址开始读、写,
  406. * 随意读、写实现了在一页范围内任意地址的读、写。
  407. * 2, 随意读操作是在页读操作后输入随意读命令和页内列地址,
  408. * 这样就可以读取到列地址所指定地址的数据。
  409. *页读/写,页内地址随意读/写的区别:
  410. * 页读/: 虽然读写过程可以不从页边界开始,但在正式场合下还是建议从页边界开始读写至页结束
  411. * 页内地址随意读/: 可以读取页内2047以内的main区地址,也可以读取页内2047以外的64字节的spare区的地址。
  412. */
  413. unsigned char nand_read_random(unsigned long page_number, unsigned long addr)
  414. {
  415.         unsigned char ch;
  416.         //1,打开Nand Flash片选
  417.         NF_CE_L();
  418.         //清RnB信号
  419.         NF_CLEAR_RB();
  420.         //2,页读命令周期1
  421.         NF_CMD(CMD_READ1);
  422.         //3,写入5个地址周期
  423.         NF_ADDR(0x00); //(页内)地址A0~A7
  424.         NF_ADDR(0x00); //(页内)地址A8~A11
  425.         NF_ADDR(page_number & 0xff); //()地址A12~A19
  426.         NF_ADDR((page_number >> 8) & 0xff); //()地址A20~A27
  427.         NF_ADDR((page_number >> 16) & 0x01); //()地址A28 (低1位)
  428.         //4,页读命令周期2
  429.         NF_CMD(CMD_READ2);
  430.         //等待RnB信号变高,即不忙
  431.         NF_WAIT_RB();
  432.         //5,随意读命令周期1
  433.         NF_CMD(CMD_RANDOMREAD1);
  434.         //6,写页内地址,共12根线,可寻址2^12=2K*2空间,每一页的main区是2K空间,spare区只有64字节
  435.         //如果读取地址是2048+6,没有像write_addr()函数一样,来截取低11位,则A11肯定是1,
  436.         //后面的+6,就可以寻址到页内spare区的第6个字节。
  437.         NF_ADDR((char)(addr & 0xff)); //(页内)地址A0~A7
  438.         NF_ADDR((char)(addr >> 8) & 0x0f); //(页内)地址A8~A11 (低4位)
  439.         //7,随意读命令周期2
  440.         NF_CMD(CMD_RANDOMREAD2);
  441.         //8, 读取指定地址的数据
  442.         ch = NF_RDDATA8();
  443.         //9, 关闭Nand Flash片选
  444.         NF_CE_H();

  445.         return ch; //将读取的数据返回
  446. }


  447. /*
  448. *功能 : 识别坏块的读取: 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
  449. * 数据方向:(Nand的start_addr->SDRAM的buf)
  450. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  451. * start_addr : 不是页编号,是 页(2K)对齐的地址。
  452. * size : 复制的字节数,是页空间整数倍
  453. *注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
  454. * 2,读操作是以页为单位进行的
  455. * 3,为了更准确地读取数据,则在读取完数据之后还要进行ECC校验判断,以确定所读取的数据是否正确。
  456. * 4,为了配合ECC校验,程序的输入参数start_addr最好直接就为K9F2G08U0A的第几页
  457. * 例如我们要读取第128064页中的内容,可以调用该程序为 : start_addr=128064*2048
  458. * 由于第128064页是第2001块中的第0页 start_addr=(128064=2001×64+0)*2048
  459. * 即 start_addr 最好是 页(2K)对齐的地址
  460. */
  461. unsigned int nand_read_oob(unsigned char * buf, unsigned long start_addr, int size)
  462. {
  463.         unsigned long ret = 0;
  464.         unsigned long col, page;
  465.         unsigned long i = 0;

  466.         for(i=start_addr; i < (start_addr + size);)
  467.         {
  468.                 col = start_addr & (2048 - 1); //截取低位 : 如果start_addr是页对齐地址,则col肯定是0
  469.                 page = start_addr / 2048; //截取高位 : 是第几个页

  470.                 ret = nand_read_oob_page(buf, page);
  471.                 if(ret == 0) //读取成功
  472.                 {
  473.                         i = i + 2048; //继续写
  474.                         buf = buf + 2048;
  475.                 }
  476.                 else if(ret == 1) //读取失败
  477.                 {
  478.                         //这里应该打印报错语句
  479.                         return 1;
  480.                 }
  481.         }
  482.         return 0;
  483. }


  484. /*
  485. *功能 : 识别坏块的读取: 从NandFlash位置第page_number页开始读取数据,将数据复制到buf处,
  486. * 一次共复制一页(2K)个字节的数据,且只复制一页的数据。
  487. * 数据方向:(Nand的start_addr--->SDRAM的buf)
  488. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  489. * page_number : 是页编号,是第几个页。
  490. *注意 : 1,read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到buf。
  491. * 2,读操作是以页为单位进行的,并且一次只读一页数据。
  492. * 3,为了更准确地读取数据,则在读取完数据之后还要进行ECC校验判断,以确定所读取的数据是否正确。
  493. * 4,这段程序是把某一页的内容读取到数组buffer中。该程序的输入参数直接就为K9F2G0
  494. * 8U0A的第几页,例如我们要读取第128064页中的内容,可以调用该程序为:nand_read_oob_page(128064)
  495. * 由于第128064页是第2001块中的第0页(128064=2001×64+0),所以为了更清楚地表示页
  496. * 与块之间的关系,也可以写为:nand_read_oob_page(2001*64)
  497. */
  498. unsigned int nand_read_oob_page(unsigned char * buf, unsigned long page_number)
  499. {
  500.         unsigned long i;
  501.         unsigned long meccd; //保存从OOB区读取出来的值,共4个字节:第2048+2049+2050+2051字节
  502.         unsigned long seccd; //保存从OOB区读取出来的值,共2个字节:第2052+2053字节

  503.         //复位ECC
  504.         NF_RSTECC();
  505.         //解锁main区ECC : 以便main区产生ECC码
  506.         NF_MECC_UnLock();
  507.         //1. 选中,片选
  508.         NF_CE_L();
  509.         //清除RB
  510.         NF_CLEAR_RB();
  511.         // 2. 发出读命令00h,读NandFlash时的第1个命令
  512.         NF_CMD(CMD_READ1);
  513.         //3. 发出地址,写地址 (分5步发出)
  514.         NF_ADDR(0x00);
  515.         NF_ADDR(0x00);
  516.         NF_ADDR(page_number & 0xff);
  517.         NF_ADDR((page_number >>8) & 0xff);
  518.         NF_ADDR((page_number >>16) & 0xff);
  519.         //4. 发出读命令30h,读NandFlash时的第2个命令
  520.         NF_CMD(CMD_READ2);
  521.         //5. 判断状态,等待NandFlash不忙
  522.         NF_WAIT_RB();
  523.         //6. 读数据
  524.         for(i=0; i<2048; i++)
  525.         {
  526.                 //1次从nandflash的8位IO口上读取8位数据。
  527.                 buf[i] = NF_RDDATA8();
  528.         }
  529.         //注意 : 读取完数据之后,硬件将自动产生main区ECC码
  530.         //锁定main区ECC值 : 自动产生的main区ECC码,不是保存到寄存器(NFMECCD0,1)中,是保存在另外一个位置。
  531.         //NFMECCD0,1用来存放已有的ECC码,已有的ECC码保存在OOB区,即spare区。
  532.         NF_MECC_Lock();

  533.         //解锁spare区ECC : 以便spare区产生ECC码
  534.         NF_SECC_UnLock();
  535.         //读spare区的前32位(4个字节)地址内容,即第2048~2051地址,这4个字节为写数据时,产生的main区的ECC码。
  536.         meccd = NF_RDDATA();
  537.         //meccd的顺序 : ECC0(0~7位)+ECC1(8~15位)+ECC2(16~23位)+ECC3(24~31位)
  538.         //注意 : 把读取到的main区的ECC校验码放入 NFMECCD0 和 NEMECCD1 的相应位置内。
  539.         //具体放置位置 : ECC0:NFMECCD0的0~7位 ECC1:NFMECCD0的16~23位。
  540.         // ECC2:NFMECCD1的0~7位 ECC3:NFMECCD1的16~23位。
  541.         rNFMECCD0 = ((meccd & 0xff00) << 8) | (meccd & 0xff);
  542.         rNFMECCD1 = ((meccd & 0xff000000) >>8) | ((meccd & 0xff0000)>>16);
  543.         //锁定spare区的ECC值 : 自动产生的spare区ECC码,不是保存到NFSECCD中,是保存在另外一个位置。
  544.         //NFSECCD用来存放已有的spare区的ECC码,已有的spare区ECC码保存在OOB区。
  545.         NF_SECC_Lock();

  546.         //继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值
  547.         seccd = NF_RDDATA();
  548.         //把读取到的spare区的ECC校验码放入NFSECCD的相应位置内
  549.         //seccd的顺序 : spare区ECC0(0~7位)+spare区ECC1(8~15位)
  550.         //具体放置位置 : spare区ECC0:NFSECCD的0~7位。
  551.         //                            spare区ECC1:NFSECCD的16~23位。
  552.         rNFSECCD = ((seccd & 0xff00) << 8) | (seccd & 0xff);
  553.         //7. 取消选中,关闭片选
  554.         NF_CE_H();
  555.         
  556.         //读完一页数据之后,自动算出一个ECC码,这个自动算出来的ECC代码 和
  557.         //已经存在的ECC码(由OOB区保存到NFMECCD0+NFMECCD1+NFMECCD中),自动进行比较,
  558.         //比较的结果,保存在NFESTAT寄存器中,[1:0]=00:表示main区无错误 [3:2]=00:表示spare区无错误。
  559.         if((rNFESTAT0 & 0xf) == 0)
  560.         {
  561.                 return 0; //读取的数据都正确
  562.         }
  563.         else
  564.         {
  565.                 return 1; //读取完成,但是数据错误
  566.         }
  567. }


  568. /*
  569. *功能 : 擦除指定的块
  570. *参数 : block_number : 块号
  571. *注意 : 1,一个NandFlash块有2K个块。块大小为128K+2K字节。
  572. * 2,块被擦除后nand对应的块中的数据全部是1
  573. * 3,命令字分为2个阶段
  574. * 4,擦除是以块为单位进行的
  575. */
  576. unsigned char nand_erase(unsigned long block_number)
  577. {
  578.         unsigned long i;
  579.         unsigned char stat;
  580.         //1,选中,片选
  581.         NF_CE_L();
  582.         //清除RB
  583.         NF_CLEAR_RB();
  584.         //2,发出擦除命令0x60,写入第1个擦除命令
  585.         NF_CMD(CMD_ERASE1);
  586.         //写块的地址 : 注意 : 只写块的地址就行,块地址都在行()地址的高位上,
  587.         //所以前面2个周期的列(页内)地址写0就行,后面3个周期的页地址的高11位要具体写出。
  588.         //列地址 : 就是页内地址, 共12位
  589.         //行地址 : 就是页地址, 共17位 : 高11位(A28-A18)是2K的块地址,低6位是64的页地址。
  590.         
  591.         //32位block_number:低11位有效 :
  592.      //| 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  593.      // 对应最后3个地址周期的页内地址的高11位 :
  594.         //| A28 | A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 | A19 | A18 |
  595.         //高11位在后面3个地址周期中应是如下的分布 :
  596.         //| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
  597.         //| | | | | | | | A28 |
  598.         //| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
  599.         //| A19 | A18 | | | | | | |
  600.         //3,仅需发出块地址(分3步发出)
  601.         NF_ADDR((block_number << 6) & 0xFF);
  602.         NF_ADDR((block_number >> 2) & 0xFF);
  603.         NF_ADDR((block_number >> 10) & 0xFF);
  604.         //4, 发出擦除命令0xD0, 写入第2个擦除命令
  605.         NF_CMD(CMD_ERASE2);
  606.         
  607.         for(i=0; i<1000; i++);
  608.         //5, 写入读状态寄存器的命令
  609.         NF_CMD(CMD_STATUS);
  610.         //6, 判断状态
  611.         do
  612.         {
  613.          //读取8位状态数据
  614.                 stat = NF_RDDATA8();
  615.         }
  616.         while(!(stat & 0x40)); //等到第6位为1 : 表示读取数据完成
  617.         //7, 取消选中,关闭片选
  618.         NF_CE_H();

  619.         //约定用0x66来表示擦除成功,或者写成功
  620.         return 0;
  621. }


  622. /*
  623. *函数功能 : 识别坏块的擦除
  624. *函数参数 : 块编号,从第0块开始,2K个块(第0块~第2047块)
  625. *函数返回值 :
  626. * 1 : 是坏块,不能擦除
  627. *注意 : 1,擦除是以块为单位进行的,因此在写地址周期时,只需写三个行周期,并且只写块地址
  628. * 并且,擦除的范围是64个页,页内的main区+spare区的64字节都擦除成0xff。
  629. * 2,只能擦除好块,不能擦除坏块,擦除坏块的操作会失败。
  630. * 也可以根据这个特性来判断一个块是不是坏块:当第1页的spare的第6个字节的值感觉不可靠,
  631. * 并且,这个块的数据不重要,可以清除时,可以直接擦除这个块,如果擦除成功,表示是好块,
  632. * 如果擦除失败,则表示这个块是坏块,我们再在spare区的第6个字节标记一下,这是个坏块。
  633. * 3,该程序的输入参数为K9F2G08U0A的第几块,例如我们要擦除第2001块,
  634. * 则调用该函数为 : nand_erase_oob(2001)

  635.         //写地址(要读取数据的起始地址) :
  636.         //包括 :(页内)地址,是低位地址,共A0~A11,在具体的page页内寻址。2^12=2K*2 一个page页=(2K+64)字节
  637.         //()地址,是高位地址,共A12~A28,可以寻址到page页。2^17=128K, 一个Nand器件=2K*64个page页 = 128K页
  638.         // 高位地址中,A12~A17,可以寻址到block块下面的page页。2^6=64, 一个block块=64个page页
  639.         // 高位地址中,A18~A28,可以寻址到block块。2^11=2K, 一个Nand器件=2K个block块
  640.         //要写全部的5个周期的地址
  641.     
  642.         //32位的page_number : 低29位有效 :
  643.         //| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16| 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  644.      // 对应最后3个地址周期的页内地址的高11位 :
  645.         //|A28|A27|A26|A25|A24|A23|A22|A21|A20|A19|A18|A17|A16|A15|A14|A13|A12|A11|A10|A09|A08|A07|A06|A05|A04|A03|A02|A01|A00|
  646.         //29位在5个地址周期中应是如下的分布 :
  647.         //| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
  648.         //| L | L | L | L | L | L | L | A28 |
  649.         //| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
  650.         //| A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 |
  651.         //| L | L | L | L | A11 | A10 | A9 | A8 |
  652.         //| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
  653. */
  654. unsigned int nand_erase_oob(unsigned long block_number)
  655. {
  656.         unsigned long i = 0;
  657.         unsigned char stat; //保存 状态寄存器 中的值
  658.         unsigned int flag; //坏块标志
  659.         //判断该块是否为坏块
  660.         flag = nand_IsBadBlock(block_number);
  661.         if(flag == 1) //是坏块
  662.         {
  663.                 return 1; //是坏块,返回1
  664.         }
  665.         //本块不是坏块,才继续走到下面来
  666.         //1,选中,片选
  667.         NF_CE_L();
  668.         //清除RB
  669.         NF_CLEAR_RB();
  670.         //2,发出擦除命令0x60,写入第1个擦除命令
  671.         NF_CMD(CMD_ERASE1);
  672.         //3,仅需发出块地址(分3步发出)
  673.         //()地址,是高位地址,共A12~A28,可以寻址到page页。
  674.         //A12~A17,可以寻址到block块下面的page页; A18~A28,可以寻址到block块。
  675.         NF_ADDR((block_number << 6) & 0xc0); //()地址 A18~A19(高2位)
  676.         NF_ADDR((block_number >> 2) & 0xff); //()地址 A20~A27(8位)
  677.         NF_ADDR((block_number >> 10) & 0x01); //()地址 A28 (低1位)
  678.         //4, 发出擦除命令0xD0,写入第2个擦除命令
  679.         NF_CMD(CMD_ERASE2);
  680.         //会擦除本块的所有64个页,包括页内的main区空间+页内的spare区空间
  681.         for(i=0; i<100; i++); //延时一段时间
  682.         //5, 读状态命令
  683.         NF_CMD(CMD_STATUS);
  684.         //6, 判断状态
  685.         do
  686.         {
  687.                 //将状态寄存器的值 赋值给stat。只取8位状态数据。
  688.                 stat=NF_RDDATA();
  689.         }
  690.         while(!(stat & 0x40)); //等到第6位为1 : 表示读取状态寄存器完成。
  691.         //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
  692.         //7, 取消选中,关闭片选
  693.         NF_CE_H();
  694.         //判断状态寄存器的值的第0位是否为0,为0则表示擦除操作成功完成,否则表示擦除操作失败
  695.         if(stat & 0x01)
  696.         {
  697.                 //如果擦除失败,则标记该块为坏块,也即恢复刚才可能擦除的坏块标志。
  698.                 flag = nand_MarkBadBlock(block_number); //标注该块为坏块
  699.                 if(flag == 0)
  700.                 {
  701.                         return 2; //标注坏块成功,擦除操作失败
  702.                 }
  703.                 else
  704.                 {
  705.                         return 3; //标注坏块失败,擦除操作失败
  706.                 }
  707.         }    
  708.         else
  709.         {
  710.                 return 0; //擦除操作成功
  711.         }
  712. }


  713. /*功能 : 将buf处开始取数据,写入NandFlash的start_addr地址处,共写入size个字节。
  714. * 数据方向:(SDRAM的buf->Nand的start_addr)
  715. *参数 : buf : SDRAM的缓冲区起始地址
  716. * start_addr : 如果要确保正确使用本函数来传输数据,这里的地址:不是页编号,
  717. * 但是必须每一个page页的开始地址,即页边界地址,即地址要2K对齐。
  718. * size : 复制的字节数,最好是页空间整数倍。即使不指定为页的整数倍,也会读取2K整数倍的空间
  719. *注意 : 1,write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
  720. * 2,页写操作的大致流程为:在两个写命令周期之间分别写入页地址和数据
  721. * 3,如果为了保证下次读取该数据时的正确性,还需要把main区的ECC值和spare区
  722. * 的ECC值写入到该页的spare区内。
  723. */
  724. unsigned int nand_write(unsigned char * buf, unsigned long start_addr, int size)
  725. {
  726.         unsigned long i,j;
  727.         unsigned char stat;
  728.         //1,选中,片选
  729.         NF_CE_L();
  730.         
  731.         for(i=start_addr; i < (start_addr + size);)
  732.         {
  733.                 //清除RB
  734.                 NF_CLEAR_RB();
  735.                 //2. 发出写命令0x80,写NandFlash时的第1个命令 : NandFlash写准备
  736.                 NF_CMD(CMD_WRITE1);
  737.                 //3. 发出地址,写地址(分5步发出)
  738.                 write_addr(i);
  739.                 //4, 写数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
  740.                 for(j=0; j<2048; j++, i++)
  741.                 {
  742.                  //1次写8位的数据给NandFlash的8位IO口
  743.                  //注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
  744.                  //前一次写数据操作不完成,是不会启动下一次写数据操作的
  745.                         NF_WRDATA8(*buf);
  746.                         buf++;
  747.                 }

  748.                 //5. 发出写命令0x10,写NandFlash时的第2个命令 : 启动写操作
  749.                 //此时,Flash内部会自动完成写,校验操作。
  750.                 NF_CMD(CMD_WRITE2);
  751.                 for(j=0; j<100; j++);
  752.                 //6. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
  753.                 NF_CMD(CMD_STATUS);
  754.                 do
  755.                 {
  756.                  //读取8位状态数据
  757.                         stat = NF_RDDATA8();
  758.                 }
  759.                 while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。
  760.         }
  761.         //7. 取消选中,关闭片选
  762.         NF_CE_H();
  763.         
  764.         return 0;
  765. }


  766. /*函数功能 : 将buf处开始取数据,写入NandFlash的start_addr地址处,共写入size个字节。
  767. * 数据方向:(SDRAM的buf->Nand的start_addr)
  768. *函数参数 : buf : SDRAM的缓冲区起始地址
  769. * start_addr : 不是页编号,是具体的页内地址,可以不是每一个page页的开始地址,
  770. * 即可以是非页边界地址,即地址没有2K对齐的要求。当然地址是页对齐也会支持
  771. * size : 复制的字节数,可以是任意字节数。
  772. * 这个大小仅仅只包括页内main区的空间,不包括页内spare区的空间。*注意 : 1,write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
  773. *注意 : 1,页写操作的大致流程为:在两个写命令周期之间分别写入页地址和数据
  774. * 2,如果为了保证下次读取该数据时的正确性,还需要把main区的ECC值和spare区
  775. * 的ECC值写入到该页的spare区内。
  776. * 3,如果nand起始地址不是页对齐,则使用下面的 nand_write2 函数。
  777. * 当然,页对齐的地址,也可以使用本函数。
  778. * 4,本函数已经测试通过,可以使用,功能很强大,完全兼容上面的 nand_write 函数。
  779. */
  780. unsigned int nand_write2(unsigned char * buf, unsigned long start_addr, int size)
  781. {
  782.         unsigned long i,j;
  783.         unsigned char stat;
  784.         
  785.         unsigned long page_num = 0; //写入的页page的数量
  786.         unsigned long page_yu_num = 0; //不足一页page的字节数
  787.         unsigned long start_yu_num = 0; //start_addr起始地址 到 本页末尾地址 的个数。

  788.         page_num = size/2048; //写入的页page的数量
  789.         page_yu_num = size & (2048 -1); //不足一页page的字节数
  790.         start_yu_num = 2048-(start_addr & (2048 -1)); //这个非对齐的页的起始地址 到 本页末尾地址 的个数。

  791.         //1,选中,片选
  792.         NF_CE_L();
  793.         
  794.         for(i=start_addr; i < (start_addr + size);)
  795.         {
  796.                 //清除RB
  797.                 NF_CLEAR_RB();
  798.                 //2. 发出写命令0x80,写NandFlash时的第1个命令 : NandFlash写准备
  799.                 NF_CMD(CMD_WRITE1);
  800.                 //3. 发出地址,写地址(分5步发出)
  801.                 write_addr(i);
  802.                 //4, 写数据 : 虽然读写过程可以不从页边界开始,但在这里必须从页边界开始读写至页结束
  803.                 if((i & (2048 -1)) == 0) //起始地址:是页对齐的地址
  804.                 {
  805.                         if(page_num > 0) //剩下需要写入的字节数>1页page
  806.                         {
  807.                                 for(j=0; j<2048; j++, i++)
  808.                                 {
  809.                                  //1次写8位的数据给NandFlash的8位IO口
  810.                                  //注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
  811.                                  //前一次写数据操作不完成,是不会启动下一次写数据操作的
  812.                                         NF_WRDATA8(*buf);
  813.                                         buf++;
  814.                                 }
  815.                         }
  816.                         else //剩下需要写入的字节数<1页page
  817.                         {
  818.                                 for(j=0; j<page_yu_num; j++, i++) //写入不足一页的数据
  819.                                 {
  820.                                         NF_WRDATA8(*buf);
  821.                                         buf++;
  822.                                 }
  823.                         }
  824.                 }
  825.                 else //起始地址:非页对齐的地址
  826.                 {
  827.                         //start_yu_num:是 这个非对齐的页的起始地址 到 本页末尾地址 的个数。
  828.                         for(j=0; j<start_yu_num; j++, i++) //小于2048也行
  829.                         {
  830.                                     NF_WRDATA8(*buf);
  831.                                     buf++;
  832.                         }
  833.                 }
  834.                 
  835.                 //5. 发出写命令0x10,写NandFlash时的第2个命令 : 启动写操作
  836.                 //此时,Flash内部会自动完成写,校验操作。
  837.                 NF_CMD(CMD_WRITE2);
  838.                 for(j=0; j<100; j++);
  839.                 //6. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
  840.                 NF_CMD(CMD_STATUS);
  841.                 do
  842.                 {
  843.                  //读取8位状态数据
  844.                         stat = NF_RDDATA8();
  845.                 }
  846.                 while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。
  847.         }
  848.         //7. 取消选中,关闭片选
  849.         NF_CE_H();
  850.         
  851.         return 0;
  852. }


  853. /*
  854. *函数功能 : 页内地址随意写
  855. *函数参数 : page_number : 是页编号,从第0页开始。
  856. * 1块=64页,第2块最开始的一页是第64页
  857. * addr : 页内地址(包括2K的main区空间+64字节的spare区空间)
  858. * data : 要写入的1字节的数据
  859. *函数返回值 :
  860. * 0 : 写入成功
  861. * 1 : 写入失败
  862. *注意 : 1, 页读和页写是从页的首地址开始读、写,
  863. * 随意读、写实现了在一页范围内任意地址的读、写。
  864. * 2, 随意写操作是在页写操作的第二个页写命令周期前,输入随意写命令和页内列地址,
  865. * 以及要写入的数据,这样就可以把数据写入到列地址所指定的地址内。
  866. */
  867. unsigned int nand_write_random(unsigned long page_number, unsigned long addr, unsigned char data)
  868. {
  869.         unsigned int i = 0;
  870.         unsigned char stat; //保存状态寄存器的值
  871.         //1,打开Nand Flash片选
  872.         NF_CE_L();
  873.         //清RnB信号
  874.         NF_CLEAR_RB();
  875.         //2,页写命令周期1
  876.         NF_CMD(CMD_WRITE1);
  877.         //3,写入5个地址周期
  878.         NF_ADDR(0x00); //列地址A0~A7
  879.         NF_ADDR(0x00); //列地址A8~A11
  880.         NF_ADDR(page_number & 0xff); //行地址A12~A19
  881.         NF_ADDR((page_number >> 8) & 0xff); //行地址A20~A27
  882.         NF_ADDR((page_number >> 16) & 0x01); //行地址A28
  883.         //4,随意写命令
  884.         NF_CMD(CMD_RANDOMWRITE);
  885.         //5,页内地址
  886.         NF_ADDR((char)(addr & 0xff)); //列地址A0~A7
  887.         NF_ADDR((char)((addr >> 8) & 0x0f)); //列地址A8~A11
  888.         //6,写入数据
  889.         NF_WRDATA8(data);
  890.         //7,页写命令周期2
  891.         NF_CMD(CMD_WRITE2);
  892.         for(i=0; i<100; i++); //延时一段时间
  893.         //8. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
  894.         NF_CMD(CMD_STATUS);
  895.         do
  896.         {
  897.                 stat = NF_RDDATA8();
  898.         }
  899.         while(!(stat&0x40)); //等待第6位为1,表示写数据完成
  900.         //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同

  901.         //9,关闭Nand Flash片选
  902.         NF_CE_H();
  903.         
  904.         //判断状态值的第0位是否为0,为0则写操作正确,否则错误
  905.         if(stat &0x1)
  906.         {
  907.                 return 1; //写入失败
  908.         }
  909.         else
  910.         {
  911.                 return 0; //写入成功
  912.         }
  913. }


  914. /*功能 : 识别坏块的写 : 将buf处开始的数据,写入NandFlash的start_addr地址处,共写入size个字节。
  915. * 数据方向:(SDRAM的buf->Nand的start_addr)
  916. *参数 : buf : SDRAM的缓冲区起始地址
  917. * start_addr : 不是页编号,是页对齐地址,
  918. * 是具体的读取NandFlash的起始地址,包括页内地址
  919. * size : 复制的字节数,是页空间整数倍,也就是说,size是2K整数倍
  920. *注意 : 1,write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
  921. * 2,坏块的话写进去的东西肯定是不对的。一般情况下如果你手上的系统支持NAND运行,
  922. * 那么相关的读取数据代码中肯定有跳坏块处理的
  923. */
  924. unsigned int nand_write_oob(unsigned char * buf, unsigned long start_addr, int size)
  925. {
  926.         unsigned long ret = 0;
  927.         unsigned long col, page;
  928.         unsigned long i = 0;

  929.         for(i=start_addr; i < (start_addr + size);)
  930.         {
  931.                 col = start_addr & (2048 - 1); //如果start_addr是页对齐地址,则col肯定是0
  932.                 page = start_addr / 2048; //截取高位,是第几个页
  933.                 
  934.                 ret = nand_write_oob_page(buf, page);
  935.                 if(ret == 0) //写成功
  936.                 {
  937.                         i = i + 2048; //跳过坏块
  938.                         buf = buf + 2048; //继续写
  939.                 }
  940.                 else if(ret == 1) //是坏块
  941.                 {
  942.                         i = i + 2048; //跳过坏块
  943.                         buf = buf;     //再写,buf不增加
  944.                 }
  945.                 else if(ret == 2) //写失败,但是标注坏块成功,判断是坏块
  946.                 {
  947.                         //这里应该打印一句报错信息
  948.                         i = i + 2048; //跳过坏块
  949.                         buf = buf;                //再写,buf不增加
  950.                 }
  951.                 else if(ret == 3) //写失败,标注坏块也失败,读取的时候,无法判断是否是坏块,这里当做好块
  952.                 {
  953.                         //这里应该打印一句报错信息
  954.                         i = i; //当做好块,再写一次
  955.                         buf = buf; //再写,buf不增加
  956.                 }
  957.         }
  958.         return 0;
  959. }


  960. /*功能 : 识别坏块的写 : 将数据 从SDRAM的buf 复制到 NandFlash第page_number页开始的位置。
  961. * 一次写且仅写一页(2K)个字节的数据
  962. * 数据方向:(SDRAM的buf--->nandflash的第page_number页)
  963. *参数 : buf : SDRAM的缓冲区起始地址
  964. * page_number : 是页编号,是第几页,从第0页开始
  965. *注意 : 1,write是相对于cpu说的,cpu写数据到nand中, buf中的数据拷贝到Nand中。
  966. * 2,写操作是以页为单位进行的,并且一次只写一页数据
  967. * 3,本程序检查坏块,标记坏块,并且将ECC码记录在OOB区。
  968. */
  969. unsigned int nand_write_oob_page(unsigned char * buf, unsigned long page_number)
  970. {
  971.         unsigned long i;
  972.         unsigned long mecc0; //保存 NFMECC0 寄存器的值,32位值均有效,即4个字节:ECC0+ECC1+ECC2+ECC3
  973.         unsigned long secc; //保存 NFSECC 寄存器的值,低16位值有效,即2个字节:spare区ECC0+spare区ECC1
  974.         unsigned char stat;
  975.         unsigned int flag;                //坏块标志,标注成功与否的标志
  976.         unsigned char ECCBuf[6];    //用来保存 上面的6个字节:mecc0+secc

  977.         //判断该块是否为坏块
  978.         flag = nand_IsBadBlock(page_number>>6);
  979.         if(1 == flag)
  980.         {
  981.                 return 1; //是坏块,返回
  982.         }
  983.         //复位ECC
  984.         NF_RSTECC();
  985.         //解锁main区的ECC : 以便产生该区的ECC码
  986.         NF_MECC_UnLock();
  987.         //1,选中,片选
  988.         NF_CE_L();
  989.         //清除RB
  990.         NF_CLEAR_RB();
  991.         //2. 发出写命令0x80,写NandFlash时的第1个命令 : NandFlash写准备
  992.         NF_CMD(CMD_WRITE1);
  993.         //3. 发出地址,写地址(分5步发出)
  994.         NF_ADDR(0x00);
  995.         NF_ADDR(0x00);
  996.         NF_ADDR(page_number & 0xff);
  997.         NF_ADDR((page_number >>8) & 0xff);
  998.         NF_ADDR((page_number >>16) & 0xff);
  999.         //4, 写数据 : 一般1次写1页的数据
  1000.         for(i=0; i<2048; i++)
  1001.         {
  1002.                 //1次写8位的数据给NandFlash的8位IO口
  1003.                 //注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
  1004.                 //前一次写数据操作不完成,是不会启动下一次写数据操作的
  1005.                 NF_WRDATA8(buf[i]);
  1006.                 //buf++;
  1007.         }
  1008.         //注意 : main区写完数据之后,硬件将自动产生main区的ECC码,共4个字节。
  1009.         //锁定main区的ECC值 : 会自动把产生的main区的ECC码,保存到相应的寄存器(NFMECC0)中。
  1010.         NF_MECC_Lock();
  1011.         //读取main区的ECC校验码,保存到本地变量mecc0中。
  1012.         mecc0=rNFMECC0;
  1013.         //把ECC校验码由字型转换为字节型,并保存到字节变量数组ECCBuf中
  1014.         ECCBuf[0] = (unsigned char)(mecc0 & 0xff);                    //0~7位
  1015.         ECCBuf[1] = (unsigned char)((mecc0 >> 8) & 0xff);        //8~15位
  1016.         ECCBuf[2] = (unsigned char)((mecc0 >> 16) & 0xff);    //16~23位
  1017.         ECCBuf[3] = (unsigned char)((mecc0 >> 24) & 0xff);    //24~31位

  1018.         //解锁spare区的ECC :
  1019.         NF_SECC_UnLock();
  1020.         //把main区的ECC值写入到spare区的前4个字节地址内,即第2048~2051地址
  1021.         for(i=0; i<4; i++)
  1022.         {
  1023.                 //注意 : 页内main区写满之后,继续写,会写到本页的第2049个地址,即开始写spare区的第1个字节。
  1024.                 NF_WRDATA8(ECCBuf[i]);
  1025.         }
  1026.         //注意 : spare区写完数据之后,硬件将自动产生spare区的ECC码,共2个字节。
  1027.         //锁定spare区的ECC值 : 会自动把产生的spare区的ECC码,保存到相应的寄存器(NFSECC)中。
  1028.         NF_SECC_Lock();

  1029.         //读取spare区的ECC校验码,保存到本地变量secc中。
  1030.         secc = rNFSECC;
  1031.         //把ECC校验码保存到字节变量数组ECCBuf中
  1032.         ECCBuf[4]=(unsigned char)(secc & 0xff);                    //0~7位
  1033.         ECCBuf[5]=(unsigned char)((secc >> 8) & 0xff);    //8~15位
  1034.         //把spare区的ECC值继续写入到spare区,第2052~2053地址内。
  1035.         //注意 : 每块的第0页的spare区,第2054个字节处,会有坏块标志,其他页没有。
  1036.         for(i=4; i<6; i++)
  1037.         {
  1038.                 NF_WRDATA8(ECCBuf[i]);
  1039.         }
  1040.         //5. 发出读命令0x10,写NandFlash时的第2个命令 : 启动写操作
  1041.         NF_CMD(CMD_WRITE2);
  1042.         for(i=0; i<100; i++); //延时一段时间,以等待写操作完成
  1043.         //6. 判断状态,写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
  1044.         NF_CMD(CMD_STATUS);
  1045.         do
  1046.         {
  1047.                 //读取8位状态数据
  1048.                 stat = NF_RDDATA8();
  1049.         }
  1050.         while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。
  1051.         //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
  1052.         
  1053.         //7. 取消选中,关闭片选
  1054.         NF_CE_H();    
  1055.         //判断状态值的第0位是否为0,为0则写操作正确,否则错误
  1056.         if(stat & 0x1)
  1057.         {
  1058.                 //如果写入失败,则标记坏块。
  1059.                 //A12~A17,可以寻址到block块下面的page页; A18~A28,可以寻址到block块。
  1060.                 //右移6位,表示去掉 A12~A17,只剩下 块地址 A18~A28 共11位的有效值。
  1061.                 flag = nand_MarkBadBlock(page_number>>6);
  1062.                 if(0 == flag)
  1063.                 {
  1064.                         return 2; //标注坏块成功,写入操作失败
  1065.                 }
  1066.                 else
  1067.                 {
  1068.                         return 3; //标注坏块失败,写入操作失败
  1069.                 }
  1070.         }
  1071.         else
  1072.         {
  1073.                 return 0; //写操作成功
  1074.         }
  1075. }


  1076. /*
  1077.  *函数功能 : 检查指定的块是否是坏块
  1078.  *函数参数 : 块的编号,从第0块开始
  1079.  *函数返回值 : 0 : 好块 1 : 坏块
  1080.  *注意 :
  1081.  * 1, 为了和固有坏块信息保持一致,将新发现的坏块的第1个page的 spare area的第6个Byte标记为非0xff的值。
  1082.  * 我们定义在spare区的第6个地址(即每页的第2054地址)用来标注坏块,0x44表示该块为坏块
  1083.  * 要判断坏块时,利用随意读命令来读取2054地址的内容是否为0x44,正常是0xff
  1084.  * 2, 坏块的特性是:当编程/擦除这个块时,不能将某些位拉高,这 会造成Page Program和
  1085.  * Block Erase操作时的错误,相应地反映到Status Register的相应位。
  1086.  * 我们去查看 Status Register 的相应位,我们就可以找到这个坏块是怎么来的了。即可知是什么原因造成的坏块。
  1087.  * 3, NAND Flash出厂时在OOB中已经反映出了坏块信息,因此,如果在擦除一个块之前,
  1088.  * 一定要先check一下OOB的第6个byte是否是0xff,如果是就证明这是一个好块,
  1089.  * 可以擦除;如果是非0xff,那么就不能擦除。
  1090.  * 当然,这样处理可能会犯一个错误―――“错杀伪坏块”,因为在芯片操作过程中可能
  1091.  * 由于电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可 靠性及软件设计
  1092.  * 的简单化,我们就要奉行“蒋委员长”的“宁可错杀一千,也决不放过一个”的宗旨。
  1093.  *            4, 如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,
  1094.  * 首先就要把其他好的page里面的内容备份到另外一个空的好块里面,然后,把这个块标记为
  1095.  * 坏块。
  1096.  * 5, 当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完页备份之后,
  1097.  * 再将这个块擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!
  1098.  * 6, 1个块有128K,64个页,为什么是第1页的OOB区的第6个Byte?这个是各个厂家默认的规定。
  1099.  * 即这是NAND Flash生产商的默认约定,你可以看到Samsung,Toshiba,STMicroelectronics都是使用这个Byte作为坏块标记的。
  1100.  */
  1101. unsigned int nand_IsBadBlock(unsigned long block)
  1102. {
  1103.         unsigned char ch;
  1104.         
  1105.         //如果是第0块,0*64=0,则第1个参数是第0页
  1106.         //如果是第1块,1*64=64,则第1个参数是第64页。
  1107.         //第0~63页是第0块,第64页是第1块最开始的一页
  1108.         ch = nand_read_random(block*64, 2048+6);
  1109.         if(0xFF == ch) //是好块
  1110.         {
  1111.                 return 0;
  1112.         }
  1113.         else //是坏块
  1114.         {
  1115.                 return 1;
  1116.         }
  1117. }


  1118. /*
  1119. *函数功能 : 标注坏块,标注该页所在的块为坏块
  1120. *函数参数 : 块的编号,从第0块开始
  1121. *函数返回值 :
  1122. * 0 : 标注坏块 成功
  1123. * 1 : 标注坏块 失败
  1124. *注意 : 1,要标注坏块时,利用随意写命令来向2048+6地址写0x33
  1125. * 2, 输入参数都为块地址,也就是即使仅仅一页出现问题,我们也标注整个块为坏块。
  1126. * 3,标注位置 : 约定是在 本块,第0页的第2054个字节处。即本块第0页的spare区的第6个字节处。其他页没有。
  1127. */
  1128. unsigned int nand_MarkBadBlock(unsigned long block)
  1129. {
  1130.         unsigned char flag;

  1131.         //如果是第0块,0*64=0,则第1个参数是第0页
  1132.         //如果是第1块,1*64=64,则第1个参数是第64页。
  1133.         //第0~63页是第0块,第64页是第1块最开始的一页
  1134.         flag = nand_write_random(block*64, 2048+6, 0x33);
  1135.         if(0 == flag) //写入成功
  1136.         {
  1137.                 return 0; //标注坏块 成功
  1138.         }
  1139.         else
  1140.         {
  1141.                 return 1; //标注坏块 失败
  1142.         }
  1143. }


  1144. /*
  1145. 坏块管理:
  1146. 1,我们以前没有进行坏块管理,为什么也能直接使用nandflash?
  1147. 因为三星公司为了支持nandflash的启动,确保了第0块没有坏块,所以我们读//擦除第0块是不会出现问题的。
  1148. 2,第0块之后的块,是可能出现坏块的。
  1149. 3,本程序没有实现坏块表BBT,有时间要实现一下。
  1150. */

  1151. /*
  1152. 测试

  1153. */
nand.h文件

点击(此处)折叠或打开

  1. #ifndef _NAND_H_
  2. #define _NAND_H_

  3. /*NANDFLASH配置寄存器 : NFCONF:用来设置时序参数宽度,数据位宽*/
  4. #define rNFCONF (*(volatile unsigned long *)0x4E000000)
  5. /*NANDFLASH控制寄存器 : NFCONT:用来禁止/使能nandflash控制器,使能/禁止片选信号*/
  6. #define rNFCONT (*(volatile unsigned long *)0x4E000004)
  7. /*NANDFLASH命令集寄存器 : NFCMMD:用来向nandflash发出命令信号*/
  8. #define rNFCMMD (*(volatile unsigned long *)0x4E000008)
  9. /*NANDFLASH地址集寄存器 : NFADDR:用来向nandflash发出地址信号*/
  10. #define rNFADDR (*(volatile unsigned long *)0x4E00000C)
  11. /*NANDFLASH数据寄存器 : NFDATA,32位=0x4E000010+0x4E000011+0x4E000012+0x4E000013*/
  12. #define rNFDATA (*(volatile unsigned long *)0x4E000010)
  13. /*截取NANDFLASH数据寄存器的8位 : 仅0x4E000010,实际上每次读写也只用到低8位,不可能用到32位。
  14. 每次读写此寄存器,就将启动一次对nandflash的读数据写数据的操作。*/
  15. #define rNFDATA8 (*(volatile unsigned char *)0x4E000010)
  16. /*NANDFLASH运行状态寄存器 : NFSTAT:只用了最低位,0:busy,1:ready*/
  17. #define rNFSTAT (*(volatile unsigned long *)0x4E000020)





  18. /************************************************************************/
  19. //下面3个寄存器 : 是在读取nandflash数据的时候,需要将已有的ECC写入下面3个寄存器,已有的ECC保存在OOB区中。
  20. //读取完毕之后,会自动算出一个ECC,再将这个已有的ECC和这个刚算出来的ECC比较。比较的结果会自动放在NFESTAT寄存器中。
  21. /*main区ECC寄存器0,本nandflash只有8位IO口
  22. 只有16位数据有效,但不是连续的16位 : 0~7位:ECC0; 16~23位:ECC1*/
  23. #define rNFMECCD0 (*(volatile unsigned long *)0x4e000014)
  24. /*main区ECC寄存器1,本nandflash只有8位IO口
  25. 只有16位数据有效,但不是连续的16位 : 0~7位:ECC2; 16~23位:ECC3*/
  26. #define rNFMECCD1 (*(volatile unsigned long *)0x4e000018)
  27. /*spare区ECC寄存器,本nandflash只有8位IO口
  28. 只有16位数据有效,但不是连续的16位 : 0~7位:spare区ECC0; 16~23位:spare区ECC1*/
  29. #define rNFSECCD (*(volatile unsigned long *)0x4e00001c)

  30. /*nandflash的ECC状态寄存器0*/
  31. #define rNFESTAT0 (*(volatile unsigned long *)0x4e000024)
  32. /*nandflash的ECC状态寄存器1,本nandflash只有8位IO口,所以只用上面的NFESTAT0寄存器就行了,NFESTAT1是适用于nandflash有16位IO口的。*/
  33. #define rNFESTAT1 (*(volatile unsigned long *)0x4e000028)

  34. //下面3个寄存器 : 是在将数据写入nandflash的时候,硬件自动生成的ECC码,硬件自动保存的位置,我们一般再保存到OOB区中。
  35. /*main区ECC寄存器0,只适用于nandflash的低8位IO口
  36. 32位数据均有效 : 0~7位:ECC0; 8~15位:ECC1; 16~23位:ECC2; 24~31位:ECC3*/
  37. #define rNFMECC0 (*(volatile unsigned long *)0x4e00002c)
  38. /*main区ECC寄存器1,本nandflash只有8位IO口,所以只用上面的NFMECC0寄存器就行了,NFMECC1是适用于nandflash有16位IO口的。*/
  39. #define rNFMECC1 (*(volatile unsigned long *)0x4e000030)
  40. /*spare区ECC寄存器,本nandflash只有8位IO口,只用本寄存器的低2个字节,高2个字节是给16位IO的nandflash用的。
  41. 低16位数据有效 : 0~7位:spare区ECC0; 8~15位:spare区ECC1*/
  42. #define rNFSECC (*(volatile unsigned long *)0x4e000034)
  43. /************************************************************************/





  44. /*相关命令如下 : */
  45. /*读NandFlash页时的命令1*/
  46. #define CMD_READ1 0x00
  47. /*读NandFlash页时的命令2*/
  48. #define CMD_READ2 0x30

  49. /*页编程 : 写NandFlash页时的命令1*/
  50. #define CMD_WRITE1 0x80
  51. /*页编程 : 写NandFlash页时的命令2*/
  52. #define CMD_WRITE2 0x10

  53. /*擦除NandFlash块的命令1*/
  54. #define CMD_ERASE1 0x60
  55. /*擦除NandFlash块的命令2*/
  56. #define CMD_ERASE2 0xD0

  57. /*读状态寄存器的命令*/
  58. #define CMD_STATUS 0x70
  59. /*读取芯片ID的命令*/
  60. #define CMD_READID 0x90
  61. /*重启命令*/
  62. #define CMD_RESET 0xFF




  63. /*随意读命令周期1*/
  64. #define CMD_RANDOMREAD1 0x05
  65. /*随意读命令周期2*/
  66. #define CMD_RANDOMREAD2 0xE0
  67. /*随意写命令*/
  68. #define CMD_RANDOMWRITE 0x85




  69. #define NF_CE_L() {(rNFCONT) &= (~(1<<1));}
  70. /*禁止nandfalsh : 控制寄存器NFCONT[1]=1*/
  71. #define NF_CE_H() {(rNFCONT) |= (1<<1);}

  72. /*向存储器发出命令 : 命令寄存器NFCMMD[7:0]*/
  73. #define NF_CMD(data) {(rNFCMMD) = (data);}
  74. /*向存储器发出地址值 : 地址寄存器NFADDR[7:0]*/
  75. #define NF_ADDR(data) {(rNFADDR) = (data);}

  76. /*等待NandFlash准备就绪(即不忙) : 一直等到状态寄存器NFSTAT[0]=1,循环才退出*/
  77. #define NF_WAIT_RB() {while(!((rNFSTAT) & (1<<0)));}
  78. /*清除RB:即检测RB传输*/
  79. #define NF_CLEAR_RB() {(rNFSTAT) |= (1<<2);}
  80. /*等待检测RnB : 一直等到状态寄存器NFSTAT[2]=1,循环才退出*/
  81. #define NF_DETECT_RB {while(!((rNDSTA) & (1<<2)));}

  82. /*从NANDFLASH的IO口中读数据(32位) : 数据寄存器NFDATA*/
  83. #define NF_RDDATA() (rNFDATA)
  84. /*从NANDFLASH的IO口中读数据(8位) : 数据寄存器NFDATA*/
  85. #define NF_RDDATA8() (rNFDATA8)
  86. /*向NANDFLASH的IO口中写数据(32位) : 数据寄存器NFDATA*/
  87. #define NF_WRDATA(data) {(rNFDATA) = (data);}
  88. /*向NANDFLASH的IO口中写数据(8位) : 数据寄存器NFDATA*/
  89. #define NF_WRDATA8(data) {(rNFDATA8) = (data);}




  90. /************************************************************************/
  91. /*复位ECC:初始化ECC编码器/译码器*/
  92. #define NF_RSTECC()             {rNFCONT |= (1<<4);}
  93. /*解锁main区ECC*/
  94. #define NF_MECC_UnLock() {rNFCONT &= (~(1<<5));}
  95. /*锁定main区ECC*/
  96. #define NF_MECC_Lock() {rNFCONT |= (1<<5);}
  97. /*解锁spare区(OOB区)ECC*/
  98. #define NF_SECC_UnLock() {rNFCONT &= (~(1<<6));}
  99. /*锁定spare区(OOB区)ECC*/
  100. #define NF_SECC_Lock() {rNFCONT |= (1<<6);}
  101. /************************************************************************/




  102. /*时序信号参数的宽度*/
  103. #define TACLS 1
  104. #define TWRPH0 3
  105. #define TWRPH1 0

  106. int isBootFromNorFlash(void);
  107. void nand_reset();
  108. void nand_init();
  109. unsigned char nand_readId();
  110. void write_addr(unsigned long addr);
  111. void nand_read(unsigned char *buf, unsigned long start_addr, int size);
  112. void nand_read_1(unsigned char *buf, unsigned long start_addr, int size);
  113. int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size);

  114. unsigned int nand_read2(unsigned char * buf, unsigned long start_addr, int size);
  115. unsigned char nand_read_random(unsigned long page_number, unsigned long addr);
  116. unsigned int nand_read_oob(unsigned char *buf, unsigned long start_addr, int size);
  117. unsigned int nand_read_oob_page(unsigned char * buf, unsigned long page_number);

  118. unsigned char nand_erase(unsigned long block_number);
  119. unsigned int nand_erase_oob(unsigned long block_number);

  120. unsigned int nand_write(unsigned char *buf, unsigned long start_addr, int size);
  121. unsigned int nand_write_random(unsigned long page_number, unsigned long addr, unsigned char data);
  122. unsigned int nand_write_oob(unsigned char *buf, unsigned long start_addr, int size);
  123. unsigned int nand_write_oob_page(unsigned char * buf, unsigned long page_number);

  124. unsigned int nand_IsBadBlock(unsigned long block);
  125. unsigned int nand_MarkBadBlock(unsigned long block);

  126. #endif
nand.lds文件

点击(此处)折叠或打开

  1. SECTIONS { /*.:表示当前*/
  2.     . = 0x31000000;         /*汇编语言中:.text段中_start的值=0x31000000*/
  3.     .text : { *(.text) }
  4.     
  5.     . = ALIGN(4);
  6.     .rodata : {*(.rodata*)}
  7.     
  8.     . = ALIGN(4);
  9.     .data : { *(.data) }
  10.     
  11.     . = ALIGN(4);
  12.     __bss_start = .; /*编译出来的2进制程序比如uboot.bin中不会含有static变量初始化为0的存放的空间。我们把static变量统一放在bss段,运行程序前,将bss段清0,即将static变量初始化为0了*/
  13.     .bss : { *(.bss) *(COMMON) }
  14.     __bss_end = .;
  15. }


  16. /*
  17. 加上nandflash的其他函数之后, head.o init.o nand.o >4K 了,开发板只复制nand的前面4K到启动石。
  18. 使用下面的链接脚本会有局限性。
  19. SECTIONS {
  20.     firtst     0x00000000 : { head.o init.o nand.o}
  21.     second     0x30001000 : AT(4096) { leds.o }
  22. }
  23. 解决方法 :
  24. 1,如果非要使用上面的链接脚本,就必须把nand.c文件控制在很小范围内,
  25. 其他nand相关函数独立编写一个文件nand2.c,比如 :
  26. SECTIONS {
  27.     firtst     0x00000000 : { head.o init.o nand.o}
  28.     second     0x30001000 : AT(4096) { leds.o nand2.o }
  29. }
  30. 2,使用本文件使用的链接脚本,只要保证head.S中bl调用的少量函数(init_uart, nand_init, nand_read)在4K范围内,就可以了
  31. 整个.o文件可以完全大于4K范围。
  32. */
setup.c文件

点击(此处)折叠或打开

  1. #include "setup.h"

  2. int mystrlen(char *str)
  3. {
  4.     int i = 0;
  5.     while (str[i])
  6.     {
  7.         i++;
  8.     }
  9.     return i;
  10. }

  11. void mystrcpy(char *dest, char *src)
  12. {
  13.     while ((*dest++ = *src++) != '\0');
  14. }

  15. /*
  16. *功能:使能ICache
  17. */
  18. void icache_enable()
  19. {
  20.     unsigned int temp = 1<<12;

  21.     asm(
  22.             "mrc p15,0,r0,c1,c0,0\n" /*读出控制寄存器c1的值到r0中*/
  23.             "orr r0,r0,%0\n" /*第12位I位置1,即使能ICache*/
  24.             "mcr p15,0,r0,c1,c0,0\n" /*将修改后的值写入控制寄存器*/
  25.             :
  26.             :"r"(temp)
  27.             :"r0"
  28.         );
  29. }


  30. /*
  31. *功能:禁止ICache
  32. */
  33. void icache_disable()
  34. {
  35.     unsigned int temp = 1<<12;

  36.     asm( /*GCC内联汇编函数*/
  37.             "mrc p15,0,r0,c1,c0,0\n"
  38.             "bic r0,r0,%0\n" /*第12位I位置0,即禁止ICache*/
  39.             "mcr p15,0,r0,c1,c0,0\n"
  40.             :
  41.             :"r"(temp)
  42.             :"r0"
  43.         );
  44. }
setup.h文件

点击(此处)折叠或打开

  1. #ifndef _SETUP_H_
  2. #define _SETUP_H_

  3. int mystrlen(char *str);
  4. void mystrcpy(char *dest, char *src);

  5. void icache_enable();
  6. void icache_disable();

  7. #endif
uart.c文件

点击(此处)折叠或打开

  1. #include "uart.h"

  2. /***********************************************************************************/
  3. //串口相关函数
  4. /*功能:初始化串口UART0
  5. *轮询方式来使用串口
  6. */
  7. void init_uart(void)
  8. {
  9.      /*GPHCON寄存器
  10.     [7:6]=10,GPH2作为TXD0,串口发送数据引脚
  11.     [5:4]=10,GPH3作为RXD0,串口接受数据引脚
  12.     GPH2,GPH3控制串口通道0
  13.     GPH4,GPH5控制串口通道1
  14.     GPH6,GPH7控制串口通道2
  15.     */
  16.         rGPHCON = ((2<<6)|(2<<4)|(0<<2)|(0<<0));
  17.         rGPHUP = ((1<<3)|(1<<2)); /*禁止上拉*/
  18.         
  19.         /*ULCON0串口线路控制寄存器:设置传输格式
  20.         [6]=0,普通模式,非红外工作模式
  21.         [5:3]=000,无奇偶校验位,100=奇校验,101=偶校验,110=固定校验位为1,111=固定校验位为0
  22.         [2]=0,每帧1个停止位,1:每帧2个停止位
  23.         [1:0]=11,8位,每帧发送或接受的数据位的个数。00=5位,01=6位,10=7位
  24.         */
  25.         rULCON0 = 0x3;
  26.         
  27.         /*UCON0:串口控制寄存器:用于选择UART时钟源,设置UART中断方式
  28.         [15:12]:[11:10]选择PCLK时,这4位无效。选择FCLK/n时,表示n值。
  29.         [11:10]=00: 选择PCLK给UART比特率,10:PCLK, 01:UEXTCLK, 11:FCLK/n。
  30.         [9]=1: 中断请求类型
  31.         [8]=0: 中断请求类型
  32.         [7]=0: 禁止超时中断,1:使能超时中断
  33.         [6]=1: 产生接受错误状态中断,1:不产生接受错误状态中断
  34.         [5]=0: 正常操作,非回环模式。发送引脚发送的数据,直接到达接受引脚,一般是测试用。
  35.         [4]=0: 正常操作,不发送断电信号
  36.         [3:2]=01: 发送模式,中断或者轮询,如果中断寄存器不设置,就是轮询。
  37.         [1:0]=01: 接受模式,中断或者轮询
  38.         */
  39.         rUCON0 = 0x5;
  40.         
  41.         /*UFCON:串口FIFO控制寄存器:用于设置是否使用FIFO,设置各FIFO的触发阀值。
  42.         [7:6]=发送FIFO的触发深度,即在发送时,发送FIFO中还剩有多少个数据时,才触发中断,告诉CPU可以继续发送了。
  43.         [5:4]=接受FIFO的触发深度,即在接受时,接受FIFO中接受了多少个数据后,才触发中断,告诉CPU已经有接受到的数据了。
  44.         [3]=
  45.         [2]=TX的FIFO是否复位
  46.         [1]=RX的FIFO是否复位
  47.         [0]=0,不使用FIFO,非FIFO模式,相当于接受和发送时,不使用缓冲寄存器,上面的设置无效。如果这里是1,则上面的位肯定有对应的设置。
  48.         */
  49.         rUFCON0 = 0x0;
  50.         
  51.         /*UMCON:串口MODEM控制寄存器:用于流量控制
  52.         [7:5]=000,规定必须为0
  53.         [4]=0:禁止自动流控制(AFC),即不使用流控。一般不使用这种硬件自动流控制手段。
  54.         [3:1]=000:规定必须为0
  55.         [0]=0:高电平(撤销nRTS),1:低电平(激活nRTS),因为,AFC位禁止,nRTS必须由软件控制。如果AFC使能,则忽略这个位。
  56.         */
  57.         rUMCON0 = 0x0;
  58.         
  59.         /*UBRDIV:波特率分频寄存器:用于设置波特率
  60.         [15:0],波特率分频值。使用UEXTCLK作为输入时钟时,可以设置UBRDIV为0.
  61.         UBRDIV = (int)(UART时钟/(波特率*16))-1
  62.         */
  63.         rUBRDIV0 = UART_BRD;
  64. }

  65. /*功能:向串口发送一个字符
  66. *参数:待发送的字符

  67. UTRSTAT0:用来表明数据是否已经发送完毕,是否已经接受到数据
  68. 串口收发TX/RX状态寄存器,是CPU自动更新的,程序员检测它的状态就行。
  69. [2]:发送移位寄存器空标志。
  70. 检测为0:表示发送移位寄存器非空,或发送缓冲寄存器非空。
  71. 检测为1:表示发送移位寄存器为空,且发送缓冲寄存器为空,程序员可以继续发送数据了。
  72. [1]:发送缓冲区空标志位。
  73. 检测为0:表示发送缓冲寄存器非空,即上次要发送的数据还没有发完。不能太快,否则会覆盖掉。
  74. 检测为1:表示发送缓冲寄存器为空,程序员可以继续发送数据了。(非FIFO模式,非请求中断,非DMA)
  75. 注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
  76. [0]:接收缓冲器数据就绪标志位。
  77. 检测为0:表示接收缓冲器为空。
  78. 检测为1:表示接收缓冲器接收到有效数据,程序员可以取数据了。(非FIFO模式,非请求中断,非DMA)
  79. 注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。

  80. UTXH0:串口发送缓冲寄存器
  81. [7:0]:串口要发送的数据
  82. */
  83. void uart_sendByte(int data)
  84. {
  85.     if(data == '\n')
  86.     {
  87.         while(!(rUTRSTAT0 &0x2));
  88.         WrUTXH0('\r'); /*回车不换行*/
  89.     }
  90.     /*等待,直到发送缓冲区中的数据已经全部发送出去
  91.     这里也可以检测UTRSTAT0[2]*/
  92.     while(!(rUTRSTAT0 &0x2));
  93.     /*向UTXH0寄存器中写入数据,UART即自动将它发送出去
  94.     #define rUTXH0 (*(volatile unsigned char *)0x50000020)    //UART 0 Transmission Hold
  95.     #define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
  96.     所以:WrUTXH0(data); 相当于 rUTXH0 = data;
  97.     */
  98.     WrUTXH0(data);
  99. }

  100. /*功能:向串口打印一个字符串
  101. *参数:待打印的字符串
  102. */
  103. void uart_sendString(char *p)
  104. {
  105.     while(*p)
  106.         uart_sendByte(*p++);
  107. }

  108. /*功能:向串口打印一个字符的16进制格式
  109. *参数:待打印的字符
  110. * 数字0=0x30,数字9=0x39
  111. */
  112. void uart_sendByte_hex(unsigned long val)
  113. {
  114.     // val = 0x1234ABCD
  115.     unsigned long c;
  116.     int i = 0;
  117.     
  118.     uart_sendByte('0');
  119.     uart_sendByte('x');

  120.     for (i = 0; i < 8; i++)
  121.     {
  122.         c = (val >> ((7-i)*4)) & 0xf;
  123.         if((c >= 0) && (c <= 9))
  124.         {
  125.             c = '0' + c;
  126.         }
  127.         else if ((c >= 0xA) && (c <= 0xF))
  128.         {
  129.             c = 'A' + (c - 0xA);
  130.         }
  131.         uart_sendByte((int)c);
  132.     }
  133. }

  134. /*功能:从串口接受一个字符
  135. *返回值:接受到的字符

  136. UTRSTAT0: 串口收发TX/RX状态寄存器
  137. [0]:接收缓冲器数据就绪标志位。
  138. 0:接收缓冲器为空
  139. 1:接收缓冲器接收到有效数据(非FIFO模式,非请求中断,非DMA)
  140. 注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。

  141. URXH0:串口接受缓冲寄存器
  142. [7:0]:串口接受到的数据
  143. */
  144. char uart_getch(void)
  145. {
  146.     /*等待,直到接受缓冲区中有数据*/
  147.     while(!(rUTRSTAT0 & 0x1));
  148.     /*直接读取URXH0寄存器,即可以获得接受到的数据
  149.     #define rURXH0 (*(volatile unsigned char *)0x50000024)    //UART 0 Receive buffer
  150.     #define RdURXH0() (*(volatile unsigned char *)0x50000024)
  151.     所以:return RdURXH0(); 相当于:return rURXH0;
  152.     */
  153.     return RdURXH0();
  154. }

  155. /*功能:从串口接受一个字符串
  156. *参数:输入的字符串
  157. */
  158. void uart_getString(char *string)
  159. {
  160.     char *string2 = string;
  161.     char c;
  162.     while((c=uart_getch()) != '\r')
  163.     {
  164.         if(c == '\b')
  165.         {
  166.             if((int)string2 < (int)string)
  167.             {
  168.                 //uart_printf("\b\b");
  169.                 string--;
  170.             }
  171.         }
  172.         else
  173.         {
  174.             *string++ = c;
  175.             uart_sendByte(c);
  176.         }
  177.     }
  178.     *string = '\0';
  179.     uart_sendByte('\n');
  180. }

  181. /***********************************************************************************/
  182. /*
  183. *功能:整型(int) 转化成 字符型(char)
  184. *注意:不用 % / 符号的话,只能正确打印:0...9的数字对应的字符'0'...'9'
  185. */
  186. void itoa(unsigned int n, char * buf)
  187. {
  188.     int i;

  189.     if(n < 10){
  190.         buf[0] = n + '0';
  191.         buf[1] = '\0';
  192.         return;
  193.     }
  194.     itoa(n / 10, buf);
  195.     
  196.     for(i=0; buf[i]!='\0'; i++);
  197.     
  198.     buf[i] = (n % 10) + '0';
  199.     
  200.     buf[i+1] = '\0';
  201. }

  202. /*
  203. *功能:16进制字(0x) 转化成 字符型(char)
  204. *注意:不用 % / 符号的话,只能正确打印,0...9..15的数字,对应的'0'...'9''A'...'F'
  205. *注意:由于编译问题,这个函数,暂时由uart_sendByte_hex()函数替代
  206. */
  207. void xtoa(unsigned int n, char * buf)
  208. {
  209.     int i;

  210.     if(n < 16)
  211.     {
  212.         if(n < 10)
  213.         {
  214.             buf[0] = n + '0';
  215.         }
  216.         else
  217.         {
  218.             buf[0] = n - 10 + 'a';
  219.         }
  220.         buf[1] = '\0';
  221.         return;
  222.     }
  223.     xtoa(n / 16, buf);
  224.     
  225.     for(i = 0; buf[i] != '\0'; i++);
  226.     
  227.     if((n % 16) < 10)
  228.     {
  229.         buf[i] = (n % 16) + '0';
  230.     }
  231.     else
  232.     {
  233.         buf[i] = (n % 16) - 10 + 'a';
  234.     }
  235.     buf[i + 1] = '\0';
  236. }

  237. /*******************************************************************************************
  238. 为了支持求余求模,需要:
  239. 1,修改makefile如下:增加libgcc的库
  240.     arm-linux-ld -Tuart.lds -o uart_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
  241. 上面一句展开后,其实就是下面的一句:
  242. ##    arm-linux-ld -Tuart.lds -o uart_elf head.o init.o uart.o leds.o /home/wangxc/linux/toolchain/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5/libgcc.a
  243. 2,自己手写libgcc的库。
  244. 这个在uart3实现
  245. *******************************************************************************************/

  246. /*功能:向串口格式化打印一个字符串
  247. *参数:格式化的字符串
  248. *注意:由于求模求余的问题没有解决,所以这里%d输出时,只能输出0~9,10和10以上的不能输出
  249. */
  250. int uart_printf(const char *fmt, ...)
  251. {
  252.     int count = 0;
  253.     char c;
  254.     char *s;
  255.     int n;
  256.     char buf[65];
  257.     va_list ap;
  258.     
  259.     va_start(ap, fmt);
  260.     
  261.     while(*fmt != '\0')
  262.     {
  263.         if(*fmt == '%')
  264.         {
  265.             fmt++;
  266.             switch(*fmt)
  267.          {
  268.                 case 'd': /*整型*/
  269.                     n = va_arg(ap, int);
  270.                     if(n < 0)
  271.                     {
  272.                         //uputchar('-');
  273.                         uart_sendByte('-');
  274.                         n = -n;
  275.                     }    
  276.                     itoa(n, buf);
  277.                     //_uputs(buf);
  278.                     uart_sendString(buf);
  279.                     break;        
  280.                 case 'c': /*字符型*/
  281.                     c = va_arg(ap, int);
  282.                     uart_sendByte(c);
  283.                     break;
  284.                 case 'x': /*16进制*/
  285.                     n = va_arg(ap, int);
  286.                     //uart_sendByte_hex(n); /*由于求模求余编译有问题,所以用uart_sendByte_hex来替代下面的2行代码*/
  287.                     xtoa(n, buf);
  288.                     uart_sendString(buf);
  289.                     break;
  290.                 case 's': /*字符串*/
  291.                     s = va_arg(ap, char *);
  292.                     uart_sendString(s);
  293.                     break;
  294.                 case '%': /*输出%*/
  295.                     uart_sendByte('%');
  296.                     break;
  297.                 default:
  298.                     break;
  299.             }    
  300.         }
  301.         else
  302.         {
  303.             uart_sendByte(*fmt);
  304.             if(*fmt == '\n')
  305.             {
  306.                 //uart_sendByte('\r');
  307.             }
  308.         }
  309.         fmt++;
  310.     }

  311.     va_end(ap);

  312.     return count;
  313. }
uart.h文件

点击(此处)折叠或打开

  1. #ifndef _UART_H_
  2. #define _UART_H_

  3. /**************************************************************************/
  4. //串口相关寄存器
  5. #define rULCON0 (*(volatile unsigned *)0x50000000)
  6. #define rUCON0 (*(volatile unsigned *)0x50000004)
  7. #define rUFCON0 (*(volatile unsigned *)0x50000008)
  8. #define rUMCON0 (*(volatile unsigned *)0x5000000c)
  9. #define rUTRSTAT0 (*(volatile unsigned *)0x50000010)
  10. #define rUBRDIV0 (*(volatile unsigned *)0x50000028)

  11. #define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
  12. #define RdURXH0() (*(volatile unsigned char *)0x50000024)

  13. #define rGPHCON (*(volatile unsigned *)0x56000070)
  14. #define rGPHUP (*(volatile unsigned *)0x56000078)

  15. #define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
  16. #define UART_CLK PCLK // UART0的时钟源设为PCLK
  17. #define UART_BAUD_RATE 115200 // 波特率
  18. #define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)

  19. typedef char * va_list;
  20. #define va_start(ap,p) (ap = (char *) (&(p)+1))
  21. #define va_arg(ap, type) ((type *) (ap += sizeof(type)))[-1]
  22. #define va_argp(ap, type) ((type *) (ap += sizeof(type)))-1
  23. #define va_end(ap)

  24. void init_uart(void);
  25. void uart_sendByte(int data);
  26. void uart_sendString(char *p);
  27. void uart_sendByte_hex(unsigned long val);
  28. char uart_getch(void);
  29. void uart_getString(char *string);

  30. void itoa(unsigned int n, char * buf);
  31. void xtoa(unsigned int n, char * buf);
  32. int uart_printf(const char *fmt, ...);
  33. /**************************************************************************/

  34. #endif

阅读(1548) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~