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

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-02 13:35:28

IIC测试程序,使用AT24C02存储芯片来测试的。
程序功能比较完善,有随意读,写,连续读,写,是使用IIC控制器,再加上一堆寄存器来完成的。
at24c02.c文件

点击(此处)折叠或打开

  1. #include "iic.h"
  2. #include "at24c02.h"
  3. #include "uart.h"
  4. #include "string.h"
  5. #include "interrupt.h"
  6. #include "init.h"

  7. /*
  8.  * 函数功能 : 从at24c02芯片中,指定的一个地址处,读取一个字节数据
  9.  * 函数参数 : address : at24c02芯片的地址
  10.  * 函数返回值 : 该地址处的数据
  11.  * 注意 :
  12.  * 有看数据手册at24xx,第3页,可知,AT24C02A是256字节*8bit=2Kbit的芯片,容量是256字节
  13.  * 使用8位地址模式,就可以寻址到所有的AT24C02A芯片的存储空间。
  14.  *            第8页,可知,AT24C02A固化地址前面7位固定是1|0|1|0|A2|A1|A0|
  15.  *            第8位,0 : 写, 1 :
  16.  *            AT24C02A在TQ2440原理图中,A0,A1,A2是0,0,0,
  17.  *            所以AT24C02A,作为从机,从机地址是0xA0
  18.  */
  19. unsigned char at24cxx_read(unsigned char address)
  20. {
  21.         unsigned char val;

  22.         iic_write(0xA0, &address, 1);
  23.         iic_read(0xA1, (unsigned char *)&val, 1);
  24.         
  25.         return val;
  26. }

  27. /*
  28.  * 函数功能 : 向at24c02芯片中,指定的一个地址处,写入一个字节的数据
  29.  * 函数参数 :
  30.  * address : at24c02芯片的地址
  31.  * data : 待写入的数据
  32.  * 注意 : 本函数所说的地址address, 是芯片内部的地址,不是芯片的固化地址。
  33.  */
  34. void at24cxx_write(unsigned char address, unsigned char data)
  35. {
  36.         unsigned char val[2];
  37.         
  38.         val[0] = address; //芯片内部的地址
  39.         val[1] = data;
  40.         //AT24C02A固化地址:0xA0
  41.         //uart_printf("at24cxx_write address = [%d] data = [%d]\n", address, data);
  42.         iic_write(0xA0, val, 2);
  43.         //uart_printf("at24cxx_write address ok\n");
  44. }
at24c02.h文件

点击(此处)折叠或打开

  1. #ifndef _AT24C02_H_
  2. #define _AT24C02_H_

  3. unsigned char at24cxx_read(unsigned char address);
  4. void at24cxx_write(unsigned char address, unsigned char data);

  5. #endif
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: head.S head.o init.o nand.o interrupt.o uart.o touchscreen.o main.o
  3. @ 功能: 设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
  4. @******************************************************************************
  5. .text
  6. .global _start
  7. _start:
  8. @******************************************************************************
  9. @ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
  10. @******************************************************************************
  11.     b Reset

  12. @ 0x04: 未定义指令中止模式的向量地址
  13. HandleUndef:
  14.     b HandleUndef
  15.  
  16. @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
  17. HandleSWI:
  18.     b HandleSWI

  19. @ 0x0c: 指令预取终止导致的异常的向量地址
  20. HandlePrefetchAbort:
  21.     b HandlePrefetchAbort

  22. @ 0x10: 数据访问终止导致的异常的向量地址
  23. HandleDataAbort:
  24.     b HandleDataAbort

  25. @ 0x14: 保留
  26. HandleNotUsed:
  27.     b HandleNotUsed

  28. @ 0x18: 中断模式的向量地址
  29.     b HandleIRQ

  30. @ 0x1c: 快中断模式的向量地址
  31. HandleFIQ:
  32.     b HandleFIQ

  33. Reset:
  34.     ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
  35.     bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
  36.     bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
  37.     bl memsetup @ 设置存储控制器以使用SDRAM
  38.     bl nand_init @ 初始化NAND Flash
  39. @******************************************************************************
  40. @ 注意 : 代码的运行地址,并非代码的连接地址,所以上面的函数 disable_watch_dog clock_init memsetup nand_init
  41. @ 上面的函数中,都不能有全局变量,因为全局变量和static变量一起,放在bss段中,大于4K地址范围了,bl是4K内可以跳转,所以函数使用全局变量会失败
  42. @******************************************************************************
  43.                             @ 复制nand中4k后的代码到SDRAM中
  44.     ldr r0, =_start         @ 1. 目标地址 = 0x30000000,这是SDRAM的起始地址
  45.     mov r1, #0 @ 2. 源地址 = 0,代码保存在NAND Flash 0地址开始处
  46.     ldr r2, =__bss_start
  47.     sub r2, r2, r0 @ 3. 复制长度
  48.     bl CopyCode2SDRAM @ 调用C函数CopyCode2SDRAM

  49.     bl clean_bss @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段

  50.     msr cpsr_c, #0xd2 @ 进入中断模式
  51.     ldr sp, =0x31000000 @ 设置中断模式栈指针

  52.     msr cpsr_c, #0xdf @ 进入系统模式
  53.     ldr sp, =0x34000000 @ 设置系统模式栈指针,
  54. /*
  55.     ldr lr, =ret_inituart @ 设置返回地址
  56.     ldr pc, =init_uart            @ 这里为什么不能用 bl init_uart ???,也可以在main函数里面初始化
  57. ret_inituart:
  58.         
  59.         
  60.     ldr lr, =ret_initirq @ 设置返回地址
  61.     ldr pc, =init_irq @ 调用中断初始化函数
  62. ret_initirq:
  63. */
  64.     msr cpsr_c, #0x5f @ 设置I-bit=0,开IRQ中断

  65.     ldr lr, =halt_loop @ 设置返回地址
  66.     ldr pc, =main @ 调用main函数
  67. halt_loop:
  68.     b halt_loop

  69. HandleIRQ:
  70.     sub lr, lr, #4 @ 计算返回地址
  71.     stmdb { r0-r12,lr } @ 保存使用到的寄存器
  72.                                     @ 注意,此时的sp是中断模式的sp
  73.                                     @ 初始值是上面设置的4096
  74.     
  75.     ldr lr, =int_return @ 设置调用IRQ_Handle函数后的返回地址
  76.     ldr pc, =EINT_Handle @ 调用中断分发函数,在interrupt.c中
  77. int_return:
  78.     ldmia { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
iic.c文件

点击(此处)折叠或打开

  1. /*
  2.  * FILE: i2c.c
  3.  * 用于主机发送/接收
  4.  */

  5. #include "iic.h"
  6. #include "uart.h"
  7. #include "string.h"
  8. #include "interrupt.h"
  9. #include "init.h"

  10. static S3C2440_IIC s3c2440_iic;

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

  21. /*
  22.  * IIC初始化
  23.  */
  24. void iic_init(void)
  25. {
  26.         /*设置GPIO相关引脚为 IIC引脚*/
  27.     GPEUP |= 0xc000; // 禁止内部上拉
  28.     GPECON |= 0xa0000000; // 选择引脚功能:GPE15:IICSDA 数据线, GPE14:IICSCL 时钟线

  29.         /*设置中断屏蔽寄存器,一级中断
  30.         *INTMSK[27]:0使能/1禁止 INT_IIC
  31.         */
  32.     INTMSK &= ~(BIT_IIC);

  33.         /*多主机 IIC总线控制(IICCON)寄存器
  34.         * bit[7] = 1, 使能ACK
  35.         * bit[6] = 0, IICCLK = PCLK/16
  36.         * bit[5] = 1, 使能IIC发送/接收中断
  37.         * bit[3:0] = 0xf, Tx clock = IICCLK/(15+1)
  38.         * PCLK = 50MHz, IICCLK = 3.125MHz, 发送器时钟 Tx Clock = 0.195MHz
  39.         */
  40.     IICCON = (1<<7) | (0<<6) | (1<<5) | (0xf); // 0xaf

  41.         /*多主机 IIC总线地址(IICADD)寄存器
  42.         * bit[7:1] = 1, 使能ACK
  43.         */
  44.     IICADD = 0x10; // S3C2440 从机地址 = [7:1]

  45.         /*多主机 IIC总线控制/状态(IICSTAT)寄存器
  46.         * bit[7:6] = 00, 从机接收器模式
  47.         * bit[5] = 0, 总线空闲
  48.         * bit[4] = 0, 使能发送/接收功能
  49.         * bit[3:0] = 0xf, Tx clock = IICCLK/(15+1)
  50.         */
  51.     IICSTAT = 0x10; // I2C串行输出使能(Rx/Tx)
  52.     
  53.     request_irq(ISR_IIC_OFT, iic_interrupt); /*安装IIC中断处理函数*/
  54. }

  55. /*
  56.  * 函数功能 : 主机发送
  57.  * 函数参数 :
  58.  * slv_addr : 从机地址
  59.  * buf : 数据存放的缓冲区
  60.  * len : 数据长度
  61.  * 注意 : 操作步骤,查看芯片手册at24xx,第9页,Figure 2 : 任意写。Figure 3 : 页写。
  62.  */
  63. void iic_write(unsigned int slv_addr, unsigned char *buf, int len)
  64. {
  65.         uart_printf("iic_write address buf[0]=[%d], buf[1]=[%d]\n", buf[0], buf[1]);
  66.     s3c2440_iic.mode = WRDATA;                //写操作
  67.     s3c2440_iic.date_offset = 0;        //索引值初始为0
  68.     s3c2440_iic.data = buf;                        //保存缓冲区地址
  69.     s3c2440_iic.data_len = len;             //传输长度
  70.     
  71.     /*在IICSTAT[4]=1时,才可以写入将要发送的数据,这里是数据是IIC芯片的固化地址0xA0*/
  72.     IICDS = slv_addr;
  73.     /*
  74.     此时,没有中断发生,IICCON[4]读取出来应该是0,
  75.     IICSTAT[7:6]=11,主机发送器模式,
  76.     令IICSTAT[5:4]=11,将发出S信号,之后IICDS中的地址数据也将自动发送,用来寻址从机。
  77.     此时,将从IIC总线锁存7位从机地址到 IICADD寄存器。
  78.     */
  79.     IICSTAT = 0xf0;
  80.     /*此时,已经启动IIC传输,并且是主机发送器模式。另外IICCON[5]=1,表示已经使能中断了。
  81.     *当发送地址信息到一个从机地址并吻合,收到ACK信号时,即响应周期之后,IIC中断将会发生。
  82.     *一旦中断发生,IICCON[4]将等于1,IIC将停止传输。
  83.     */

  84.     /* 等待直至数据传输完毕 */
  85.     while(1)
  86.        {
  87.                uart_printf("\n iic_write s3c2440_iic.data_len = [%d]\n", s3c2440_iic.data_len);
  88.                if(s3c2440_iic.data_len == -1)
  89.                {
  90.                        uart_printf("\n iic_write s3c2440_iic.data_len = [%d]\n", s3c2440_iic.data_len);
  91.                        break;
  92.                }
  93.        }
  94.     uart_printf("iic_write ok\n");
  95. }

  96. /*
  97.  * 函数功能 : 主机接收
  98.  * 函数参数 :
  99.  * slv_addr : 从机地址
  100.  * buf : 数据存放的缓冲区
  101.  * len : 数据长度
  102.  * 注意 : 操作步骤,查看芯片手册at24xx,第9页,Figure 5 : 任意读。Figure 6 : 连续读。
  103.  */
  104. void iic_read(unsigned int slv_addr, unsigned char *buf, int len)
  105. {
  106.     s3c2440_iic.mode = RDDATA;        //读操作
  107.     s3c2440_iic.date_offset = -1;    //索引值初始化为-1,表示第1个中断时不接收数据(地址中断)
  108.     s3c2440_iic.data = buf;                //保存缓冲区地址
  109.     s3c2440_iic.data_len = len;        //传输长度

  110.     IICDS = slv_addr; //IIC芯片的固化地址0xA0
  111.     /*
  112.     IICSTAT[7:6]=10,主机接收器模式,
  113.     令IICSTAT[5:4]=11,将发出S信号,之后IICDS中的固化地址数据也将自动发送,用来寻址从机。
  114.     此时,将从IIC总线锁存7位从机地址到 IICADD寄存器。
  115.     */
  116.     IICSTAT = 0xb0; // 主机接收,启动
  117.     
  118.     /* 等待直至数据传输完毕 */
  119.     while(1)
  120.     {
  121.             uart_printf("\n iic_read s3c2440_iic.data_len = [%d]\n", s3c2440_iic.data_len);
  122.             if(s3c2440_iic.data_len == 0)
  123.             {
  124.                     uart_printf("\n iic_read s3c2440_iic.data_len = [%d]\n", s3c2440_iic.data_len);
  125.                     break;
  126.             }
  127.     }
  128.     uart_printf("iic_read ok\n");
  129. }

  130. /*
  131.  * IIC中断服务程序 : 支持页写,连续读
  132.  * 根据剩余的数据长度选择继续传输或者结束
  133.  * 注意 : 操作步骤,查看芯片手册at24xx,第9页,Figure 2 : 任意写。Figure 3 : 页写。
  134.  * 注意 : 操作步骤,查看芯片手册at24xx,第9页,Figure 5 : 任意读。Figure 6 : 连续读。
  135.  * 问题 : 如果不使用全局变量,如何区分是哪种情况下发生的中断???
  136.  */
  137. void iic_interrupt(void)
  138. {
  139.         unsigned long iic_stat; /*保存寄存器IICSTAT的值*/

  140.         //清中断(中断处理前:最好清除所有的挂起寄存器; 完成之后:必须清除所有的挂起寄存器)
  141.         SRCPND = BIT_IIC;
  142.         INTPND = BIT_IIC;
  143.         iic_stat = IICSTAT;

  144.     /*
  145.     IICSTAT[4]=0,表示总线仲裁成功,即本次传输抢到总线,因为只有一个主机,所以每次都可以抢到总线。
  146.     */
  147.     //uart_printf("IICSTAT=[%x]\n", IICSTAT);
  148.         if(iic_stat & 0x8)
  149.         {
  150.                 uart_printf("Bus arbitration failed\n\r");
  151.         }
  152.         else //IICSTAT[4]=0,本次传输抢到总线
  153.         {
  154.                 switch(s3c2440_iic.mode)
  155.                 {
  156.                         case WRDATA: //写模式
  157.                  {
  158.                          uart_printf("s3c2440_iic.mode=[WRDATA]\n");
  159.                  if(s3c2440_iic.data_len == 0) //想发送的数据已经写完了
  160.                  {
  161.                          //uart_printf("s3c2440_iic.data_len=[%d]\n", s3c2440_iic.data_len);
  162.                          s3c2440_iic.data_len--; //写函数iic_write还在循环等待这个值变小。
  163.                  //下面两行用来恢复(唤醒)I2C操作,发出P信号
  164.                  /*
  165.                                  此时,有中断发生,IICCON[4]读取出来应该是1,停止所有的IIC传输。
  166.                                  IICSTAT[7:6]=11,主机发送器模式,令IICSTAT[5:4]=00,将发出P信号。
  167.                                  */
  168.                  IICSTAT = 0xd0;
  169.                  /*虽然本次写数据已经完成,但是最后,还是要向IICCON[4]=0,恢复刚才暂停的IIC操作,
  170.                  以便可以继续传输数据,
  171.                  */
  172.                  IICCON = 0xaf;
  173.                  wait(100); //等待一段时间以便P信号已经发出
  174.                  break;
  175.                  }
  176.                  //走到这里说明:想发送的数据,还没有发送完,继续将data中的下一个数据,保存到IICDS。
  177.                  //uart_printf("s3c2440_iic.date_offset=[%d]\n", s3c2440_iic.date_offset);
  178.                  IICDS = s3c2440_iic.data[s3c2440_iic.date_offset];
  179.                  //uart_printf("IICDS=[%d]\n", IICDS);
  180.                  s3c2440_iic.date_offset++;

  181.                  //将数据写入IICDS后,需要一段时间才能出现在SDA线上
  182.                  wait(100);
  183.                  /*此时,IICCON[4]读取出来应该是1,停止所有的IIC传输。
  184.                  2440此时作为发送器,已经将要发送的数据写入IICDS寄存器中,
  185.                  向IICCON[4]写0,即可恢复IIC操作,即可立马发送 IICDS中的数据。
  186.                  当发送完一个字节的数据之后,又会产生新的IIC中断了。
  187.                  IICCON[3:0]写f,是位了保存在IIC初始化函数中设置的值一致。
  188.                  */
  189.                  IICCON = 0xaf; //恢复I2C传输
  190.                  s3c2440_iic.data_len--;
  191.                  break;
  192.                  }
  193.                  case RDDATA: //读模式
  194.                  {
  195.                          uart_printf("s3c2440_iic.mode=[RDDATA]\n");
  196.                  if(s3c2440_iic.date_offset == -1)
  197.                  {
  198.                  //这次中断的原因:2440发送IIC设备的固化地址后发生的
  199.                  //只接收一个数据时,不要发出ACK信号
  200.                  s3c2440_iic.date_offset = 0;
  201.                  if(s3c2440_iic.data_len == 1) //只读取最后一位数据了
  202.                  {
  203.                          /*
  204.                          因为此时2440是主机接收器模式,IICCON[7]写0:表示2440在接收数据完成之后,不发送ACK信号
  205.                          向IICCON[4]写0,即可恢复IIC操作。
  206.                          因为此时2440是主机接收器模式,所以,可以从IICDS寄存器中读取接收到的数据。
  207.                          */
  208.                                                 IICCON = 0x2f; //恢复I2C传输,开始接收数据,接收到数据后不发出ACK
  209.                                         }
  210.                  else
  211.                  {
  212.                          /*
  213.                          因为此时2440是主机接收器模式,IICCON[1]写0:表示2440在接收数据完成之后,发送ACK信号,2440可以连续读。
  214.                          向IICCON[4]写0,即可恢复IIC操作。
  215.                          因为此时2440是主机接收器模式,所以,可以从IICDS寄存器中读取接收到的数据。
  216.                          */
  217.                                                 IICCON = 0xaf; //恢复I2C传输,开始接收数据
  218.                                         }
  219.                  break;
  220.                  }
  221.                  /*从IICDS寄存器中读取接收到的数据*/
  222.                                 s3c2440_iic.data[s3c2440_iic.date_offset] = IICDS;
  223.                                 s3c2440_iic.date_offset++;

  224.                                 s3c2440_iic.data_len--;
  225.                  if(s3c2440_iic.data_len == 0) //所有数据都读取完成
  226.                  {
  227.                                         //下面两行恢复(唤醒)I2C操作,发出P信号
  228.                                         /*
  229.                                  此时,有中断发生,IICCON[4]读取出来应该是1,停止所有的IIC传输。
  230.                                  IICSTAT[7:6]=10,主机接收器模式,令IICSTAT[5:4]=00,将发出P信号。
  231.                                  */
  232.                                         IICSTAT = 0x90;
  233.                                         /*向IICCON[4]写0,即可恢复IIC操作。此时在主机接收器模式,以便可以发出P信号。*/
  234.                                         IICCON = 0xaf;
  235.                                         wait(100); //等待一段时间以便P信号已经发出
  236.                                         break;
  237.                  }
  238.                                 else //数据还未读取完成
  239.                                 {
  240.                                         if(s3c2440_iic.data_len == 1) //接收最后一个数据时,不要发出ACK信号
  241.                                         {
  242.                                                 IICCON = 0x2f; // 恢复(唤醒)I2C传输,接收到下一数据时无ACK
  243.                                         }
  244.                                         else //接收的不是最后一个数据时,要发出ACK信号,以便继续接收下一个数据。
  245.                                         {
  246.                                                 IICCON = 0xaf; // 恢复(唤醒)I2C传输,接收到下一数据时发出ACK
  247.                                         }
  248.                                 }
  249.                                 break;
  250.                  }
  251.                  default:
  252.                  break;
  253.                 }
  254.         }
  255. }



  256. /*
  257. 芯片介绍:
  258. AT24C02 : 是具有IIC接口的EEPROM,大小是:256字节*8位=2K bit,
  259. 所以 AT24C02的内部地址只用8根地址线即可全部寻址。

  260. AT24C04 : 4k的EEPROM,大小是:512字节*8位=4K bit,
  261. 所以 AT24C04的内部地址要用9根地址线才可全部寻址。
  262. AT24C04的固话地址的低[1]是P0,相对于是第9根地址线了,
  263. 这样EEPROM就会根据P0=0,选择页0, P1=0,选择页1。

  264. AT24C08 有P0,P1。 AT24C16 有P0,P1,P2 同理,可以寻址多个页。
  265. 每个页的空间大小是256字节。

  266. 图解4 :
  267. CURRENT ADDRESS READ:
  268. The internal data word address counter maintains the last address accessed during the last read
  269. or write operation, incremented by one.
  270. This address stays valid between operations as long as the chip power is maintained.
  271. The address “roll over” during read is from the last byte of the last memory page to the first byte of the first page.
  272. The address “roll over” during write is from the last byte of the current page to the first byte of the same page.

  273. Once the device address with the read/write select bit set to one is clocked in and acknowledged by the EEPROM, the current address data word is serially clocked out.
  274. The microcontroller does not respond with an input zero but does generate a following stop condition (refer to Figure 4).

  275. 内部数据字地址计数器保持在过去的读或写操作时,递增一最后一次访问的地址。
  276. 这个地址在操作之间有效,只要芯片功率保持。
  277. 在读地址“翻转”是从存储器的最后页到第一页的第一个字节的最后一个字节。
  278. 写在地址“翻转”是从当前页的同一页的第一个字节的最后一个字节。
  279. 一旦与读取设备地址/写选择位为1的时钟并得到EEPROM应答后,当前地址的数据字就会串行输出。
  280. 该微控制器与一个不返回应答信号,而是产生一个紧随的停止条件(参见图4)。


  281. 比如,图解5,最后2440不发出一个ACK回应,不发出Stop命令,
  282. 直接来图解4,发出START命令,读取的是下一个递增的地址。
  283. 另外,图解5,最后2440给出一个ACK回应,不发出Stop命令,
  284. 直接来图解6,读取的也是下一个递增的地址。
  285. 之所以叫当前地址读,刚才操作的地址的下一个地址,就是当前地址.

  286. */
iic.h文件

点击(此处)折叠或打开

  1. #ifndef _IIC_H_
  2. #define _IIC_H_

  3. /* IIC 相关的 GPIO 寄存器 */
  4. #define GPECON        (*(volatile unsigned long *)0x56000040)
  5. #define GPEDAT        (*(volatile unsigned long *)0x56000044)
  6. #define GPEUP            (*(volatile unsigned long *)0x56000048)

  7. /* IIC 相关的寄存器 */
  8. #define IICCON     (*(volatile unsigned long *)0x54000000)    //多主机 IIC总线控制(IICCON)寄存器
  9. #define IICSTAT     (*(volatile unsigned long *)0x54000004)    //多主机 IIC总线控制/状态(IICSTAT)寄存器
  10. #define IICADD     (*(volatile unsigned long *)0x54000008)    //多主机 IIC总线地址(IICADD)寄存器
  11. #define IICDS     (*(volatile unsigned long *)0x5400000c)    //多主机 IIC总线发送/接收数据移位(IICDS)寄存器
  12. #define IICLC            (*(volatile unsigned long *)0x54000010)    //多主机 IIC总线线控制(IICLC)寄存器

  13. void iic_init(void);
  14. void iic_write(unsigned int slvAddr, unsigned char *buf, int len);
  15. void iic_read(unsigned int slvAddr, unsigned char *buf, int len);
  16. void iic_interrupt(void);


  17. /*
  18. 因为IIC中断发生时,无法判断是哪种,没有哪一个寄存器说明是什么情况下发生的中断,
  19. 所以,需要一个全局的结构体来保存相关信息,以便在中断服务程序中判断是什么情况下产生的IIC中断。
  20. */
  21. typedef struct iic_struct
  22. {
  23.         unsigned char * data;            /*数据缓冲区*/
  24.         volatile int data_len;        /*等待传输的数据长度*/
  25.         volatile int stat;                /*状态*/
  26.         volatile int mode;                /*模式:读/*/
  27.         volatile int date_offset;    /*data中待传输数据的位置*/
  28. }S3C2440_IIC;

  29. #define WRDATA (1)
  30. #define RDDATA (2)

  31. #endif
iic.lds文件

点击(此处)折叠或打开

  1. SECTIONS { /*.:表示当前*/
  2.     . = 0x30000000;         /*汇编语言中:.text段中_start的值=0x30000000*/
  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) } /*全局变量和static变量一起,放在bss段中*/
  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. */
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. /*
  63. *清空bss段
  64. */
  65. void clean_bss(void)
  66. {
  67.     extern int __bss_start, __bss_end; /*这2个在链接脚本中定义的变量*/
  68.     int *p = &__bss_start;
  69.     
  70.     for(; p < &__bss_end; p++)
  71.        {
  72.                 *p = 0;
  73.         }
  74. }
init.h文件

点击(此处)折叠或打开

  1. #ifndef _INIT_H_
  2. #define _INIT_H_

  3. //按键相关的GPIO
  4. #define GPFCON (*(volatile unsigned long *)0x56000050)
  5. #define GPFDAT (*(volatile unsigned long *)0x56000054)
  6. #define GPFUP (*(volatile unsigned long *)0x56000058)
  7. /*注意:这个程序没有mmu,所以寄存器的地址是物理地址*/
  8. /**************************LED相关寄存器*******************************/
  9. #define GPBCON (*(volatile unsigned long *)0x56000010)
  10. #define GPBDAT (*(volatile unsigned long *)0x56000014)
  11. #define GPBUP (*(volatile unsigned long *)0x56000018)
  12. /*注意:这个程序没有mmu,所以寄存器的地址是物理地址*/

  13. #define GPB5_OUT (1<<(5*2))
  14. #define GPB6_OUT (1<<(6*2))
  15. #define GPB7_OUT (1<<(7*2))
  16. #define GPB8_OUT (1<<(8*2))

  17. #define GPB5_ON (~(1<<5))
  18. #define GPB6_ON (~(1<<6))
  19. #define GPB7_ON (~(1<<7))
  20. #define GPB8_ON (~(1<<8))

  21. #define GPB5_OFF (1<<5)
  22. #define GPB6_OFF (1<<6)
  23. #define GPB7_OFF (1<<7)
  24. #define GPB8_OFF (1<<8)
  25. /*********************************************************/

  26. /* WATCHDOG寄存器 */
  27. #define WTCON (*(volatile unsigned long *)0x53000000)
  28. /* SDRAM寄存器 */
  29. #define     MEM_CTL_BASE    0x48000000
  30. #define        SDRAM_BASE        0x30000000
  31. #define        GSTATUS1            (*(volatile unsigned long *)0x560000B0)


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

  36. void disable_watch_dog();
  37. void clock_init(void);
  38. void memsetup();
  39. void clean_bss(void);

  40. #endif
interrupt.c文件

点击(此处)折叠或打开

  1. #include "interrupt.h"
  2. #include "uart.h"
  3. #include "init.h"

  4. /*定义一个函数指针数组 : 数组的每一个元素是一个函数指针,用来保存用户定义的中断处理函数*/
  5. static void (*irq_interrupt_array[32])(void);

  6. void init_irq_interrupt(void) /*没有安装中断函数的时候,使用这个函数来初始化中断服务函数*/
  7. {
  8.     while(1);
  9. }

  10. /*函数功能:初始化函数指针数组
  11. */
  12. void all_init_irq(void)
  13. {
  14.     int i = 0;
  15.     for (i = 0; i < (sizeof(irq_interrupt_array) / sizeof(irq_interrupt_array[0])); i++)
  16.     {
  17.         irq_interrupt_array[i] = init_irq_interrupt;
  18.     }
  19. }

  20. /*
  21. *函数功能:安装中断处理函数
  22. *参数:
  23. * num : 32个一级中断的编号,即中断号
  24. * void (*irq_interrupt)(void) : 是个函数指针,是用户编写的中断处理函数。
  25. *注意:
  26. * 这个函数完整版是:用2个参数:1个表示1级中断,1个表示2级中断(没有2级中断时,用0)
  27. */
  28. int request_irq(int num, void (*irq_interrupt)(void))
  29. {
  30.         irq_interrupt_array[num] = irq_interrupt;

  31.         return 0;
  32. }

  33. /*
  34.  * 功能:初始化按键相关的GPIO引脚为外部中断
  35.  * 本程序:
  36.  * K1 : EINT1,引脚是GPF1
  37.  * K2 : EINT4,引脚是GPF4
  38.  * K3 : EINT2,引脚是GPF2
  39.  * K4 : EINT0,引脚是GPF0
  40.  */
  41. void init_irq(void)
  42. {
  43.      GPFCON |= (GPF0_EINT | GPF1_EINT | GPF2_EINT | GPF4_EINT); /*设置这几个引脚为中断功能*/
  44.     GPFUP = 0x00;
  45.     
  46.     EXTINT0 = 0x00; /*设置外部中断控制寄存器,低电平触发,默认也是0x00,所以这句可以不要*/
  47.         
  48.         /*设置外部中断屏蔽寄存器,EINT4~EINT24,共20个需要在这个寄存器中使能他们,
  49.          *EINTMASK只能使能EINT4, EINT0,EINT1,EINT2,EINT3 不受EINTMASK控制
  50.          *EINTMASK[4]:禁止/使能EINT4
  51.          */
  52.         EINTMASK &= (~(1<<4));
  53.         
  54.         /*设置模式寄存器 :在IRQ模式中处理,默认也是0x00,所以这句可以不要*/
  55.         INTMOD = 0x00;
  56.         
  57.         /*设置中断优先级寄存器
  58.         *仲裁租0和6,优先级不轮换使能,每个仲裁组下优先级顺序:0-1-2-3-4-5-6,默认也是这个顺序
  59.         *最终按键产生中断的优先级顺序:K4(EINT0) > K1(EINT1) > K3(EINT2) > K2(EINT4)
  60.         */
  61.         PRIORITY &= (~(1<<0)) & (~(1<<6)) ;
  62.         
  63.         /*设置中断屏蔽寄存器,一级中断
  64.         *INTMSK[0]:禁止/使能EINT0
  65.         *INTMSK[1]:禁止/使能EINT1
  66.         *INTMSK[2]:禁止/使能EINT2
  67.         *INTMSK[4]:禁止/使能EINT4~EINT7
  68.         */
  69.         INTMSK &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4)); /*其实到这里,中断仍未完全开启,CPSR的I位还要清0*/
  70.         
  71.         all_init_irq();
  72. }

  73. /*
  74. *功能:中断服务子程序
  75. *注意:1,按键产生中断的优先级顺序:K4(EINT0) > K1(EINT1) > K3(EINT2) > K2(EINT4)
  76. * 所以,同时按下其中2个,3个或者4个,只有优先级最高的哪个LED会被点亮
  77. * 2,如果硬件接线有LED连接了EINT4,EINT5,由于他们的中断优先级相同,EINTPEND[4:5]都是1,但都只会让SRCPND[4]=1,
  78. * 如果他们同时按下,则INTPND[4]=1,且INTOFFSET=4,走到case 4,EINTPEND[4:5]=11b,2个if都成立,所以,最后这2个LED会被同时点亮。
  79. */
  80. void EINT_Handle()
  81. {
  82.             /*INTOFFSET:中断偏移寄存器,INTPND寄存器中的值被置1之后,中断偏移寄存器的值=32个一级中断的编号*/
  83.             unsigned long offset = INTOFFSET;

  84.             uart_printf("\n\nINTOFFSET(interrupt no.)=[%d]\n", offset);

  85.             irq_interrupt_array[offset](); //调用对应的irq中断服务函数

  86.             /*中断处理完成之后:必须清除所有的挂起寄存器
  87.             *注意:向挂起寄存器中写1,即可令此位为0;写入0是没有效果的,若写入0,挂起寄存器中的数据保持不变。
  88.             */
  89.             if(offset == 4) /*二级外部中断,还必须清除 EINTPEND*/
  90.             {
  91.                      EINTPEND = (1<<4);
  92.             }
  93.             SRCPND = (1<<offset);
  94.             INTPND = (1<<offset);

  95.             //uart_printf("SRCPND=[0x%x]\n", SRCPND);
  96.             //uart_printf("INTPND=[0x%x]\n", INTPND);
  97.             //uart_sendString("*******************************************\n");
  98.             /*在清除 SRCPND INTPND时,INTOFFSET会被自动清除*/
  99. }
interrupt.h文件

点击(此处)折叠或打开

  1. #ifndef _INTERRUPT_H_
  2. #define _INTERRUPT_H_

  3. /**************************************************************************/
  4. #define GPF0_EINT (2<<(0*2))
  5. #define GPF1_EINT (2<<(1*2))
  6. #define GPF2_EINT (2<<(2*2))
  7. #define GPF3_EINT (2<<(3*2))
  8. #define GPF4_EINT (2<<(4*2))
  9. #define GPF5_EINT (2<<(5*2))
  10. #define GPF6_EINT (2<<(6*2))
  11. #define GPF7_EINT (2<<(7*2))
  12. /**************************************************************************/

  13. /**************************************************************************/
  14. //中断相关寄存器
  15. #define EXTINT0 (*(volatile unsigned long *)0x56000088)
  16. #define EINTMASK (*(volatile unsigned long *)0x560000A4)
  17. #define EINTPEND (*(volatile unsigned long *)0x560000A8)

  18. #define SRCPND (*(volatile unsigned long *)0x4A000000)
  19. #define INTMOD (*(volatile unsigned long *)0x4A000004)
  20. #define INTMSK (*(volatile unsigned long *)0x4A000008)
  21. #define PRIORITY (*(volatile unsigned long *)0x4A00000C)
  22. #define INTPND (*(volatile unsigned long *)0x4A000010)
  23. #define INTOFFSET (*(volatile unsigned long *)0x4A000014)

  24. #define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
  25. #define INTSUBMSK (*(volatile unsigned long *)0x4A00001C)


  26. #define ISR_EINT0_OFT 0
  27. #define ISR_EINT1_OFT 1
  28. #define ISR_EINT2_OFT 2
  29. #define ISR_EINT3_OFT 3
  30. #define ISR_EINT4_7_OFT 4
  31. #define ISR_EINT8_23_OFT 5
  32. #define ISR_NOTUSED6_OFT 6
  33. #define ISR_BAT_FLT_OFT 7
  34. #define ISR_TICK_OFT 8
  35. #define ISR_WDT_OFT 9
  36. #define ISR_TIMER0_OFT 10
  37. #define ISR_TIMER1_OFT 11
  38. #define ISR_TIMER2_OFT 12
  39. #define ISR_TIMER3_OFT 13
  40. #define ISR_TIMER4_OFT 14
  41. #define ISR_UART2_OFT 15
  42. #define ISR_LCD_OFT 16
  43. #define ISR_DMA0_OFT 17
  44. #define ISR_DMA1_OFT 18
  45. #define ISR_DMA2_OFT 19
  46. #define ISR_DMA3_OFT 20
  47. #define ISR_SDI_OFT 21
  48. #define ISR_SPI0_OFT 22
  49. #define ISR_UART1_OFT 23
  50. #define ISR_NOTUSED24_OFT 24
  51. #define ISR_USBD_OFT 25
  52. #define ISR_USBH_OFT 26
  53. #define ISR_IIC_OFT 27
  54. #define ISR_UART0_OFT 28
  55. #define ISR_SPI1_OFT 29
  56. #define ISR_RTC_OFT 30
  57. #define ISR_ADC_OFT 31


  58. // PENDING BIT
  59. #define BIT_EINT0        (0x1)
  60. #define BIT_EINT1        (0x1<<1)
  61. #define BIT_EINT2        (0x1<<2)
  62. #define BIT_EINT3        (0x1<<3)
  63. #define BIT_EINT4_7        (0x1<<4)
  64. #define BIT_EINT8_23    (0x1<<5)
  65. #define BIT_CAM            (0x1<<6)        // Added for 2440.
  66. #define BIT_BAT_FLT        (0x1<<7)
  67. #define BIT_TICK        (0x1<<8)
  68. #define BIT_WDT_AC97    (0x1<<9)
  69. #define BIT_TIMER0        (0x1<<10)
  70. #define BIT_TIMER1        (0x1<<11)
  71. #define BIT_TIMER2        (0x1<<12)
  72. #define BIT_TIMER3        (0x1<<13)
  73. #define BIT_TIMER4        (0x1<<14)
  74. #define BIT_UART2        (0x1<<15)
  75. #define BIT_LCD            (0x1<<16)
  76. #define BIT_DMA0        (0x1<<17)
  77. #define BIT_DMA1        (0x1<<18)
  78. #define BIT_DMA2        (0x1<<19)
  79. #define BIT_DMA3        (0x1<<20)
  80. #define BIT_SDI            (0x1<<21)
  81. #define BIT_SPI0        (0x1<<22)
  82. #define BIT_UART1        (0x1<<23)
  83. #define BIT_NFCON        (0x1<<24)        // Added for 2440.
  84. #define BIT_USBD        (0x1<<25)
  85. #define BIT_USBH        (0x1<<26)
  86. #define BIT_IIC            (0x1<<27)
  87. #define BIT_UART0        (0x1<<28)
  88. #define BIT_SPI1        (0x1<<29)
  89. #define BIT_RTC            (0x1<<30)
  90. #define BIT_ADC            (0x1<<31)
  91. #define BIT_ALLMSK        (0xffffffff)

  92. #define BIT_SUB_ALLMSK    (0x7fff)
  93. #define BIT_SUB_AC97     (0x1<<14)
  94. #define BIT_SUB_WDT     (0x1<<13)
  95. #define BIT_SUB_CAM_S    (0x1<<12)        // Added for 2440.
  96. #define BIT_SUB_CAM_C    (0x1<<11)        // Added for 2440.
  97. #define BIT_SUB_ADC        (0x1<<10)
  98. #define BIT_SUB_TC        (0x1<<9)
  99. #define BIT_SUB_ERR2    (0x1<<8)
  100. #define BIT_SUB_TXD2    (0x1<<7)
  101. #define BIT_SUB_RXD2    (0x1<<6)
  102. #define BIT_SUB_ERR1    (0x1<<5)
  103. #define BIT_SUB_TXD1    (0x1<<4)
  104. #define BIT_SUB_RXD1    (0x1<<3)
  105. #define BIT_SUB_ERR0    (0x1<<2)
  106. #define BIT_SUB_TXD0    (0x1<<1)
  107. #define BIT_SUB_RXD0    (0x1<<0)
  108. /**************************************************************************/

  109. void init_irq(void);
  110. void all_init_irq(void);
  111. void init_irq_interrupt(void);
  112. int request_irq(int num, void (*irq_interrupt)(void));

  113. #endif
main.c文件

点击(此处)折叠或打开

  1. #include "def.h"
  2. #include "init.h"
  3. #include "nand.h"
  4. #include "uart.h"
  5. #include "string.h"
  6. #include "setup.h"
  7. #include "iic.h"
  8. #include "at24c02.h"
  9. #include "interrupt.h"

  10. /*如果在interupt.h的头文件中,没有申明下面的函数 init_irq,
  11. 只在interupt.c的文件中有定义,又想在本文件中使用函数init_irq,
  12. 就需要使用 extern 来申明一下,其他文件中有定义
  13. extern void init_irq(void);
  14. */

  15. /*
  16. *功能:延时函数
  17. */
  18. static void wait(volatile unsigned int num)
  19. {
  20.     unsigned int i = 0;
  21.     unsigned int j = 0;
  22.     for(i=0; i<1000; i++)
  23.         for(j=0; j<num; j++);
  24. }

  25. int iic_test()
  26. {
  27.         char get_ch;
  28.         char str[128];
  29.         int address = 0;
  30.         int data = 0;
  31.     
  32.     iic_init();
  33.     
  34.     while (1)
  35.     {
  36.             memset(str, 0, sizeof(str));
  37.             address = 0;
  38.             data = 0;

  39.         uart_printf("\r\n##### AT24C02 Menu #####\r\n");
  40.         uart_printf("[R] Read AT24C02\n\r");
  41.         uart_printf("[W] Write AT24C02\n\r");
  42.         uart_printf("Enter your selection: ");

  43.         get_ch = uart_getch();
  44.         uart_printf("%c\n\r", get_ch);
  45.         switch(get_ch)
  46.         {
  47.             case 'r':
  48.             case 'R':
  49.             {
  50.                 uart_printf("Enter address: ");
  51.                 memset(str, 0, sizeof(str));
  52.                 uart_getString(str);
  53.                                 address = atoi(str);
  54.                                 uart_printf("\r\n read address = [%d]\r\n", address);
  55.                                 data = at24cxx_read(address);
  56.                                 uart_printf("data = [%d]\r\n", data);
  57.                                 uart_printf("read ok!\n");
  58.                 break;
  59.             }
  60.             case 'w':
  61.             case 'W':
  62.             {
  63.                 uart_printf("Enter address: ");
  64.                 memset(str, 0, sizeof(str));
  65.                 uart_getString(str);
  66.                 address = atoi(str);
  67.                                 uart_printf("get str [%s]\r\n", str);
  68.                 uart_printf("Enter data: ");
  69.                                 uart_getString(str);
  70.                 data = atoi(str);
  71.                                 uart_printf("write address [%d] with data [%d]\r\n", address, data);
  72.                                 at24cxx_write(address, data);
  73.                                 uart_printf("write ok!\n");
  74.                 break;
  75.             }
  76.             default:
  77.                  break;
  78.         }
  79.         uart_printf("continue!\n");
  80.     }
  81.     return 0;
  82. }


  83. int main()
  84. {
  85.         icache_enable();
  86.         init_irq();
  87.         init_uart();
  88.         iic_init();
  89.         
  90.         uart_sendString("\n\n");
  91.         uart_sendString("My iictest start:\n\n");
  92.         
  93.         GPBCON = (GPB5_OUT | GPB6_OUT | GPB7_OUT | GPB8_OUT);
  94.         GPBUP = 0x1e0;
  95.         
  96.         iic_test();
  97.         
  98.         while(1)
  99.         {
  100.          GPBDAT = (GPB5_ON & GPB6_ON & GPB7_ON & GPB8_ON);
  101.          wait(1000);
  102.          GPBDAT = (GPB5_OFF | GPB6_OFF | GPB7_OFF | GPB8_OFF);
  103.          wait(1000);
  104.         }
  105.         
  106.         return 0;
  107. }
makefile文件

点击(此处)折叠或打开

  1. objs := head.o init.o setup.o nand.o main.o uart.o iic.o string.o at24c02.o interrupt.o

  2. iic.bin: $(objs)
  3.     arm-linux-ld -Tiic.lds -o iic_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
  4.     arm-linux-objcopy -O binary -S iic_elf $@
  5.     arm-linux-objdump -D -m arm iic_elf > iic.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 iic.bin iic_elf iic.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
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
string.c文件

点击(此处)折叠或打开

  1. #include "string.h"
  2. #include "uart.h"

  3. char * ___strtok;

  4. /**
  5.  * strcpy - Copy a %NUL terminated string
  6.  * @dest: Where to copy the string to
  7.  * @src: Where to copy the string from
  8.  */
  9. char * strcpy(char * dest,const char *src)
  10. {
  11.     char *tmp = dest;

  12.     while ((*dest++ = *src++) != '\0')
  13.         /* nothing */;
  14.     return tmp;
  15. }

  16. /**
  17.  * strncpy - Copy a length-limited, %NUL-terminated string
  18.  * @dest: Where to copy the string to
  19.  * @src: Where to copy the string from
  20.  * @count: The maximum number of bytes to copy
  21.  *
  22.  * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
  23.  * However, the result is not %NUL-terminated if the source exceeds
  24.  * @count bytes.
  25.  */
  26. char * strncpy(char * dest,const char *src,unsigned int count)
  27. {
  28.     char *tmp = dest;

  29.     while (count-- && (*dest++ = *src++) != '\0')
  30.         /* nothing */;

  31.     return tmp;
  32. }

  33. /**
  34.  * strcat - Append one %NUL-terminated string to another
  35.  * @dest: The string to be appended to
  36.  * @src: The string to append to it
  37.  */
  38. char * strcat(char * dest, const char * src)
  39. {
  40.     char *tmp = dest;

  41.     while (*dest)
  42.         dest++;
  43.     while ((*dest++ = *src++) != '\0')
  44.         ;

  45.     return tmp;
  46. }

  47. /**
  48.  * strncat - Append a length-limited, %NUL-terminated string to another
  49.  * @dest: The string to be appended to
  50.  * @src: The string to append to it
  51.  * @count: The maximum numbers of bytes to copy
  52.  *
  53.  * Note that in contrast to strncpy, strncat ensures the result is
  54.  * terminated.
  55.  */
  56. char * strncat(char *dest, const char *src, unsigned int count)
  57. {
  58.     char *tmp = dest;

  59.     if (count) {
  60.         while (*dest)
  61.             dest++;
  62.         while ((*dest++ = *src++)) {
  63.             if (--count == 0) {
  64.                 *dest = '\0';
  65.                 break;
  66.             }
  67.         }
  68.     }

  69.     return tmp;
  70. }

  71. /**
  72.  * strcmp - Compare two strings
  73.  * @cs: One string
  74.  * @ct: Another string
  75.  */
  76. int strcmp(const char * cs,const char * ct)
  77. {
  78.     register signed char __res;

  79.     while (1) {
  80.         if ((__res = *cs - *ct++) != 0 || !*cs++)
  81.             break;
  82.     }

  83.     return __res;
  84. }

  85. /**
  86.  * strncmp - Compare two length-limited strings
  87.  * @cs: One string
  88.  * @ct: Another string
  89.  * @count: The maximum number of bytes to compare
  90.  */
  91. int strncmp(const char * cs,const char * ct,unsigned int count)
  92. {
  93.     register signed char __res = 0;

  94.     while (count) {
  95.         if ((__res = *cs - *ct++) != 0 || !*cs++)
  96.             break;
  97.         count--;
  98.     }

  99.     return __res;
  100. }

  101. /**
  102.  * strchr - Find the first occurrence of a character in a string
  103.  * @s: The string to be searched
  104.  * @c: The character to search for
  105.  */
  106. char * strchr(const char * s, int c)
  107. {
  108.     for(; *s != (char) c; ++s)
  109.         if (*s == '\0')
  110.             return NULL;
  111.     return (char *) s;
  112. }

  113. /**
  114.  * strrchr - Find the last occurrence of a character in a string
  115.  * @s: The string to be searched
  116.  * @c: The character to search for
  117.  */
  118. char * strrchr(const char * s, int c)
  119. {
  120.        const char *p = s + strlen(s);
  121.        do {
  122.            if (*p == (char)c)
  123.                return (char *)p;
  124.        } while (--p >= s);
  125.        return NULL;
  126. }

  127. /**
  128.  * strlen - Find the length of a string
  129.  * @s: The string to be sized
  130.  */
  131. unsigned int strlen(const char * s)
  132. {
  133.     const char *sc;

  134.     for (sc = s; *sc != '\0'; ++sc)
  135.         /* nothing */;
  136.     return sc - s;
  137. }

  138. /**
  139.  * strnlen - Find the length of a length-limited string
  140.  * @s: The string to be sized
  141.  * @count: The maximum number of bytes to search
  142.  */
  143. unsigned int strnlen(const char * s, unsigned int count)
  144. {
  145.     const char *sc;

  146.     for (sc = s; count-- && *sc != '\0'; ++sc)
  147.         /* nothing */;
  148.     return sc - s;
  149. }

  150. /**
  151.  * strspn - Calculate the length of the initial substring of @s which only
  152.  *     contain letters in @accept
  153.  * @s: The string to be searched
  154.  * @accept: The string to search for
  155.  */
  156. unsigned int strspn(const char *s, const char *accept)
  157. {
  158.     const char *p;
  159.     const char *a;
  160.     unsigned int count = 0;

  161.     for (p = s; *p != '\0'; ++p) {
  162.         for (a = accept; *a != '\0'; ++a) {
  163.             if (*p == *a)
  164.                 break;
  165.         }
  166.         if (*a == '\0')
  167.             return count;
  168.         ++count;
  169.     }

  170.     return count;
  171. }

  172. /**
  173.  * strpbrk - Find the first occurrence of a set of characters
  174.  * @cs: The string to be searched
  175.  * @ct: The characters to search for
  176.  */
  177. char * strpbrk(const char * cs,const char * ct)
  178. {
  179.     const char *sc1,*sc2;

  180.     for( sc1 = cs; *sc1 != '\0'; ++sc1) {
  181.         for( sc2 = ct; *sc2 != '\0'; ++sc2) {
  182.             if (*sc1 == *sc2)
  183.                 return (char *) sc1;
  184.         }
  185.     }
  186.     return NULL;
  187. }

  188. /**
  189.  * strtok - Split a string into tokens
  190.  * @s: The string to be searched
  191.  * @ct: The characters to search for
  192.  *
  193.  * WARNING: strtok is deprecated, use strsep instead.
  194.  */
  195. char * strtok(char * s,const char * ct)
  196. {
  197.     char *sbegin, *send;

  198.     sbegin = s ? s : ___strtok;
  199.     if (!sbegin) {
  200.         return NULL;
  201.     }
  202.     sbegin += strspn(sbegin,ct);
  203.     if (*sbegin == '\0') {
  204.         ___strtok = NULL;
  205.         return( NULL );
  206.     }
  207.     send = strpbrk( sbegin, ct);
  208.     if (send && *send != '\0')
  209.         *send++ = '\0';
  210.     ___strtok = send;
  211.     return (sbegin);
  212. }

  213. /**
  214.  * strsep - Split a string into tokens
  215.  * @s: The string to be searched
  216.  * @ct: The characters to search for
  217.  *
  218.  * strsep() updates @s to point after the token, ready for the next call.
  219.  *
  220.  * It returns empty tokens, too, behaving exactly like the libc function
  221.  * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
  222.  * Same semantics, slimmer shape. ;)
  223.  */
  224. char * strsep(char **s, const char *ct)
  225. {
  226.     char *sbegin = *s, *end;

  227.     if (sbegin == NULL)
  228.         return NULL;

  229.     end = strpbrk(sbegin, ct);
  230.     if (end)
  231.         *end++ = '\0';
  232.     *s = end;

  233.     return sbegin;
  234. }

  235. /**
  236.  * memset - Fill a region of memory with the given value
  237.  * @s: Pointer to the start of the area.
  238.  * @c: The byte to fill the area with
  239.  * @count: The size of the area.
  240.  *
  241.  * Do not use memset() to access IO space, use memset_io() instead.
  242.  */
  243. void * memset(void * s,int c,unsigned int count)
  244. {
  245.     char *xs = (char *) s;

  246.     while (count--)
  247.         *xs++ = c;

  248.     return s;
  249. }

  250. /**
  251.  * bcopy - Copy one area of memory to another
  252.  * @src: Where to copy from
  253.  * @dest: Where to copy to
  254.  * @count: The size of the area.
  255.  *
  256.  * Note that this is the same as memcpy(), with the arguments reversed.
  257.  * memcpy() is the standard, bcopy() is a legacy BSD function.
  258.  *
  259.  * You should not use this function to access IO space, use memcpy_toio()
  260.  * or memcpy_fromio() instead.
  261.  */
  262. void bcopy(const void *src, void *dest, unsigned int count)
  263. {
  264.     char *destTmp = (char *)dest;
  265.     char *srcTmp = (char *)src;

  266.     while (count--)
  267.         *destTmp++ = *srcTmp++;
  268. }

  269. /**
  270.  * memcpy - Copy one area of memory to another
  271.  * @dest: Where to copy to
  272.  * @src: Where to copy from
  273.  * @count: The size of the area.
  274.  *
  275.  * You should not use this function to access IO space, use memcpy_toio()
  276.  * or memcpy_fromio() instead.
  277.  */
  278. void * memcpy(void * dest,const void *src,unsigned int count)
  279. {
  280.     char *tmp = (char *) dest, *s = (char *) src;

  281.     while (count--)
  282.         *tmp++ = *s++;

  283.     return dest;
  284. }

  285. /**
  286.  * memmove - Copy one area of memory to another
  287.  * @dest: Where to copy to
  288.  * @src: Where to copy from
  289.  * @count: The size of the area.
  290.  *
  291.  * Unlike memcpy(), memmove() copes with overlapping areas.
  292.  */
  293. void * memmove(void * dest,const void *src,unsigned int count)
  294. {
  295.     char *tmp, *s;

  296.     if (dest <= src) {
  297.         tmp = (char *) dest;
  298.         s = (char *) src;
  299.         while (count--)
  300.             *tmp++ = *s++;
  301.         }
  302.     else {
  303.         tmp = (char *) dest + count;
  304.         s = (char *) src + count;
  305.         while (count--)
  306.             *--tmp = *--s;
  307.         }

  308.     return dest;
  309. }

  310. /**
  311.  * memcmp - Compare two areas of memory
  312.  * @cs: One area of memory
  313.  * @ct: Another area of memory
  314.  * @count: The size of the area.
  315.  */
  316. int memcmp(const void * cs,const void * ct,unsigned int count)
  317. {
  318.     const unsigned char *su1, *su2;
  319.     int res = 0;

  320.     for( su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
  321.         if ((res = *su1 - *su2) != 0)
  322.             break;
  323.     return res;
  324. }

  325. /**
  326.  * memscan - Find a character in an area of memory.
  327.  * @addr: The memory area
  328.  * @c: The byte to search for
  329.  * @size: The size of the area.
  330.  *
  331.  * returns the address of the first occurrence of @c, or 1 byte past
  332.  * the area if @c is not found
  333.  */
  334. void * memscan(void * addr, int c, unsigned int size)
  335. {
  336.     unsigned char * p = (unsigned char *) addr;

  337.     while (size) {
  338.         if (*p == c)
  339.             return (void *) p;
  340.         p++;
  341.         size--;
  342.     }
  343.       return (void *) p;
  344. }

  345. /**
  346.  * strstr - Find the first substring in a %NUL terminated string
  347.  * @s1: The string to be searched
  348.  * @s2: The string to search for
  349.  */
  350. char * strstr(const char * s1,const char * s2)
  351. {
  352.     int l1, l2;

  353.     l2 = strlen(s2);
  354.     if (!l2)
  355.         return (char *) s1;
  356.     l1 = strlen(s1);
  357.     while (l1 >= l2) {
  358.         l1--;
  359.         if (!memcmp(s1,s2,l2))
  360.             return (char *) s1;
  361.         s1++;
  362.     }
  363.     return NULL;
  364. }

  365. /**
  366.  * memchr - Find a character in an area of memory.
  367.  * @s: The memory area
  368.  * @c: The byte to search for
  369.  * @n: The size of the area.
  370.  *
  371.  * returns the address of the first occurrence of @c, or %NULL
  372.  * if @c is not found
  373.  */
  374. void *memchr(const void *s, int c, unsigned int n)
  375. {
  376.     const unsigned char *p = (const unsigned char *)s;
  377.     while (n-- != 0) {
  378.             if ((unsigned char)c == *p++) {
  379.             return (void *)(p-1);
  380.         }
  381.     }
  382.     return NULL;
  383. }

  384. /*功能:向字符串 格式化打印一个字符串
  385. *参数:格式化的字符串
  386. *注意:这个是简易版本 (%02x 没法完成)
  387. */
  388. int sprintf(char * str, const char *fmt, ...)
  389. {
  390.     int count = 0;
  391.     char c;
  392.     char *s;
  393.     int n;
  394.     char buf[65];
  395.     va_list ap;
  396.     
  397.     va_start(ap, fmt);
  398.     
  399.     while(*fmt != '\0')
  400.     {
  401.         if(*fmt == '%')
  402.         {
  403.             fmt++;
  404.             switch(*fmt)
  405.          {
  406.                 case 'd': /*整型*/
  407.                     n = va_arg(ap, int);
  408.                     if(n < 0)
  409.                     {
  410.                         *str = '-';
  411.                         str++;
  412.                         n = -n;
  413.                     }    
  414.                     itoa(n, buf);
  415.                     memcpy(str, buf, strlen(buf));
  416.                     str += strlen(buf);

  417.                     break;        
  418.                 case 'c': /*字符型*/
  419.                     c = va_arg(ap, int);
  420.                     *str = c;
  421.                     str++;
  422.                     
  423.                     break;
  424.                 case 'x': /*16进制*/
  425.                     n = va_arg(ap, int);
  426.                     xtoa(n, buf);
  427.                     memcpy(str, buf, strlen(buf));
  428.                     str += strlen(buf);

  429.                     break;
  430.                 case 's': /*字符串*/
  431.                     s = va_arg(ap, char *);
  432.                     memcpy(str, s, strlen(s));
  433.                     str += strlen(s);
  434.                     
  435.                     break;
  436.                 case '%': /*输出%*/
  437.                     *str = '%';
  438.                     str++;
  439.                     
  440.                     break;
  441.                 default:
  442.                     break;
  443.             }    
  444.         }
  445.         else
  446.         {
  447.             *str = *fmt;
  448.             str++;

  449.             if(*fmt == '\n')
  450.             {
  451.                 //uart_sendByte('\r');
  452.             }
  453.         }
  454.         fmt++;
  455.     }

  456.     va_end(ap);

  457.     return count;
  458. }


  459. /*
  460. *功能:整型(int) 转化成 字符型(char)
  461. *注意:不用 % / 符号的话,只能正确打印:0...9的数字对应的字符'0'...'9'
  462. */
  463. void itoa(unsigned int n, char * buf)
  464. {
  465.         int i;
  466.         
  467.         if(n < 10)
  468.         {
  469.                 buf[0] = n + '0';
  470.                 buf[1] = '\0';
  471.                 return;
  472.         }
  473.         itoa(n / 10, buf);

  474.         for(i=0; buf[i]!='\0'; i++);
  475.         
  476.         buf[i] = (n % 10) + '0';
  477.         
  478.         buf[i+1] = '\0';
  479. }

  480. /*
  481. *功能:字符型(char) 转化成 整型(int)
  482. */
  483. int atoi(char* pstr)
  484. {
  485.         int int_ret = 0;
  486.         int int_sign = 1; //正负号标示 1:正数 -1:负数
  487.         
  488.         if(pstr == '\0') //判断指针是否为空
  489.         {
  490.                 uart_printf("Pointer is NULL\n");
  491.                 return -1;
  492.         }
  493.         while(((*pstr) == ' ') || ((*pstr) == '\n') || ((*pstr) == '\t') || ((*pstr) == '\b'))
  494.         {
  495.                 pstr++; //跳过前面的空格字符
  496.         }
  497.         
  498.         /*
  499.         * 判断正负号
  500.         * 如果是正号,指针指向下一个字符
  501.         * 如果是符号,把符号标记为Integer_sign置-1,然后再把指针指向下一个字符
  502.         */
  503.         if(*pstr == '-')
  504.         {
  505.                 int_sign = -1;
  506.         }
  507.         if(*pstr == '-' || *pstr == '+')
  508.         {
  509.                 pstr++;
  510.         }
  511.         
  512.         while(*pstr >= '0' && *pstr <= '9') //把数字字符串逐个转换成整数,并把最后转换好的整数赋给Ret_Integer
  513.         {
  514.                 int_ret = int_ret * 10 + *pstr - '0';
  515.                 pstr++;
  516.         }
  517.         int_ret = int_sign * int_ret;
  518.         
  519.         return int_ret;
  520. }

  521. /*
  522. *功能:16进制字(0x) 转化成 字符型(char)
  523. *注意:不用 % / 符号的话,只能正确打印,0...9..15的数字,对应的'0'...'9''A'...'F'
  524. *注意:由于编译问题,这个函数,暂时由uart_sendByte_hex()函数替代
  525. */
  526. void xtoa(unsigned int n, char * buf)
  527. {
  528.         int i;
  529.         
  530.         if(n < 16)
  531.         {
  532.                 if(n < 10)
  533.                 {
  534.                         buf[0] = n + '0';
  535.                 }
  536.                 else
  537.                 {
  538.                         buf[0] = n - 10 + 'a';
  539.                 }
  540.                 buf[1] = '\0';
  541.                 return;
  542.         }
  543.         xtoa(n / 16, buf);
  544.         
  545.         for(i = 0; buf[i] != '\0'; i++);
  546.         
  547.         if((n % 16) < 10)
  548.         {
  549.                 buf[i] = (n % 16) + '0';
  550.         }
  551.         else
  552.         {
  553.                 buf[i] = (n % 16) - 10 + 'a';
  554.         }
  555.         buf[i + 1] = '\0';
  556. }

  557. /*
  558.  * 判断一个字符是否数字
  559.  */
  560. int isDigit(unsigned char c)
  561. {
  562.     if (c >= '0' && c <= '9')
  563.         return 1;
  564.     else
  565.         return 0;
  566. }

  567. /*
  568.  * 判断一个字符是否英文字母
  569.  */
  570. int isLetter(unsigned char c)
  571. {
  572.     if (c >= 'a' && c <= 'z')
  573.         return 1;
  574.     else if (c >= 'A' && c <= 'Z')
  575.         return 1;
  576.     else
  577.         return 0;
  578. }


  579. /*
  580. 研究数据结构的时候,还有很多字符串处理函数
  581. */
string.h文件

点击(此处)折叠或打开

  1. #ifndef _STRING_H_
  2. #define _STRING_H_

  3. typedef char * va_list;
  4. #define va_start(ap,p) (ap = (char *) (&(p)+1))
  5. #define va_arg(ap, type) ((type *) (ap += sizeof(type)))[-1]
  6. #define va_argp(ap, type) ((type *) (ap += sizeof(type)))-1
  7. #define va_end(ap)

  8. #define NULL 0

  9. char * strcpy(char * dest,const char *src);
  10. char * strncpy(char * dest,const char *src,unsigned int count);
  11. char * strcat(char * dest, const char * src);
  12. char * strncat(char *dest, const char *src, unsigned int count);
  13. int strcmp(const char * cs,const char * ct);
  14. int strncmp(const char * cs,const char * ct,unsigned int count);
  15. char * strchr(const char * s, int c);
  16. char * strrchr(const char * s, int c);
  17. unsigned int strlen(const char * s);
  18. unsigned int strnlen(const char * s, unsigned int count);
  19. unsigned int strspn(const char *s, const char *accept);
  20. char * strpbrk(const char * cs,const char * ct);
  21. char * strtok(char * s,const char * ct);
  22. char * strsep(char **s, const char *ct);
  23. void * memset(void * s,int c,unsigned int count);
  24. void bcopy(const void *src, void *dest, unsigned int count);
  25. void * memcpy(void * dest,const void *src,unsigned int count);
  26. void * memmove(void * dest,const void *src,unsigned int count);
  27. int memcmp(const void * cs,const void * ct,unsigned int count);
  28. void * memscan(void * addr, int c, unsigned int size);
  29. char * strstr(const char * s1,const char * s2);
  30. void *memchr(const void *s, int c, unsigned int n);
  31. int sprintf(char * str, const char *fmt, ...);
  32. void itoa(unsigned int n, char * buf);
  33. int atoi(char* pstr);
  34. void xtoa(unsigned int n, char * buf);
  35. int isDigit(unsigned char c);
  36. int isLetter(unsigned char c);

  37. #endif
uart.c文件

点击(此处)折叠或打开

  1. #include "string.h"
  2. #include "uart.h"


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

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

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

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

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

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

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

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

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

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

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


  182. /*******************************************************************************************
  183. 为了支持求余求模,需要:
  184. 1,修改makefile如下:增加libgcc的库
  185.     arm-linux-ld -Tuart.lds -o uart_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
  186. 上面一句展开后,其实就是下面的一句:
  187. ##    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
  188. 2,自己手写libgcc的库。
  189. 这个在uart3实现
  190. *******************************************************************************************/

  191. /*功能:向串口格式化打印一个字符串
  192. *参数:格式化的字符串
  193. *注意:由于求模求余的问题没有解决,所以这里%d输出时,只能输出0~9,10和10以上的不能输出
  194. */
  195. int uart_printf(const char *fmt, ...)
  196. {
  197.     int count = 0;
  198.     char c;
  199.     char *s;
  200.     int n;
  201.     char buf[65];
  202.     va_list ap;
  203.     
  204.     va_start(ap, fmt);
  205.     
  206.     while(*fmt != '\0') //所有的'\n'都会输出
  207.     {
  208.         if(*fmt == '%')
  209.         {
  210.             fmt++;
  211.             switch(*fmt)
  212.          {
  213.                 case 'd': /*整型*/
  214.                     n = va_arg(ap, int);
  215.                     if(n < 0)
  216.                     {
  217.                         //uputchar('-');
  218.                         uart_sendByte('-');
  219.                         n = -n;
  220.                     }    
  221.                     itoa(n, buf);
  222.                     //_uputs(buf);
  223.                     uart_sendString(buf);
  224.                     break;        
  225.                 case 'c': /*字符型*/
  226.                     c = va_arg(ap, int);
  227.                     uart_sendByte(c);
  228.                     break;
  229.                 case 'x': /*16进制*/
  230.                     n = va_arg(ap, int);
  231.                     //uart_sendByte_hex(n); /*由于求模求余编译有问题,所以用uart_sendByte_hex来替代下面的2行代码*/
  232.                     xtoa(n, buf);
  233.                     uart_sendString(buf);
  234.                     break;
  235.                 case 's': /*字符串*/
  236.                     s = va_arg(ap, char *);
  237.                     uart_sendString(s);
  238.                     break;
  239.                 case '%': /*输出%*/
  240.                     uart_sendByte('%');
  241.                     break;
  242.                 default:
  243.                     break;
  244.             }    
  245.         }
  246.         else
  247.         {
  248.             uart_sendByte(*fmt); //最后一个 '\n'也会输出
  249.             if(*fmt == '\n')
  250.             {
  251.                 //uart_sendByte('\r');
  252.             }
  253.         }
  254.         fmt++;
  255.     }

  256.     va_end(ap);

  257.     return count;
  258. }
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. void init_uart(void);
  20. void uart_sendByte(int data);
  21. void uart_sendString(char *p);
  22. void uart_sendByte_hex(unsigned long val);
  23. char uart_getch(void);
  24. void uart_getString(char *string);
  25. int uart_printf(const char *fmt, ...);

  26. /**************************************************************************/

  27. #endif


















阅读(1128) | 评论(0) | 转发(0) |
0

上一篇:sdram裸机驱动程序

下一篇:SPI裸机驱动

给主人留下些什么吧!~~