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

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: 嵌入式

2015-12-02 12:33:50

我自己写的最简易版本的,可以nand启动,也可以nor启动,当然可以启动内核,挂载nfs文件系统

最简单的bootloader的编写步骤:
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 如果bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置"要传给内核的参数"
5. 跳转执行内核


改进:
1. 提高CPU频率, 200MHZ ==> 400MHZ
2. 启动ICACHE


注意:本bootloader功能:
1,nand启动
2,引导内核
没有tftp功能,希望在bootloader中实现


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
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. void memsetup(void)
  34. {
  35.     volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

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

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

点击(此处)折叠或打开

  1. #ifndef _INIT_H_
  2. #define _INIT_H_

  3. /* WATCHDOG寄存器 */
  4. #define WTCON (*(volatile unsigned long *)0x53000000)
  5. /* SDRAM寄存器 */
  6. #define     MEM_CTL_BASE        0x48000000


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

  11. void disable_watch_dog();
  12. void clock_init(void);
  13. void memsetup();
  14. void clean_bss(void);

  15. #endif
makefile文件

点击(此处)折叠或打开

  1. objs := step1.o init.o nand.o step2.o setup.o uart.o

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

点击(此处)折叠或打开

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

点击(此处)折叠或打开

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

  4. /*
  5. *函数功能:判断是nand启动,还是nor启动
  6. *返回值:0:nand启动,1:nor启动
  7. */
  8. int isBootFromNorFlash(void)
  9. {
  10.     volatile int *p = (volatile int *)0;
  11.     int val;

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

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

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


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


  144. /*功能 : 发出地址
  145. *注意: 这个地址,不是页编号,是具体的读取NandFlash的起始地址,包括页内地址
  146. */
  147. void write_addr(unsigned int addr)
  148. {
  149.         //写地址(要读取数据的起始地址) :
  150.         //包括 :(页内)地址,是低位地址,共A0~A11,在具体的page页内寻址。一个page页=(2K+64)字节
  151.         //()地址,是高位地址,共A12~A28,可以寻址到page页。一个Nand器件=2K*64个page页 = 128K页
  152.         // 高位地址中,A12~A17,可以寻址到block块下面的page页。一个block块=64个page页
  153.         // 高位地址中,A18~A28,可以寻址到block块。一个Nand器件=2K个block块
  154.         //要写全部的5个周期的地址
  155.     
  156.         //32位的page_number : 低29位有效 :
  157.         //| 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 |
  158.      // 对应最后3个地址周期的页内地址的高11位 :
  159.         //|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|
  160.         //29位在5个地址周期中应是如下的分布 :
  161.         //| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
  162.         //| L | L | L | L | L | L | L | A28 |
  163.         //| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
  164.         //| A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 |
  165.         //| L | L | L | L | A11 | A10 | A9 | A8 |
  166.         //| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
  167.     
  168.     int i;
  169.     int col, page;

  170.         col = addr & (2048 -1); //截取低位:取2048的原因:本程序使用的nand芯片的一个page页是2048字节
  171.         page = addr / 2048; //截取高位
  172.     
  173.     NF_ADDR(col & 0xFF); //(页内)地址 A0~A7
  174.     for(i=0; i<100; i++);
  175.         NF_ADDR((col >> 8) & 0x0F);     //(页内)地址 A8~A11
  176.         for(i=0; i<100; i++);
  177.         
  178.         NF_ADDR(page & 0xFF); //()地址 A12~A19
  179.         for(i=0; i<100; i++);
  180.         NF_ADDR((page >> 8) & 0xFF); //()地址 A20~A27
  181.         for(i=0; i<100; i++);
  182.         NF_ADDR((page >> 16) & 0x03); //()地址 A28~A29
  183.         for(i=0; i<100; i++);
  184. }

  185. /*
  186. *功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
  187. * 数据方向:(Nand的start_addr->SDRAM的buf)
  188. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  189. * start_addr : 不是页编号,也就是说,不是每一个page页的开始地址处理,
  190. * 是具体的读取NandFlash的起始地址,包括页内地址:0x1003=4099
  191. * size : 复制的字节数,可以是页空间整数倍,也可以不是,也就是说,size可以小于2K,也可以大于2K。0x810,0x800=2048
  192. *注意 : read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
  193. */
  194. void nand_read(unsigned char *buf, unsigned long start_addr, int size)
  195. {
  196.         U32 i = 0, j = 0;
  197.         //片选
  198.         NF_CE_L();
  199.         
  200.         for(i=start_addr; i < (start_addr + size);)
  201.         {
  202.                 //清除RB
  203.                 NF_CLEAR_RB();
  204.                 //读NandFlash时的第1个命令
  205.               //0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
  206.                 //会自动满足时序图的要求,不再需要人工参与了。
  207.                 //这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
  208.                 NF_CMD(CMD_READ1);
  209.                 //写地址
  210.                 write_addr(i);
  211.                 //读NandFlash时的第2个命令
  212.                 NF_CMD(CMD_READ2);
  213.                 //等待NandFlash不忙
  214.                 NF_WAIT_RB();
  215.                 
  216.                 //读数据 : 一般1次读1页的数据(1页=2K + 64字节),可能跨了2页才能读取一页的数据。
  217.                 //也可以只读一个page页的一部分数据,只是多了几次下面的循环。
  218.                 for(j=0; j<2048; j++, i++) //小于2048也行
  219.                 {
  220.                     //1次从nandflash的8位IO口上读取8位数据。
  221.                     //注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
  222.                     //前一次读操作未完成,是不会启动下一次读操作的。
  223.                     *buf = NF_RDDATA8();
  224.                     buf++;
  225.                 }
  226.                 uart_sendString("#"); //2个nand read函数:目的是:main之后才初始化uart,可以使用这个带打印#的函数
  227.         }
  228.         //关闭片选
  229.         NF_CE_H();
  230.         
  231. }

  232. /*
  233. *功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
  234. * 数据方向:(Nand的start_addr->SDRAM的buf)
  235. *参数 : buf : SDRAM的缓冲区起始地址:0x30001000
  236. * start_addr : 不是页编号,也就是说,不是每一个page页的开始地址处理,
  237. * 是具体的读取NandFlash的起始地址,包括页内地址:0x1003=4099
  238. * size : 复制的字节数,可以是页空间整数倍,也可以不是,也就是说,size可以小于2K,也可以大于2K。0x810,0x800=2048
  239. *注意 : read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
  240. */
  241. void nand_read_1(unsigned char *buf, unsigned long start_addr, int size)
  242. {
  243.         U32 i = 0, j = 0;
  244.         //片选
  245.         NF_CE_L();
  246.         
  247.         for(i=start_addr; i < (start_addr + size);)
  248.         {
  249.                 //清除RB
  250.                 NF_CLEAR_RB();
  251.                 //读NandFlash时的第1个命令
  252.               //0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
  253.                 //会自动满足时序图的要求,不再需要人工参与了。
  254.                 //这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
  255.                 NF_CMD(CMD_READ1);
  256.                 //写地址
  257.                 write_addr(i);
  258.                 //读NandFlash时的第2个命令
  259.                 NF_CMD(CMD_READ2);
  260.                 //等待NandFlash不忙
  261.                 NF_WAIT_RB();
  262.                 
  263.                 //读数据 : 一般1次读1页的数据(1页=2K + 64字节),可能跨了2页才能读取一页的数据。
  264.                 //也可以只读一个page页的一部分数据,只是多了几次下面的循环。
  265.                 for(j=0; j<2048; j++, i++) //小于2048也行
  266.                 {
  267.                     //1次从nandflash的8位IO口上读取8位数据。
  268.                     //注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
  269.                     //前一次读操作未完成,是不会启动下一次读操作的。
  270.                     *buf = NF_RDDATA8();
  271.                     buf++;
  272.                 }
  273.         }
  274.         //关闭片选
  275.         NF_CE_H();
  276.         
  277. }


  278. /*
  279. *    buf : 目标地址 = 0x30000000,这是SDRAM的起始地址
  280. *    start_addr : 源地址 = 4096,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处
  281. * size : 复制长度 = 600K,对于本实验,这是足够了,没有图片的话,16K足够了,有图片了,得300K
  282. */
  283. int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size)
  284. {
  285.     int i = 0;
  286.     
  287.     /* 如果是NOR启动 */
  288.     if (isBootFromNorFlash())
  289.     {
  290.         while (i < size)
  291.         {
  292.             buf[i] = *((char *)start_addr);
  293.             i++;
  294.             start_addr++;
  295.         }
  296.     }
  297.     else
  298.     {
  299.         nand_read_1(buf, start_addr, size);
  300.     }
  301.     
  302.   return 0;
  303. }


  304. /*
  305. *功能 : 擦除指定的块
  306. *参数 : block_number : 块号
  307. *注意 : 1,一个NandFlash块有2K个块。
  308. * 2,块被擦除后nand对应的块中的数据全部是1
  309. * 3,命令字分为2个阶段
  310. U8 nand_erase(U32 block_number)
  311. {
  312.         int i;
  313.         U8 stat;
  314.         //片选
  315.         NF_CE_L();
  316.         //清除RB
  317.         NF_CLEAR_RB();
  318.         //写入第1个擦除命令
  319.         NF_CMD(CMD_ERASE1);
  320.         //写块的地址 : 注意 : 只写块的地址就行,块地址都在行()地址的高位上,
  321.         //所以不需要写前面2个周期的列(页内)地址,写后面3个周期的页地址的高11位就行。
  322.         //列地址 : 就是页内地址, 共12位
  323.         //行地址 : 就是页地址, 共17位 : 高11位(A28-A18)是2K的块地址,低6位是64的页地址。
  324.         
  325.         //32位block_number:低11位有效 :
  326.      // | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  327.      // 对应最后3个地址周期的页内地址的高11位 :
  328.         //| A28 | A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 | A19 | A18 |
  329.         //高11位在后面3个地址周期中应是如下的分布 :
  330.         //| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
  331.         //| | | | | | | | A28 |
  332.         //| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
  333.         //| A19 | A18 | | | | | | |
  334.         //
  335.         NF_ADDR((block_number << 6) & 0xFF);
  336.         NF_ADDR((block_number >> 2) & 0xFF);
  337.         NF_ADDR((block_number >> 10) & 0xFF);
  338.         //写入第2个擦除命令
  339.         NF_CMD(CMD_ERASE2);
  340.         
  341.         for(i=0; i<1000; i++);
  342.         //写入读状态寄存器的命令
  343.         NF_CMD(CMD_STATUS);
  344.         do
  345.         {
  346.          //读取8位状态数据
  347.                 stat = NF_RDDATA8();
  348.         }
  349.         while(!(stat & 0x40)); //等到第6位为1 : 表示读取数据完成。第0位为0,表示擦除成功
  350.         //关闭片选
  351.         NF_CE_H();
  352.         
  353.         //约定用0x66来表示擦除成功,或者写成功
  354.         return 0x66;
  355. }
  356. */


  357. /*功能 : 将buf处开始的数据,写入NandFlash的start_addr地址处,共写入size个字节。
  358. * 数据方向:(SDRAM的buf->Nand的start_addr)
  359. *参数 : buf : SDRAM的缓冲区起始地址
  360. * start_addr : 不是页编号,也就是说,不是每一个page页的开始地址处理,
  361. * 是具体的读取NandFlash的起始地址,包括页内地址:0x1003=4099
  362. * size : 复制的字节数,可以是页空间整数倍,也可以不是,也就是说,size可以小于2K,也可以大于2K。0x810,0x800=2048
  363. *注意 : write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
  364. U8 nand_write(unsigned char *buf, unsigned long start_addr, int size)
  365. {
  366.         int i,j;
  367.         U8 stat;
  368.         //片选
  369.         NF_CE_L();
  370.         
  371.         for(i=start_addr; i < (start_addr + size);)
  372.         {
  373.                 //清除RB
  374.                 NF_CLEAR_RB();
  375.                 //写NandFlash时的第1个命令 : NandFlash写准备
  376.                 NF_CMD(CMD_WRITE1);
  377.                 //写地址
  378.                 write_addr(i);
  379.                 //写数据 : 一般1次写1页的数据 : 1页=2K字节+64字节,可能跨了2页才能写一页的数据。
  380.                 //也可以只写一个page页的一部分数据,只是多了几次下面的循环。
  381.                 for(j=0; j<2048; j++, i++)
  382.                 {
  383.                  //1次写8位的数据给NandFlash的8位IO口
  384.                  //注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
  385.                  //前一次写数据操作不完成,是不会启动下一次写数据操作的
  386.                         NF_WRDATA8(*buf);
  387.                         buf++;
  388.                 }
  389.                 
  390.                 //写NandFlash时的第2个命令 : 启动写操作
  391.                 //此时,Flash内部会自动完成写,校验操作。
  392.                 NF_CMD(CMD_WRITE2);
  393.                 for(j=0; j<100; j++);
  394.                 //写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
  395.                 NF_CMD(CMD_STATUS);
  396.                 do
  397.                 {
  398.                  //读取8位状态数据
  399.                         stat = NF_RDDATA8();
  400.                 }
  401.                 while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。第0位为0,表示写数据成功
  402.         }
  403.         //关闭片选
  404.         NF_CE_H();
  405.         
  406.         return 0x66;
  407. }
  408. */
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. /*读NandFlash页时的命令1*/
  20. #define CMD_READ1 0x00
  21. /*读NandFlash页时的命令2*/
  22. #define CMD_READ2 0x30

  23. /*页编程 : 写NandFlash页时的命令1*/
  24. #define CMD_WRITE1 0x80
  25. /*页编程 : 写NandFlash页时的命令2*/
  26. #define CMD_WRITE2 0x10

  27. /*擦除NandFlash块的命令1*/
  28. #define CMD_ERASE1 0x60
  29. /*擦除NandFlash块的命令2*/
  30. #define CMD_ERASE2 0xD0

  31. /*读状态寄存器的命令*/
  32. #define CMD_STATUS 0x70
  33. /*读取芯片ID的命令*/
  34. #define CMD_READID 0x90
  35. /*重启命令*/
  36. #define CMD_RESET 0xFF

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

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

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

  50. /*从NANDFLASH的IO口中读数据(32位) : 数据寄存器NFDATA*/
  51. #define NF_RDDATA() (rNFDATA)
  52. /*从NANDFLASH的IO口中读数据(8位) : 数据寄存器NFDATA*/
  53. #define NF_RDDATA8() (rNFDATA8)
  54. /*向NANDFLASH的IO口中写数据(32位) : 数据寄存器NFDATA*/
  55. #define NF_WRDATA(data) {(rNFDATA) = (data);}
  56. /*向NANDFLASH的IO口中写数据(8位) : 数据寄存器NFDATA*/
  57. #define NF_WRDATA8(data) {(rNFDATA8) = (data);}

  58. /*时序信号参数的宽度*/
  59. #define TACLS 1
  60. #define TWRPH0 3
  61. #define TWRPH1 0

  62. int isBootFromNorFlash(void);
  63. void nand_reset();
  64. void nand_init();
  65. char nand_readId();
  66. void write_addr(unsigned int addr);
  67. void nand_read(unsigned char *buf, unsigned long start_addr, int size);
  68. void nand_read_1(unsigned char *buf, unsigned long start_addr, int size);
  69. int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size);
  70. //U8 nand_erase(U32 block_number);
  71. //U8 nand_write(unsigned char *buf, unsigned long start_addr, int size);

  72. #endif
setup.c文件

点击(此处)折叠或打开

  1. #include "setup.h"

  2. static struct tag *params;

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

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

  16. void setup_start_tag(void)
  17. {
  18.     params = (struct tag *)0x30000100;

  19.     params->hdr.tag = ATAG_CORE;
  20.     params->hdr.size = tag_size (tag_core);

  21.     params->u.core.flags = 0;
  22.     params->u.core.pagesize = 0;
  23.     params->u.core.rootdev = 0;

  24.     params = tag_next (params);
  25. }

  26. void setup_memory_tags(void)
  27. {
  28.     params->hdr.tag = ATAG_MEM;
  29.     params->hdr.size = tag_size (tag_mem32);
  30.     
  31.     params->u.mem.start = 0x30000000;
  32.     params->u.mem.size = 64*1024*1024;
  33.     
  34.     params = tag_next (params);
  35. }

  36. void setup_commandline_tag(char *cmdline)
  37. {
  38.     int len = mystrlen(cmdline) + 1;
  39.     
  40.     params->hdr.tag = ATAG_CMDLINE;
  41.     params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;

  42.     mystrcpy (params->u.cmdline.cmdline, cmdline);

  43.     params = tag_next (params);
  44. }

  45. void setup_end_tag(void)
  46. {
  47.     params->hdr.tag = ATAG_NONE;
  48.     params->hdr.size = 0;
  49. }


  50. /*
  51. *功能:使能ICache
  52. */
  53. void icache_enable()
  54. {
  55.     unsigned int temp = 1<<12;

  56.     asm(
  57.             "mrc p15,0,r0,c1,c0,0\n" /*读出控制寄存器c1的值到r0中*/
  58.             "orr r0,r0,%0\n" /*第12位I位置1,即使能ICache*/
  59.             "mcr p15,0,r0,c1,c0,0\n" /*将修改后的值写入控制寄存器*/
  60.             :
  61.             :"r"(temp)
  62.             :"r0"
  63.         );
  64. }


  65. /*
  66. *功能:禁止ICache
  67. */
  68. void icache_disable()
  69. {
  70.     unsigned int temp = 1<<12;

  71.     asm( /*GCC内联汇编函数*/
  72.             "mrc p15,0,r0,c1,c0,0\n"
  73.             "bic r0,r0,%0\n" /*第12位I位置0,即禁止ICache*/
  74.             "mcr p15,0,r0,c1,c0,0\n"
  75.             :
  76.             :"r"(temp)
  77.             :"r0"
  78.         );
  79. }
setup.h文件

点击(此处)折叠或打开

  1. /*
  2.  * linux/include/asm/setup.h
  3.  *
  4.  * Copyright (C) 1997-1999 Russell King
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10.  * Structure passed to kernel to tell it about the
  11.  * hardware it's running on. See linux/Documentation/arm/Setup
  12.  * for more info.
  13.  *
  14.  * NOTE:
  15.  * This file contains two ways to pass information from the boot
  16.  * loader to the kernel. The old struct param_struct is deprecated,
  17.  * but it will be kept in the kernel for 5 years from now
  18.  * (2001). This will allow boot loaders to convert to the new struct
  19.  * tag way.
  20.  */

  21. #ifndef _SETUP_H_
  22. #define _SETUP_H_

  23. #define u8 unsigned char
  24. #define u16 unsigned short
  25. #define u32 unsigned long

  26. /*
  27.  * Usage:
  28.  * - do not go blindly adding fields, add them at the end
  29.  * - when adding fields, don't rely on the address until
  30.  * a patch from me has been released
  31.  * - unused fields should be zero (for future expansion)
  32.  * - this structure is relatively short-lived - only
  33.  * guaranteed to contain useful data in setup_arch()
  34.  */
  35. //#define COMMAND_LINE_SIZE 1024

  36. /*
  37.  * The new way of passing information: a list of tagged entries
  38.  */

  39. /* The list ends with an ATAG_NONE node. */
  40. #define ATAG_NONE    0x00000000

  41. struct tag_header {
  42.     u32 size;
  43.     u32 tag;
  44. };

  45. /* The list must start with an ATAG_CORE node */
  46. #define ATAG_CORE    0x54410001

  47. struct tag_core {
  48.     u32 flags;        /* bit 0 = read-only */
  49.     u32 pagesize;
  50.     u32 rootdev;
  51. };

  52. /* it is allowed to have multiple ATAG_MEM nodes */
  53. #define ATAG_MEM    0x54410002

  54. struct tag_mem32 {
  55.     u32    size;
  56.     u32    start;    /* physical start address */
  57. };

  58. /* VGA text type displays */
  59. #define ATAG_VIDEOTEXT    0x54410003

  60. struct tag_videotext {
  61.     u8        x;
  62.     u8        y;
  63.     u16        video_page;
  64.     u8        video_mode;
  65.     u8        video_cols;
  66.     u16        video_ega_bx;
  67.     u8        video_lines;
  68.     u8        video_isvga;
  69.     u16        video_points;
  70. };

  71. /* describes how the ramdisk will be used in kernel */
  72. #define ATAG_RAMDISK    0x54410004

  73. struct tag_ramdisk {
  74.     u32 flags;    /* bit 0 = load, bit 1 = prompt */
  75.     u32 size;    /* decompressed ramdisk size in _kilo_ bytes */
  76.     u32 start;    /* starting block of floppy-based RAM disk image */
  77. };

  78. #define ATAG_INITRD    0x54410005

  79. /* describes where the compressed ramdisk image lives (physical address) */
  80. #define ATAG_INITRD2    0x54420005

  81. struct tag_initrd {
  82.     u32 start;    /* physical start address */
  83.     u32 size;    /* size of compressed ramdisk image in bytes */
  84. };

  85. /* board serial number. "64 bits should be enough for everybody" */
  86. #define ATAG_SERIAL    0x54410006

  87. struct tag_serialnr {
  88.     u32 low;
  89.     u32 high;
  90. };

  91. /* board revision */
  92. #define ATAG_REVISION    0x54410007

  93. struct tag_revision {
  94.     u32 rev;
  95. };

  96. /* initial values for vesafb-type framebuffers. see struct screen_info
  97.  * in include/linux/tty.h
  98.  */
  99. #define ATAG_VIDEOLFB    0x54410008

  100. struct tag_videolfb {
  101.     u16        lfb_width;
  102.     u16        lfb_height;
  103.     u16        lfb_depth;
  104.     u16        lfb_linelength;
  105.     u32        lfb_base;
  106.     u32        lfb_size;
  107.     u8        red_size;
  108.     u8        red_pos;
  109.     u8        green_size;
  110.     u8        green_pos;
  111.     u8        blue_size;
  112.     u8        blue_pos;
  113.     u8        rsvd_size;
  114.     u8        rsvd_pos;
  115. };

  116. /* command line: \0 terminated string */
  117. #define ATAG_CMDLINE    0x54410009

  118. struct tag_cmdline {
  119.     char    cmdline[1];    /* this is the minimum size */
  120. };

  121. /* acorn RiscPC specific information */
  122. #define ATAG_ACORN    0x41000101

  123. struct tag_acorn {
  124.     u32 memc_control_reg;
  125.     u32 vram_pages;
  126.     u8 sounddefault;
  127.     u8 adfsdrives;
  128. };

  129. /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
  130. #define ATAG_MEMCLK    0x41000402

  131. struct tag_memclk {
  132.     u32 fmemclk;
  133. };

  134. struct tag {
  135.     struct tag_header hdr;
  136.     union {
  137.         struct tag_core        core;
  138.         struct tag_mem32    mem;
  139.         struct tag_videotext    videotext;
  140.         struct tag_ramdisk    ramdisk;
  141.         struct tag_initrd    initrd;
  142.         struct tag_serialnr    serialnr;
  143.         struct tag_revision    revision;
  144.         struct tag_videolfb    videolfb;
  145.         struct tag_cmdline    cmdline;

  146.         /*
  147.          * Acorn specific
  148.          */
  149.         struct tag_acorn    acorn;

  150.         /*
  151.          * DC21285 specific
  152.          */
  153.         struct tag_memclk    memclk;
  154.     } u;
  155. };


  156. #define tag_next(t)    ((struct tag *)((u32 *)(t) + (t)->hdr.size))
  157. #define tag_size(type)    ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)

  158. void setup_start_tag(void);
  159. void setup_memory_tags(void);
  160. int mystrlen(char *str);
  161. void mystrcpy(char *dest, char *src);
  162. void setup_commandline_tag(char *cmdline);
  163. void setup_end_tag(void);

  164. void icache_enable();
  165. void icache_disable();

  166. #endif
setup1.S

点击(此处)折叠或打开

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

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

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

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

点击(此处)折叠或打开

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

  6. int main()
  7. {
  8.     unsigned long nand_read_dec_addr = 0x32000000; /*uImage的加载地址0x32000000*/
  9.     unsigned long nand_read_src_addr = 0x200000; /*uImage在nand中的地址*/
  10.     unsigned long nand_read_len_addr = 0x300000; /*读取nand的长度*/
  11.     
  12.     unsigned long kernel_enter_addr = 0x32000040; /*内核的入口地址 : uImage的加载地址0x32000000之后是64的头部=zImage的入口地址0x32000040*/
  13.     unsigned long kernel_flag = 0; /*固定的值*/
  14.     unsigned long kernel_board_id = 168; /*单板的机器ID*/
  15.     unsigned long kernel_tag_addr = 0x30000100; /*内核与uboot传递参数的地址*/
  16.     
  17.     void (*theKernel)(int zero, int arch, unsigned int params); /*定义内核启动函数,这个由内核固定的*/
  18.     
  19.     icache_enable();
  20.     /* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口
  21.     其实在第1阶段的代码中已经初始化的串口,这里可以不要 */
  22.     init_uart();

  23.     uart_sendString("\n\n");
  24.     uart_sendString("My BootLader start:\n\n");

  25.     /* 1. 从NAND FLASH里把内核读入内存 */
  26.     uart_sendString("Copy kernel from NAND to SDRAM\n");
  27.     uart_printf("nand_read(0x%x 0x%x 0x%x)\n", nand_read_dec_addr, nand_read_src_addr, nand_read_len_addr);
  28.     nand_read((unsigned char *)(nand_read_dec_addr), nand_read_src_addr, nand_read_len_addr);
  29.     uart_printf("\nnand_read OK!\n\n");
  30.     
  31.     /* 2. 设置参数 */
  32.     uart_sendString("Set boot params\n\n");
  33.     setup_start_tag(); //告诉内核,启动参数存放的地址
  34.     setup_memory_tags(); //告诉内核,我的内存是多大
  35.     setup_commandline_tag("bootargs=noinitrd console=ttySAC0,115200 root=/dev/nfs rw nfsroot=/home/wangxc/linux/rootfs/nfs_2.6.13 ip=192.168.1.92:192.168.1.91:192.168.1.1:255.255.255.0:host:eth0:off mem=64M");
  36.     setup_end_tag(); //告诉内核,标记列表结束了
  37.     
  38.     /* 3. 跳转执行 */
  39.     uart_printf("theKernel = (void (*)(int, int, unsigned int))(0x%x)\n", kernel_enter_addr);
  40.     theKernel = (void (*)(int, int, unsigned int))(kernel_enter_addr);
  41.     /*这里必须使用内核的入口地址 : uImage的加载地址0x32000000之后是64的头部=zImage的入口地址0x32000040*/
  42.     
  43.     /*theKernel函数参数解析:
  44.     0 : 是固定的值
  45.     168:单板的机器ID,由内核指定的固定值
  46.     0x30000100:启动参数保存的地址,必须和setup_start_tag中指定的起始地址一致。可以使用其他地址
  47.     */
  48.     uart_printf("theKernel(%d, %d, 0x%x)\n", kernel_flag, kernel_board_id, kernel_tag_addr);
  49.     uart_sendString("Boot kernel ...... \n\n\n");
  50.     theKernel(kernel_flag, kernel_board_id, kernel_tag_addr);
  51.     
  52.     /*theKernel是真正启动内核的函数。跳到内核的入口地址去执行了。
  53.     内核启动时,读取了一些参数,这些参数是在uboot时设置的,如何传递给内核?
  54.     最简单的方法是:在sdram中预留出来一块指定的地址,按某种格式,存放这个启动需要的一些参数。
  55.     uboot时,去填充这个地址,内核时,去读取这个地址就行了。
  56.     theKernel函数开始执行之后,再也不会返回了。uboot就正式结束了,uboot的任务也就全部完成了!!
  57.     */
  58.     
  59.     uart_sendString("Error!\n\r");
  60.     /* 如果一切正常, 不会执行到这里 */
  61.     //可以启动了,但是启动之后lcd上的图片和uboot启动时有些不一样,应该是还有一些参数没有传递给内核?????

  62.     return -1;
  63. }


  64. /*
  65. 详细测试过程 :


  66. */
uart.c文件

点击(此处)折叠或打开

  1. #include "uart.h"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  311.     va_end(ap);

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

点击(此处)折叠或打开

  1. #ifndef _UART_H_
  2. #define _UART_H_

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

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

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

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

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

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

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

  34. #endif

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