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

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-02 14:11:39

使用SPI控制器,查询方式 来操作oled flash adc
adc.c文件

点击(此处)折叠或打开

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

  4. /*
  5. *功能:AD转换初始化
  6. */
  7. void init_ADC()
  8. {
  9.         uart_sendString("init_ADC!\n");
  10.      //当PCLK为50M,ADC转换频率为2M时,算得预分频系数是24
  11.         unsigned int prscen = PCLK/ADC_FREQ -1;
  12.         
  13.         /*ADCCON寄存器
  14.         *[15]:只读,AD转换结束标志 0:正在转换 1:转换结束
  15.         *[14]=1 1:AD转换器的时钟使用预分频 0:AD转换器的时钟不使用预分频
  16.         *[13:6]=19,预分频系数,因为初始这8是全1
  17.         *[5:3]=0b010,AD转换的通道
  18.         *[2] 0: 正常模式 1: 静态模式 初始是1
  19.         *[1] 0:读转换数据时不启动下一次转换 1:读转换数据时启动下一次转换
  20.         *[0] 当READ_START位=1时,这个位无效。 0:无作用 1:启动AD转换(当转换真正开始时,此位被请0)
  21.         */
  22.         ADCCON &= (~(0xFF << 6)); //这里8位必须清0,
  23.         ADCCON &= (~(1 << 2));
  24.         //上面2句也可以合并成如下一句:
  25.         //ADCCON = 0x0; //全部清0,因为已经在前面保存了这个寄存器原来的值,所以这里可以全清0。
  26.         
  27.         ADCCON |= ((1 << 14) | ((prscen & 0xff) << 6));
  28.         ADCCON &= (~(1 << 1));
  29.         ADC_DISABLE(); //初始化时,并不启动AD转换
  30.         
  31.         //这里是普通的AD转换,ADCTSC寄存器的值,取默认值就行了。
  32.         ADCTSC &= (~(1<<2));
  33. }

  34. /*
  35. *功能:选择进行AD转换的通道
  36. */
  37. void get_ch(unsigned int chanal)
  38. {
  39.         int i;
  40.         unsigned int sel_mux;
  41.         sel_mux = (ADCCON>>3)&0x7; //新通道之前的通道
  42.         
  43.         uart_sendString("get_ch!\n");
  44.         if(sel_mux != chanal) //之前的通道,与现在选择的通道不同,则需要变换通道
  45.         {
  46.                 ADCCON &= (~(7<<3)); //先把已有的数据清0
  47.                 ADCCON |= (chanal<<3); //选择新的通道值。
  48.                 //注意:选择新的通道值之后,需要延时一段时间,等待AD转换器反应过来
  49.                 for(i=0; i<10000; i++);
  50.                 for(i=0; i<10000; i++);
  51.         }
  52. }


  53. /*
  54. *功能:读取AD转换后的值
  55. */
  56. unsigned int read_ADC()
  57. {
  58.         unsigned int data;
  59.         
  60.         //启动AD转换
  61.         ADC_START();
  62.         
  63.         uart_sendString("read_ADC!\n");
  64.         
  65.         while(ADCCON & 0x1); //转换真正开始时,此位会被清0,清0之后继续向下走
  66.         
  67.         while((ADCCON & 0x8000) == 0); //0:正在转换 1:转换结束,变成1之后继续向下走
  68.         
  69.         data = (ADCDAT0 & 0x3ff); //ADCDAT0[9:0]10位,是普通AD转换后的数据值
  70.         
  71.         return data;
  72. }



  73. /*
  74. *函数功能 : 把0x0到0x3FF的数值,转化为对应的电压值
  75. *注意 : 电源电压是 3.3V
  76. * AD的精度是10位,即1024个等级
  77. * 计算出来的电压值的小数位的精度:VOL_POINT=3,即保留3位有效小数位
  78. */
  79. void adc_Voltage(unsigned int data, char * Voltage)
  80. {
  81.         float vol; //用小数表示的电压值
  82.     unsigned int point; //电压值的小数部分
  83.     int i = 0;
  84.     unsigned int vol_prec = 10; //如果 VOL_POINT = 3,则这个值是1000;
  85.     
  86.     for(i=0; i<VOL_POINT-1; i++) //有求幂函数的话,也可以使用。
  87.     {
  88.             vol_prec = vol_prec * 10;
  89.     }

  90.     vol = ((float)data * VCC)/ADC_PREC; //计算电压值
  91.     point = (vol - (unsigned int)vol) * vol_prec; //计算小数部分, *1000表示小树部分保留3位 本代码中的 sprintf uart_printf 无法打印浮点数
  92.     //uart_printf("AIN_2 = %d.%dV\r", (unsigned int)vol, point);

  93.     sprintf(Voltage,"%d.%dV", (unsigned int)vol, point);
  94. }
adc.h文件

点击(此处)折叠或打开

  1. #ifndef __ADC_H__
  2. #define __ADC_H__

  3. /*模数转换相关寄存器*/
  4. #define    ADCCON        (*(volatile unsigned long *)0x58000000)
  5. #define    ADCTSC        (*(volatile unsigned long *)0x58000004)
  6. #define    ADCDAT0        (*(volatile unsigned long *)0x5800000C)

  7. #define ADC_DISABLE() ((ADCCON) & (~(1<<0)))
  8. #define ADC_START() ((ADCCON) |= (0x1))

  9. //ADC转换频率最大为2M PCLK=50M
  10. #define ADC_FREQ (2000000)
  11. #define ADC_CHANAL_AIN2 2

  12. void init_ADC();
  13. void get_ch(unsigned int chanal);
  14. unsigned int read_ADC();
  15. void adc_Voltage(unsigned int data, char * Voltage);

  16. #define ADC_WEI 10 //AD的精度是10位
  17. #define ADC_PREC ((float)((1<<ADC_WEI)*(1.0))) //AD的精度是2^10=1024
  18. #define VCC ((float)(3.3))             //电源电压值
  19. #define VOL_POINT (3)             //计算的电压值小数位的精度

  20. #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
flash.c文件

点击(此处)折叠或打开

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

  3. #include "spi.h"
  4. #include "oled.h"
  5. #include "flash.h"

  6. /*
  7. 具有spi接口的flash芯片
  8. 芯片手册 : W25Q16DV-具有SPI接口的flash芯片
  9. FLASH W25X16
  10. 1, 工作电压:2.7 ~3.6 v ,工作电流0。5mA ,
  11. 2, 空间大小16Mbit, 2M Byte
  12. 3, 8192 个可编程页, 每页=256字节;
  13. 4, 用扇区擦除指令,每次可以擦除16页; 每个扇区=16页=4K字节; 整个flash共512个扇区
  14.    用块擦除指令每次可以擦除256页; 每个块=256页=64K字节; 整个flash共32个块
  15.    用整片擦除指令可以擦除整个芯片; 整个flash2M 字节

  16. 5, 用页编程指令,每次可编程256个字节;即使用页写命令,每次可写1页=256字节
  17. */

  18. /*
  19. *函数功能 : 选中flash的地址
  20. *注意 : 我们使用的W25Q16DV芯片,flash内存是16Mbit=2M字节,共需要21根地址线来寻址
  21. * 这里使用 3个字节的地址来表示flash芯片的地址,先发送3个字节地址的最高位,最后发送最低位
  22. */
  23. void flash_select_addr(unsigned int addr)
  24. {
  25.         spi_send_byte(addr >> 16); //这个函数会截取参数的低8位
  26.         spi_send_byte(addr >> 8);
  27.         spi_send_byte(addr & 0xff);
  28. }

  29. /*
  30. *函数功能 : 设置 flash片选引脚 0:选中 1:不选中
  31. */
  32. void flash_set_cs(unsigned char val)
  33. {
  34.         if(val == 1) //让 OLED的命令|数据引脚 输出高电平
  35.         {
  36.                 GPGDAT |= (1<<10); //GPG10输出高电平
  37.         }
  38.         else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
  39.         {
  40.                 GPGDAT &= (~(1<<10)); //GPG10输出低电平
  41.         }
  42. }

  43. /*
  44. *函数功能 : 读取flash芯片的厂家ID,设备ID
  45. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 52页
  46. */
  47. void flash_read_id(unsigned char * mid, unsigned char * did)
  48. {
  49.         unsigned char m_id;
  50.         unsigned char d_id;
  51.     
  52.         flash_set_cs(0); //选中spi flash
  53.         
  54.         spi_send_byte(0x90); //发出90命令
  55.         
  56.         flash_select_addr(0); //选中flash的0地址
  57.     
  58.         m_id = spi_recv_byte(); //第1次读取厂家ID
  59.         d_id = spi_recv_byte(); //第2次读取设备ID
  60.         
  61.         *mid = m_id;
  62.         *did = d_id;

  63.         uart_printf("MID=[0x%x]\n", m_id); //0xEF
  64.         uart_printf("DID=[0x%x]\n", d_id); //0x14

  65.         flash_set_cs(1);
  66. }

  67. /*
  68. *函数功能 : flash芯片写入使能
  69. *函数参数 : enable 1 : 写使能 0 : 写禁止
  70. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 23 24页
  71. */
  72. void flash_write_enable(unsigned int enable)
  73. {
  74.         if(enable == 1) //写使能
  75.         {
  76.                 flash_set_cs(0); //选中spi flash
  77.                 spi_send_byte(0x06); //发出06命令
  78.                 flash_set_cs(1);
  79.         }
  80.         else if(enable == 0) //写禁止
  81.         {
  82.                 flash_set_cs(0); //选中spi flash
  83.                 spi_send_byte(0x04); //发出04命令
  84.                 flash_set_cs(1);
  85.         }
  86. }

  87. /*
  88. *函数功能 : 读取flash芯片的状态寄存器1
  89. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25页
  90. */
  91. unsigned char flash_read_status_reg1(void)
  92. {
  93.         unsigned char val = 0;
  94.     
  95.         flash_set_cs(0); //选中spi flash
  96.         spi_send_byte(0x05); //发出05命令
  97.         
  98.         val = spi_recv_byte();
  99.         
  100.         flash_set_cs(1);
  101.         
  102.         return val;
  103. }

  104. /*
  105. *函数功能 : 读取flash芯片的状态寄存器2
  106. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25页
  107. */
  108. unsigned char flash_read_status_reg2(void)
  109. {
  110.         unsigned char val = 0;
  111.     
  112.         flash_set_cs(0); //选中spi flash
  113.         spi_send_byte(0x35); //发出35命令

  114.         val = spi_recv_byte();

  115.         flash_set_cs(1);
  116.         
  117.         return val;        
  118. }

  119. /*
  120. *函数功能 : flash初始化函数
  121. */
  122. void flash_init(void)
  123. {
  124.         unsigned char reg1_val;

  125.         reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值

  126.         flash_clean_protect_for_status_reg(); //去除状态寄存器的保护
  127.         flash_clean_protect_for_memory(); //去除整个存储区间的写保护
  128. }

  129. /*
  130. *函数功能 : 去除状态寄存器的写保护,去保护之后,才可以写状态寄存器
  131. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 15 16页
  132. *工厂出厂默认状态是 : 写使能之后,SRP1=0,SRP0=0, 就可以写状态寄存器了。我们这里还是重新设置一下
  133. */
  134. void flash_clean_protect_for_status_reg(void)
  135. {
  136.         unsigned char reg1_val = 0;;
  137.         unsigned char reg2_val = 0;

  138.         reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
  139.         reg2_val = flash_read_status_reg2(); //保存状态寄存器2中的值
  140.         
  141.         //状态寄存器1 的bit7:SRP0 要清0
  142.         reg1_val &= (~(1<<7));
  143.     
  144.         //状态寄存器2 的bit0:SRP1 要清0
  145.         reg2_val &= (~(1<<0));

  146.         flash_write_status_reg(reg1_val, reg2_val);
  147. }

  148. /*
  149. *函数功能 : 去除整个存储区间的写保护,去保护之后,才可以写flash存储空间
  150. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 16+17页
  151. *CMP=0, BP2=0, BP1=0, BP0=0
  152. */
  153. void flash_clean_protect_for_memory(void)
  154. {
  155.         unsigned char reg1_val = 0;
  156.         unsigned char reg2_val = 0;
  157.         
  158.         reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
  159.         reg2_val = flash_read_status_reg2(); //保存状态寄存器2中的值
  160.         
  161.         //状态寄存器1 的bit4:BP2 bit3:BP1, bit2:BP0 要清0
  162.         reg1_val &= (~(1<<4));
  163.         reg1_val &= (~(1<<3));
  164.         reg1_val &= (~(1<<2));

  165.         //状态寄存器2 的bit6:CMP 要清0
  166.         reg2_val &= (~(1<<6));

  167.         flash_write_status_reg(reg1_val, reg2_val);
  168. }

  169. /*
  170. *函数功能 : 写flash芯片的状态寄存器
  171. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25+26页
  172. *注意 : 写状态寄存器之前,也要 先让flash芯片写使能 + 去除状态寄存器的保护 15页
  173. * 工厂出厂默认状态是 : 写使能之后,SRP1=0,SRP0=0, 就可以写状态寄存器了。我们这里还是重新设置一下
  174. */
  175. void flash_write_status_reg(unsigned char reg1_val, unsigned char reg2_val)
  176. {
  177.         flash_write_enable(1); //flash芯片写使能
  178.     
  179.         flash_set_cs(0); //选中spi flash
  180.         spi_send_byte(0x01); //发出01命令
  181.         
  182.         spi_send_byte(reg1_val); //写寄存器1
  183.         spi_send_byte(reg2_val); //写寄存器2

  184.         flash_set_cs(1);

  185.         flash_wait_for_busy(); //等待写数据完成
  186. }

  187. /*
  188. *函数功能 : flash擦除+写入时,需要一定处理时间,这里判断是否操作完成
  189. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 16页
  190. *注意 : 判断状态寄存器1的bit0:busy(0:内部操作完成,空闲 1:内部操作正在进行,)
  191. */
  192. void flash_wait_for_busy(void)
  193. {
  194.         unsigned char reg1_val;
  195.         
  196.         while(1)
  197.         {
  198.                 reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值,注意:这一句一定要放在循环里面,刚开始没有放在循环里面,排错排了很久
  199.                 
  200.                 //uart_printf("****** reg1_val=[0x%x]*******\n", reg1_val);
  201.                 
  202.                 if((reg1_val & 0x1) == 0) //等待bit0=0:内部操作完成
  203.                 {
  204.                         break;
  205.                 }
  206.         }
  207.         //while((reg1_val & 0x1) == 1); //等待bit0=0,是1则一直循环
  208. }

  209. /*
  210. *函数功能 : flash擦除1个扇区的内存空间
  211. *函数参数 :
  212. * addr : 将擦除的地址
  213. * 1, 这个地址值,只有前21位有效
  214. * 2, 因为是扇区擦除指令,所以一次最多擦除4K, 如果地址不是4K对齐,比如4094,则只擦除本扇区剩余的2个字节。绝对不会擦除接下来的扇区的空间
  215. * 3, 地址最好4K对齐
  216. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 43页
  217. *注意 : 我们使用的W25Q16DV芯片,flash内存是16Mbit=2M字节,共需要21根地址线来寻址
  218. * 用扇区擦除指令,每次可以擦除16页; 每个扇区=16页=4K字节; 整个flash共512个扇区
  219. */
  220. void flash_erase_memory(unsigned int addr)
  221. {
  222.         flash_write_enable(1); //flash芯片写使能
  223.     
  224.         flash_set_cs(0); //选中spi flash
  225.         spi_send_byte(0x20); //发出20命令:扇区擦除指令
  226.         
  227.         flash_select_addr(addr); //发出地址
  228.         
  229.         flash_set_cs(1);
  230.         
  231.         flash_wait_for_busy(); //等待擦除完成
  232. }

  233. /*
  234. *函数功能 : 向flash存储空间写数据
  235. *函数参数 :
  236. * addr : 将写入的地址
  237. * 1, 这个地址值,只有前21位有效
  238. * 2, 因为是页编程指令,所以一次最多可写1页, 如果地址不是页对齐256字节,比如254,则只写本页剩余的2个字节。绝对不会连续写到下面的页
  239. * 3, 写入的地址最好256字节对齐
  240. * buf : 将写入的数据
  241. * len : 将写入的数据的长度
  242. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 41页
  243. */
  244. void flash_write_memory(unsigned int addr, char * buf, int len)
  245. {
  246.         int i = 0;
  247.     
  248.         flash_write_enable(1); //flash芯片写使能
  249.     
  250.         flash_set_cs(0); //选中spi flash
  251.         spi_send_byte(0x02); //发出02命令, 页编程指令,每次可编程256个字节;即使用页写命令,每次可写1页
  252.         
  253.         flash_select_addr(addr); //发出地址
  254.         
  255.         for(i=0; i<len; i++)
  256.         {
  257.                 spi_send_byte(buf[i]); //一次写入一个字节的数据
  258.         }

  259.         flash_set_cs(1);
  260.         
  261.         flash_wait_for_busy(); //等待写数据完成
  262. }

  263. /*
  264. *函数功能 : 读取flash存储空间的数据
  265. *函数参数 :
  266. * addr : 将写入的地址
  267. * buf : 将写入的数据
  268. * len : 将写入的数据的长度
  269. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 27页
  270. */
  271. void flash_read_memory(unsigned int addr, char * buf, int len)
  272. {
  273.         int i = 0;
  274.     
  275.         flash_set_cs(0); //选中spi flash
  276.         spi_send_byte(0x03); //发出03命令

  277.         flash_select_addr(addr); //发出地址
  278.         
  279.         for(i=0; i<len; i++)
  280.         {
  281.                 buf[i] = spi_recv_byte(); //一次读取一个字节的数据
  282.         }

  283.         flash_set_cs(1);
  284. }
flash.h文件

点击(此处)折叠或打开

  1. #ifndef _FLASH_H_
  2. #define _FLASH_H_

  3. void flash_read_id(unsigned char * mid, unsigned char * did);
  4. void flash_select_addr(unsigned int addr);
  5. void flash_set_cs(unsigned char val);

  6. void flash_write_enable(unsigned int enable);
  7. unsigned char flash_read_status_reg1(void);
  8. unsigned char flash_read_status_reg2(void);
  9. void flash_clean_protect_for_status_reg(void);
  10. void flash_clean_protect_for_memory(void);
  11. void flash_write_status_reg(unsigned char reg1_val, unsigned char reg2_val);
  12. void flash_init(void);

  13. void flash_wait_for_busy(void);
  14. void flash_erase_memory(unsigned int addr);
  15. void flash_write_memory(unsigned int addr, char * buf, int len);
  16. void flash_read_memory(unsigned int addr, char * buf, int len);

  17. #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 ???,只能用ldr pc,也可以在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
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 "string.h"
  5. #include "uart.h"
  6. #include "setup.h"
  7. #include "interrupt.h"

  8. #include "spi.h"
  9. #include "oled.h"
  10. #include "flash.h"
  11. #include "adc.h"

  12. /*
  13. 本程序使用使用SPI控制器来控制spi的引脚,控制OLED
  14. */

  15. int adc_test()
  16. {
  17.         int i = 0;
  18.         unsigned int adc_data;
  19.         
  20.         //这个目的是:保留之前的配置,本程序完之后,会恢复这个值。
  21.         //之所以保存它,是因为其他程序有可能使用了这个值,虽然本程序没有使用。
  22.         unsigned int ADCCON_save = ADCCON;
  23.         unsigned char str[32];
  24.         unsigned char Voltage[32];
  25.         unsigned char buf[32];

  26.     spi_cont_init(); //spi控制器的初始化
  27.     
  28.     uart_printf("\n spi_cont_init ok\n");
  29.         oled_init(); //oled初始化
  30.         uart_printf("\n oled_init ok\n");
  31.         
  32.         oled_print(0, 0, "oled_init ok");
  33.     
  34.         //本实验仅完成模数转换,不涉及触摸屏,所以没有中断
  35.         uart_printf("\n ADC test:\n");
  36.         uart_printf("ADC frequency = 2MHZ\n");
  37.         uart_printf("ADC chanal = AIN 2\n");
  38.         uart_printf("\n begin:\n");

  39.         //AD转换初始化
  40.         init_ADC();

  41.         /*1,本程序流程:每按一次键,就读取一下AD转换的值,按ESC键结束本次测试。
  42.         * 2,不转动电位器时,每次读取的值可能不一样,但是差距会很小,这是正常的,每次转化会有误差,电位器电压有波动。
  43.         * 3,转动电位器,可以看见不同的点位值。
  44.         * 4,有硬件原理图可以看出:电位器电压范围是0~3.3V,这里可以转化成10位。1024个电压等级,每个等级电压差值=3.3V/1024
  45.         * 即0V时,数字量是0 3.3V时,数字量是1023。所以可以根据数字量计算出电压值。
  46.         */
  47.         while(uart_getch() != 0x1b) //0x1b 是ESC键,没按键一次,读取一次AD转换后面的值
  48.         {
  49.                 uart_printf("\n*****************************************\n");
  50.                 //选择AD通道:本开发板硬件连线,只连接了AIN 2
  51.                 get_ch(ADC_CHANAL_AIN2);

  52.                 //读取AD转换后的值。
  53.                 adc_data = read_ADC();
  54.                 
  55.                 for(i=0; i<10000; i++);
  56.                 for(i=0; i<10000; i++);
  57.                 for(i=0; i<10000; i++);
  58.                 for(i=0; i<10000; i++);
  59.                 
  60.                 memset(str, 0, sizeof(str));
  61.                 sprintf(str, "ADC_data = [0x%x]", adc_data);
  62.                 uart_printf(str);
  63.                 uart_printf("\n");

  64.                 oled_clear();
  65.                 oled_print(0, 0, str); //写到oled屏幕上
  66.                 
  67.                 memset(Voltage, 0, sizeof(Voltage));
  68.                 adc_Voltage(adc_data, Voltage);

  69.                 memset(buf, 0, sizeof(buf));
  70.                 sprintf(buf, "AIN_2 Voltage = [%s]", Voltage);
  71.                 uart_printf(buf);
  72.                 
  73.                 oled_print(4, 0, buf); //写到oled屏幕上
  74.                 
  75.                 uart_printf("\n*****************************************\n");
  76.         }
  77.         ADCCON = ADCCON_save; //恢复保存的值
  78.         
  79.         uart_sendString("end!\n");

  80.         return 0;
  81. }

  82. int main()
  83. {
  84.         icache_enable();
  85.         /*注意 : 使用SPI控制器时,上面这一句加速的一定要屏蔽
  86.         否则会超过了最大使用频率
  87.         */
  88.         
  89.         init_irq();
  90.         init_uart();
  91.         
  92.         uart_printf("\n\n");
  93.         uart_printf("My spitest start:\n\n");

  94.         adc_test();
  95.         
  96.         return 0;
  97. }
makefile文件

点击(此处)折叠或打开

  1. objs := head.o init.o setup.o nand.o main.o uart.o spi.o oled.o flash.o adc.o interrupt.o string.o

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

点击(此处)折叠或打开

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

  5. #include "def.h"
  6. #include "init.h"
  7. #include "nand.h"
  8. #include "uart.h"
  9. #include "string.h"
  10. #include "setup.h"
  11. #include "interrupt.h"

  12. #include "spi.h"
  13. #include "oled.h"
  14. #include "oledfont.h"

  15. /*
  16. *有机发光二极管又称为有机电激光显示(Organic Light-Emitting Diode,OLED)
  17. *芯片手册 : SPEC UG-2864TMBEG01 --OLED数据手册
  18. *这款OLED芯片可以使用很多种接口来通讯,其中就包括SPI
  19. */

  20. /*
  21. *函数功能 : 设置 OLED的命令|数据引脚 0 : 命令引脚 1 : 数据引脚
  22. */
  23. void oled_set_dc(unsigned char val)
  24. {
  25.         if(val == 1) //让 OLED的命令|数据引脚 输出高电平
  26.         {
  27.                 GPFDAT |= (1<<3); //GPF3输出高电平
  28.         }
  29.         else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
  30.         {
  31.                 GPFDAT &= (~(1<<3)); //GPF3输出低电平
  32.         }
  33. }

  34. /*
  35. *函数功能 : 设置 OLED片选引脚 0:选中 1:不选中
  36. */
  37. void oled_set_cs(unsigned char val)
  38. {
  39.         if(val == 1) //让 OLED的命令|数据引脚 输出高电平
  40.         {
  41.                 GPGDAT |= (1<<1); //GPG1输出高电平
  42.         }
  43.         else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
  44.         {
  45.                 GPGDAT &= (~(1<<1)); //GPG1输出低电平
  46.         }
  47. }

  48. /*
  49. *函数功能 : 通过SPI引脚,把命令cmd发送到OLED硬件上面去
  50. */
  51. void oled_write_cmd(unsigned char cmd)
  52. {
  53.         //uart_printf("oled_write_cmd \n");
  54.         oled_set_dc(0); //设置 OLED的命令|数据引脚,0 : 命令引脚
  55.         oled_set_cs(0); //设置 OLED片选引脚 0:选中

  56.         spi_send_byte(cmd); //通过SPI引脚发送数据

  57.         oled_set_cs(1); //设置 OLED片选引脚 1:不选中
  58.         oled_set_dc(1); //操作完成之后,恢复为 1 : 数据引脚
  59. }


  60. /*
  61. *函数功能 : 通过SPI引脚,把数据data发送到OLED硬件上面去
  62. */
  63. void oled_write_data(unsigned char data)
  64. {
  65.         oled_set_dc(1); //设置 OLED的命令|数据引脚,1 : 数据引脚
  66.         oled_set_cs(0); //设置 OLED片选引脚 0:选中
  67.         
  68.         spi_send_byte(data); //通过SPI引脚发送数据

  69.         oled_set_cs(1); //设置 OLED片选引脚 1:不选中
  70.         oled_set_dc(1); //操作完成之后,继续为 1 : 数据引脚
  71. }

  72. /*
  73. *函数功能 : oled全部点亮
  74. *注意 : 把8页全部写入1即可
  75. */
  76. void oled_all_light(void)
  77. {
  78.         int page, i;
  79.         for (page = 0; page < 8; page ++)
  80.         {
  81.                 oled_set_addr(page, 0);
  82.                 for (i = 0; i < 128; i++)
  83.                 {
  84.                         oled_write_data(1);
  85.                 }
  86.         }
  87. }

  88. /*
  89. *函数功能 : oled清屏
  90. *注意 : 把8页全部写入0即可
  91. */
  92. void oled_clear(void)
  93. {
  94.         int page;
  95.         int i;
  96.         for(page=0; page<8; page++)
  97.         {
  98.                 oled_set_addr(page, 0); //选中第page页,第0列
  99.                 for(i=0; i<128; i++) //128列
  100.                 {
  101.                         oled_write_data(0);
  102.                 }
  103.         }
  104. }

  105. /*
  106. *函数功能 : oled清1页
  107. *注意 : 把这页全部写入0即可
  108. */
  109. void oled_clear_page(int page)
  110. {
  111.         int i;

  112.         oled_set_addr(page, 0); //选中第page页,第0列
  113.         for(i=0; i<128; i++) //128列
  114.         {
  115.                 oled_write_data(0);
  116.         }
  117. }

  118. /*
  119. *函数功能 : 设置oled处于页地址模式
  120. *在数据手册的第30页
  121. */
  122. void oled_set_page_addr_mode(void)
  123. {
  124.         oled_write_cmd(0x20);
  125.         oled_write_cmd(0x02);
  126. }

  127. /*
  128. *函数功能 : OLED芯片的初始化
  129. *在数据手册的第29页,有初始化方法
  130. */
  131. void oled_init(void)
  132. {
  133.         /* 向OLED发命令以初始化 */
  134.     oled_write_cmd(0xAE); /*display off*/
  135.     oled_write_cmd(0x00); /*set lower column address*/
  136.     oled_write_cmd(0x10); /*set higher column address*/
  137.     oled_write_cmd(0x40); /*set display start line*/
  138.     oled_write_cmd(0xB0); /*set page address*/
  139.     oled_write_cmd(0x81); /*contract control*/
  140.     oled_write_cmd(0x66); /*128*/
  141.     oled_write_cmd(0xA1); /*set segment remap*/
  142.     oled_write_cmd(0xA6); /*normal / reverse*/
  143.     oled_write_cmd(0xA8); /*multiplex ratio*/
  144.     oled_write_cmd(0x3F); /*duty = 1/64*/
  145.     oled_write_cmd(0xC8); /*Com scan direction*/
  146.     oled_write_cmd(0xD3); /*set display offset*/
  147.     oled_write_cmd(0x00);
  148.     oled_write_cmd(0xD5); /*set osc division*/
  149.     oled_write_cmd(0x80);
  150.     oled_write_cmd(0xD9); /*set pre-charge period*/
  151.     oled_write_cmd(0x1f);
  152.     oled_write_cmd(0xDA); /*set COM pins*/
  153.     oled_write_cmd(0x12);
  154.     oled_write_cmd(0xdb); /*set vcomh*/
  155.     oled_write_cmd(0x30);
  156.     oled_write_cmd(0x8d); /*set charge pump enable*/
  157.     oled_write_cmd(0x14);

  158.     oled_set_page_addr_mode(); //设置oled处于页地址模式,默认也是处于页地址模式
  159.     oled_clear(); //oled清屏

  160.     oled_write_cmd(0xAF); /*display ON*/
  161. }

  162. /*
  163. *函数功能 : 找到oled显示屏,设置列地址+页地址。
  164. *函数参数 :
  165. * col : 0到127 列位置
  166. * page : 0到7 页位置
  167. *注意看数据手册 : 第30页+31页,在页模式下,设置页的起始地址
  168. */
  169. void oled_set_addr(unsigned int page, unsigned int col)
  170. {
  171.         //uart_printf("oled_set_addr \n");
  172.         oled_write_cmd(0xB0 + page);                //设置页地址

  173.         oled_write_cmd(col & 0xf);                    //先设置低4位列地址
  174.     oled_write_cmd(0x10 + (col >> 4));    //再设置高4位列地址
  175. }

  176. /*
  177. *函数功能 : 向oled显示屏,指定的列地址+页地址,显示一个字符。
  178. *函数参数 :
  179. * col : 0到127 val表示的字符,将要显示的列位置
  180. * page : 0到7 val表示的字符,将要显示的页位置
  181. *注意 : 一个字模跨2个page
  182. */
  183. void oled_fix_put_char(unsigned int page, unsigned int col, unsigned char val)
  184. {
  185.         int i;
  186.         /*得到字模*/
  187.         const unsigned char *zifu = oled_asc2_8x16[val - ' ']; //val - ' ' : 字符在字模数组中对应的下标

  188.         /*发给oled*/
  189.         //uart_printf("oled_fix_put_char page = [%d], col = [%d], val = [%c]\n\n", page, col, val);
  190.         oled_set_addr(page, col); //设置位置,字符所占的第1个page
  191.     /* 发出8字节数据 */
  192.     for (i = 0; i < 8; i++)
  193.         {
  194.         oled_write_data(zifu[i]); //连续写,列地址就自动加1
  195.         }
  196.     oled_set_addr(page+1, col); //1个字符占2页,字符所占的第2个page
  197.     /* 发出8字节数据 */
  198.     for (i = 0; i < 8; i++)
  199.        {
  200.         oled_write_data(zifu[i+8]);
  201.         }
  202. }

  203. /*
  204. *函数功能 : 向oled显示屏,打印一个字符串,从指定的行,列开始打印,会自动换行
  205. *函数参数 :
  206. * col : 0到127 起始存放的 第1个字符的列
  207. * page : 0到7 起始存放的 第1个字符所在的页
  208. *注意 :
  209. * 1, 我们使用的OLED的分辨率=128*64 0:表示像素熄灭, 1:表示像素点亮
  210. * 2, 显存大小 = 128*64位=1K字节
  211. * 3, OLED主控芯片数据手册,SSD1306(36页)10.1.3节,设置显存的地址模式,提供了3种地址模式,
  212. * 最常用的是 : 页地址模式。
  213. * 4, 页地址模式:
  214. * 64行分为8页,每页对应8行。可以选中指定的页,再选中指定的列,再向里面写数据,
  215. * 每写1列,列地址就自动加1,1页写完之后,继续写,会自动跳到下一页开头位置继续写。
  216. * 5, 约定一个数字|英文字符的大小是 8*16, 宽:8列,:16行。
  217. * 这样,这个OLED屏,可以显示8行,每行8个字符
  218. */
  219. void oled_print(unsigned int page, unsigned int col, char * str)
  220. {
  221.         char * tmp;
  222.         tmp = str;
  223.         
  224.         while((*tmp) != '\0')
  225.         {
  226.                 //uart_printf("oled_print *tmp = [%c]\n\n", *tmp);
  227.                 oled_fix_put_char(page, col, *tmp);
  228.                 col += 8; //每个字符占8列
  229.                 if(col > 127) //一行写满,自动换行
  230.                 {
  231.                         col = 0;
  232.                         page += 2; //每个字符占16行,跨2页
  233.                         if(page > 7) //8页都写满,再从最开始的位置,再写。
  234.                         {
  235.                                 page = 0;
  236.                         }
  237.                 }
  238.                 tmp++;        
  239.         }
  240. }
oled.h文件

点击(此处)折叠或打开

  1. #ifndef _OLED_H_
  2. #define _OLED_H_

  3. void oled_set_dc(unsigned char val);
  4. void oled_set_cs(unsigned char val);
  5. void oled_write_cmd(unsigned char cmd);
  6. void oled_write_data(unsigned char data);
  7. void oled_all_light(void);
  8. void oled_clear(void);
  9. void oled_clear_page(int page);
  10. void oled_set_page_addr_mode(void);
  11. void oled_init(void);
  12. void oled_set_addr(unsigned int page, unsigned int col);
  13. void oled_fix_put_char(unsigned int page, unsigned int col, unsigned char val);
  14. void oled_print(unsigned int page, unsigned int col, char * str);

  15. #endif
oledfont.h

点击(此处)折叠或打开

  1. #ifndef _OLEDFONT_H_
  2. #define _OLEDFONT_H_

  3. /*
  4. 注意 : 下面的对2维数组的定义,每个元素都被赋值了,即为对象已经分配内存了,则不能在多个文件中包含这个头文件。
  5. 如果仅仅是一些声明,是可以在多个文件中包含这个头文件的。

  6. 注意 : 对象必须有且只有1个定义,但是它可以有多extern声明。
  7. */

  8. /*
  9. *字模 : 8*16像素 =:8位 +:16位
  10. *注意 : 下面列出来的字符是按照ascii码从空格开始,按照顺序列出来,这么做是为了方便取码
  11. *下面理解成 : 1维数组,这个数组的每个元素又是1维数组
  12. *在oled屏幕中的位置:
  13. * 比如字符A : 在数组中的下标是33, 则oled_asc2_8x16[33]可以表示1个1维数组名,这个1维数组可以取出16个字符
  14. * {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33
  15. *
  16. * 上面页,8列,从最左1列开始,依次对应上面16个字符的前面8个字符 : 0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00
  17. *
  18. * 下面模拟OLED的1个字符的显示 像素=8*16
  19. * |0|0|0|0|0|0|0|0| 字符的最低位
  20. * |0|0|0|0|0|0|0|0|
  21. * |0|0|0|0|0|0|0|0|
  22. * |0|0|0|1|0|0|0|0|
  23. * |0|0|0|1|0|0|0|0|
  24. * |0|0|0|1|1|0|0|0|
  25. * |0|0|1|0|1|0|0|0|
  26. * _____|0|0|1|0|1|0|0|0|_____ 字符的最高位
  27. * |0|0|1|0|0|1|0|0| 字符的最低位
  28. * |0|0|1|1|1|1|0|0|
  29. * |0|1|0|0|0|1|0|0|
  30. * |0|1|0|0|0|0|1|0|
  31. * |0|1|0|0|0|0|1|0|
  32. * |1|1|1|0|0|1|1|1|
  33. * |0|0|0|0|0|0|0|0|
  34. *          |0|0|0|0|0|0|0|0| 字符的最高位
  35. * 下面页,8列,从最左1列开始,依次对应上面16个字符的后面8个字符 ; 0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20
  36. * 说明 : 所有标1的地方,看起来是不是很像大写的字符A ???
  37. */
  38. const unsigned char oled_asc2_8x16[95][16]=
  39. {
  40.     {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0 数组下标 = 对应字符的asc2码 - 空格的asc码
  41.     {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1
        {0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//"2
        {0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},//#3
        {0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},//$4
        {0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},//%5
        {0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},//&6
        {0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//'7
        {0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},//(8
        {0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},//)9
        {0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},//*10
        {0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},//+11
        {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},//,12
        {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},//-13
        {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},//.14
        {0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},///15
        {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},//016
        {0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//117
        {0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},//218
        {0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},//319
        {0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},//420
        {0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},//521
        {0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},//622
        {0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},//723
        {0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},//824
        {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},//925
        {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},//:26
        {0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},//;27
        {0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},//<28
        {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},//=29
        {0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},//>30
        {0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},//?31
        {0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},//@32
        {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33
        {0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},//B34
        {0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},//C35
        {0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},//D36
        {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},//E37
        {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},//F38
        {0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},//G39
        {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},//H40
        {0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//I41
        {0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},//J42
        {0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},//K43
        {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},//L44
        {0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},//M45
        {0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},//N46
        {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},//O47
        {0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},//P48
        {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},//Q49
        {0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},//R50
        {0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},//S51
        {0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//T52
        {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//U53
        {0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},//V54
        {0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},//W55
        {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},//X56
        {0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//Y57
        {0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},//Z58
        {0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},//[59
        {0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},//\60
        {0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},//]61
        {0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//^62
        {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},//_63
        {0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//`64
        {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},//a65
        {0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},//b66
        {0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},//c67
        {0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},//d68
        {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},//e69
        {0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//f70
        {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},//g71
        {0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//h72
        {0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//i73
        {0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},//j74
        {0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},//k75
        {0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//l76
        {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},//m77
        {0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//n78
        {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//o79
        {0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},//p80
        {0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},//q81
        {0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},//r82
        {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},//s83
        {0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},//t84
        {0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},//u85
        {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},//v86
        {0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},//w87
        {0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},//x88
        {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},//y89
        {0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},//z90
        {0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},//{91
        {0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},//|92
        {0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},//}93
        {0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//~94
    };


    #endif


setup.c

点击(此处)折叠或打开

  1. #include "setup.h"

  2. /*
  3. *功能:使能ICache
  4. */
  5. void icache_enable()
  6. {
  7.     unsigned int temp = 1<<12;

  8.     asm(
  9.             "mrc p15,0,r0,c1,c0,0\n" /*读出控制寄存器c1的值到r0中*/
  10.             "orr r0,r0,%0\n" /*第12位I位置1,即使能ICache*/
  11.             "mcr p15,0,r0,c1,c0,0\n" /*将修改后的值写入控制寄存器*/
  12.             :
  13.             :"r"(temp)
  14.             :"r0"
  15.         );
  16. }


  17. /*
  18. *功能:禁止ICache
  19. */
  20. void icache_disable()
  21. {
  22.     unsigned int temp = 1<<12;

  23.     asm( /*GCC内联汇编函数*/
  24.             "mrc p15,0,r0,c1,c0,0\n"
  25.             "bic r0,r0,%0\n" /*第12位I位置0,即禁止ICache*/
  26.             "mcr p15,0,r0,c1,c0,0\n"
  27.             :
  28.             :"r"(temp)
  29.             :"r0"
  30.         );
  31. }
setup.h文件

点击(此处)折叠或打开

  1. #ifndef _SETUP_H_
  2. #define _SETUP_H_

  3. void icache_enable();
  4. void icache_disable();

  5. #endif
sp.c文件

点击(此处)折叠或打开

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

  5. #include "def.h"
  6. #include "init.h"
  7. #include "nand.h"
  8. #include "string.h"
  9. #include "uart.h"
  10. #include "setup.h"
  11. #include "interrupt.h"

  12. #include "spi.h"
  13. #include "oled.h"
  14. #include "flash.h"

  15. /*是使用SPI控制器来控制spi的引脚
  16. *本程序使用查询模式,没有使用中断模式
  17. */

  18. /*
  19.  *函数功能 : 通过spi mosi引脚发送 一个字节的数据
  20.  *注意 : 采用的是格式A(s3c2440数据手册的361页)来发送数据
  21.  */
  22. void spi_send_byte(unsigned char val)
  23. {
  24.         icache_disable(); //降低频率
  25.         while (!(SPSTA0 & 1));
  26.     SPTDAT0 = val;
  27.     icache_enable(); //恢复频率到 PCLK=50MHZ
  28. }

  29. /*
  30.  *函数功能 : 通过spi miso引脚读取 一个字节的数据
  31.  *函数返回值 : 读取到的值
  32.  *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 27页
  33.  * 上面写的DI:是对flash的输入,对2440来说是DO:数据输出
  34.  * 上面写的DO:是对flash的输出,对2440来说是DI:数据输入
  35.  *注意 : 发送完命令+地址之后,spi_miso引脚上,已经出现了可以读取的数据,已经开始等待2440来读取了。
  36.  */
  37. unsigned char spi_recv_byte(void)
  38. {
  39.         icache_disable(); //降低频率
  40.         SPTDAT0 = 0xff; /*数据手册s3c2440的360页 : 实际上是产生8个脉冲。产生脉冲的同时,spi控制器会把数据读取出来。*/
  41.     while (!(SPSTA0 & 1));
  42.     icache_enable(); //恢复频率到 PCLK=50MHZ
  43.     return SPRDAT0;
  44. }


  45. /*
  46. *函数功能 : SPI控制器的初始化
  47. *注意 : s3c2440有2个SPI控制器,2个通道:通道0+通道1
  48. * tq2440,使用的是 GPE11,12,13口的SPI,是通道0。我们本程序使用的SPI模块是接这个
  49. * GPG5,6,7口的SPI+GPD8,9,10口的SPI,是通道1
  50. */
  51. void spi_cont_init(void)
  52. {
  53.         //1,OLED片选引脚 OLED_CSn :GPG1,设置为输出
  54.         GPGCON &= GPG1_CLEAN_0; //为了保持其他位不变,只清0相关的2个位
  55.         GPGCON |= GPG1_OUT; //为了保持其他位不变,只对相关的2个位赋值
  56.         GPGDAT |= (1<<1); //先不片选

  57.         //2,FLASH片选引脚 FLASH_CSn :GPG10,设置为输出
  58.         GPGCON &= GPG10_CLEAN_0;
  59.         GPGCON |= GPG10_OUT;
  60.         GPGDAT |= (1<<10); //先不片选

  61.         //3,OLED的命令|数据引脚 OLED_DC :GPF3,设置为输出
  62.         GPFCON &= GPF3_CLEAN_0;
  63.         GPFCON |= GPF3_OUT;

  64.         //下面才是SPI控制器相关的设置,上面是其他接口芯片的设置
  65.         //4,时钟引脚 : GPE13,设置为 10:SPICLK0 引脚
  66.         GPECON &= GPE13_CLEAN_0; //清0
  67.         GPECON |= (2<<(13*2));

  68.         //5,主机数据输出引脚 : GPE12,设置为 10:SPIMOSI 引脚
  69.         GPECON &= GPE12_CLEAN_0; //清0
  70.         GPECON |= (2<<(12*2));

  71.         //6,主机数据输入引脚 : GPE11,设置为 10:SPIMISO 引脚
  72.         GPECON &= GPE11_CLEAN_0; //清0
  73.         GPECON |= (2<<(11*2));

  74.         /*7, 波特率寄存器*/
  75.         /* OLED : 100ns, 10MHz //SPEC UG-2864TMBEG01 --OLED成品的数据手册 4线spi 17页
  76.     * FLASH : 104MHz 3.3V //W25Q16DV-具有SPI接口的flash芯片 66页
  77.     * 取10MHz
  78.     * 10 = 50M / 2 / (Prescaler value + 1) //2440最大提供25M频率
  79.     * Prescaler value = 1.5 取 2
  80.     * Baud rate = 50/2/3=8.3MHz //真实波特率
  81.     */
  82.     SPPRE0 = 2;
  83.     SPPRE1 = 2;

  84.         /*8, SPI控制寄存器*/
  85.     /*[6:5] : 00, polling mode 查询模式
  86.     * [4] : 1 = enable 使能时钟
  87.     * [3] : 1 = master 主机控制器
  88.     * [2] : CPOL位 0 = 高电平有效,在没有SPI的操作时,时钟线的电平
  89.     * [1] : CPHA位 0 = format A 格式A
  90.     * [0] : 0 = normal mode
  91.     */
  92.     SPCON0 = (1<<4) | (1<<3);
  93.     SPCON1 = (1<<4) | (1<<3);
  94. }

  95. /*
  96. 在s3c2440数据手册,英文版501页
  97. 1,CPOL : 时钟的电平的初始值
  98.        0 : SPICLK = 0
  99.        1 : SPICLK = 1
  100. 2, CPHA : 相位
  101.        0 : 在SPICLK第1个时钟沿,即第1个时钟跳变的时候,采样数据
  102.        1 : 在SPICLK第2个时钟沿,即第2个时钟跳变的时候,采样数据
  103. 注意 :
  104.     经常使用 mode0 : 00 mode3 : 11
  105.     因为他们都是在SPICLK的上升沿采样即可,不用管SPICLK的初始值,也不用再管是在SPICLK的第几个时钟沿。

  106. */
spi.h文件

点击(此处)折叠或打开

  1. #ifndef _SPI_H_
  2. #define _SPI_H_

  3. /* SPI */
  4. #define    SPCON0        (*(volatile unsigned long *)0x59000000)
  5. #define    SPSTA0        (*(volatile unsigned long *)0x59000004)
  6. #define    SPPIN0        (*(volatile unsigned long *)0x59000008)
  7. #define    SPPRE0        (*(volatile unsigned long *)0x5900000C)
  8. #define    SPTDAT0        (*(volatile unsigned char *)0x59000010)
  9. #define    SPRDAT0        (*(volatile unsigned char *)0x59000014)

  10. #define    SPCON1        (*(volatile unsigned long *)0x59000020)
  11. #define    SPSTA1        (*(volatile unsigned long *)0x59000024)
  12. #define    SPPIN1        (*(volatile unsigned long *)0x59000028)
  13. #define    SPPRE1        (*(volatile unsigned long *)0x5900002C)
  14. #define    SPTDAT1        (*(volatile unsigned char *)0x59000030)
  15. #define    SPRDAT1     (*(volatile unsigned char *)0x59000034)

  16. /* TQ2440开发板中,用来模拟spi引脚的gpio引脚 相关的寄存器 */
  17. #define GPECON        (*(volatile unsigned long *)0x56000040)
  18. #define GPEDAT        (*(volatile unsigned long *)0x56000044)
  19. #define GPEUP            (*(volatile unsigned long *)0x56000048)

  20. #define GPFCON (*(volatile unsigned long *)0x56000050)
  21. #define GPFDAT (*(volatile unsigned long *)0x56000054)
  22. #define GPFUP (*(volatile unsigned long *)0x56000058)

  23. #define GPGCON (*(volatile unsigned long *)0x56000060)
  24. #define GPGDAT (*(volatile unsigned long *)0x56000064)
  25. #define GPGUP (*(volatile unsigned long *)0x56000068)

  26. #define GPG1_CLEAN_0 (~(3<<(1*2))) //对应的2位清0
  27. #define GPG1_OUT (1<<(1*2)) //对应的2位设置为01,输出

  28. #define GPG10_CLEAN_0 (~(3<<(10*2)))
  29. #define GPG10_OUT (1<<(10*2))

  30. #define GPF3_CLEAN_0 (~(3<<(3*2)))
  31. #define GPF3_OUT (1<<(3*2))

  32. #define GPE11_CLEAN_0 (~(3<<(11*2))) //对应的2位清0,也即设置为输入
  33. #define GPE11_OUT (1<<(11*2))

  34. #define GPE12_CLEAN_0 (~(3<<(12*2)))
  35. #define GPE12_OUT (1<<(12*2))

  36. #define GPE13_CLEAN_0 (~(3<<(13*2)))
  37. #define GPE13_OUT (1<<(13*2))

  38. void spi_send_byte(unsigned char val);
  39. unsigned char spi_recv_byte(void);
  40. void spi_cont_init(void);

  41. #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. typedef char * va_list;
  388. #define va_start(ap,p) (ap = (char *) (&(p)+1))
  389. #define va_arg(ap, type) ((type *)(ap += sizeof(type)))[-1]
  390. #define va_end(ap)

  391. */
  392. int sprintf(char * str, const char *fmt, ...)
  393. {
  394.     int count = 0;
  395.     char c;
  396.     char *s;
  397.     int n;
  398.     char buf[65];
  399.     va_list ap;
  400.     
  401.     va_start(ap, fmt);
  402.     
  403.     while(*fmt != '\0')
  404.     {
  405.         if(*fmt == '%')
  406.         {
  407.             fmt++;
  408.             switch(*fmt)
  409.          {
  410.                 case 'd': /*整型*/
  411.                     n = va_arg(ap, int);
  412.                     if(n < 0)
  413.                     {
  414.                         *str = '-';
  415.                         str++;
  416.                         n = -n;
  417.                     }    
  418.                     itoa(n, buf);
  419.                     memcpy(str, buf, strlen(buf));
  420.                     str += strlen(buf);

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

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

  453.             if(*fmt == '\n')
  454.             {
  455.                 //uart_sendByte('\r');
  456.             }
  457.         }
  458.         fmt++;
  459.     }

  460.     va_end(ap);

  461.     return count;
  462. }


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

  478.         for(i=0; buf[i]!='\0'; i++);
  479.         
  480.         buf[i] = (n % 10) + '0';
  481.         
  482.         buf[i+1] = '\0';
  483. }

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

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

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

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


  583. /*
  584. 研究数据结构的时候,还有很多字符串处理函数
  585. */
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_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
  8. #define va_arg(ap,type) ( *(type *)((ap += 1) - 1) )
  9. #define va_end(ap)

  10. #define NULL 0

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

  39. #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

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

上一篇:IIC裸机驱动程序

下一篇:实现string.c

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