我自己写的最简易版本的,可以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文件
-
#ifndef __DEF_H__
-
#define __DEF_H__
-
-
#define U32 unsigned int
-
#define U16 unsigned short
-
#define S32 int
-
#define S16 short int
-
#define U8 unsigned char
-
#define S8 char
-
-
#endif
init.c文件
-
#include "init.h"
-
-
/*
-
* 关闭WATCHDOG,否则CPU会不断重启
-
*/
-
void disable_watch_dog(void)
-
{
-
WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
-
}
-
-
/*
-
* 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
-
* 有如下计算公式:
-
* S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
-
* 其中: m = MDIV + 8 = 92+8, p = PDIV + 2 = 1+2, s = SDIV = 2
-
* 对于本开发板,Fin = 12MHz
-
* 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:2:4,
-
* FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
-
*/
-
void clock_init(void)
-
{
-
// LOCKTIME = 0x00ffffff; // 使用默认值即可
-
CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
-
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
-
__asm__(
-
"mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
-
"orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
-
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
-
);
-
-
MPLLCON = S3C2440_MPLL_200MHZ;
-
/* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
-
并不保证你的任何设置都是合适的。所以,如果想要工作在200MHz,还是按照vivi的推荐值((0x5c<<12)|(0x01<<4)|(0x02))即可。
-
*/
-
}
-
-
void memsetup(void)
-
{
-
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
-
-
// 可生成”位置无关的代码”,使得这个函数可以在被复制到
-
// SDRAM之前就可以在steppingstone中运行
-
// 存储控制器13个寄存器的值
-
p[0] = 0x22011110; //BWSCON
-
p[1] = 0x00000700; //BANKCON0
-
p[2] = 0x00000700; //BANKCON1
-
p[3] = 0x00000700; //BANKCON2
-
p[4] = 0x00000700; //BANKCON3
-
p[5] = 0x00000700; //BANKCON4
-
p[6] = 0x00000700; //BANKCON5
-
p[7] = 0x00018005; //BANKCON6
-
p[8] = 0x00018005; //BANKCON7
-
-
// REFRESH,
-
// HCLK=12MHz: 0x008C07A3,
-
// HCLK=100MHz: 0x008C04F4
-
p[9] = 0x008C04F4;
-
p[10] = 0x000000B1; //BANKSIZE
-
p[11] = 0x00000030; //MRSRB6
-
p[12] = 0x00000030; //MRSRB7
-
}
-
-
void clean_bss(void)
-
{
-
extern int __bss_start, __bss_end; /*这2个在链接脚本中定义的变量*/
-
int *p = &__bss_start;
-
-
for (; p < &__bss_end; p++)
-
*p = 0;
-
}
init.h文件
-
#ifndef _INIT_H_
-
#define _INIT_H_
-
-
/* WATCHDOG寄存器 */
-
#define WTCON (*(volatile unsigned long *)0x53000000)
-
/* SDRAM寄存器 */
-
#define MEM_CTL_BASE 0x48000000
-
-
-
/*系统时钟相关寄存器*/
-
#define MPLLCON (*(volatile unsigned long *)0x4c000004)
-
#define CLKDIVN (*(volatile unsigned long *)0x4c000014)
-
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
-
-
void disable_watch_dog();
-
void clock_init(void);
-
void memsetup();
-
void clean_bss(void);
-
-
#endif
makefile文件
-
objs := step1.o init.o nand.o step2.o setup.o uart.o
-
-
myuboot.bin: $(objs)
-
arm-linux-ld -Tmyboot.lds -o myuboot_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
-
arm-linux-objcopy -O binary -S myuboot_elf $@
-
arm-linux-objdump -D -m arm myuboot_elf > myuboot.dis
-
-
%.o:%.c
-
arm-linux-gcc -Wall -O2 -c -o $@ $<
-
-
%.o:%.S
-
arm-linux-gcc -Wall -O2 -c -o $@ $<
-
-
clean:
-
rm -f myuboot.bin myuboot_elf myuboot.dis *.o
myboot.lds文件
-
SECTIONS { /*.:表示当前*/
-
. = 0x31000000; /*汇编语言中:.text段中_start的值=0x31000000*/
-
.text : { *(.text) }
-
-
. = ALIGN(4);
-
.rodata : {*(.rodata*)}
-
-
. = ALIGN(4);
-
.data : { *(.data) }
-
-
. = ALIGN(4);
-
__bss_start = .; /*编译出来的2进制程序比如uboot.bin中不会含有static变量初始化为0的存放的空间。我们把static变量统一放在bss段,运行程序前,将bss段清0,即将static变量初始化为0了*/
-
.bss : { *(.bss) *(COMMON) }
-
__bss_end = .;
-
}
nand.c文件
-
#include "nand.h"
-
#include "def.h"
-
#include "uart.h"
-
-
/*
-
*函数功能:判断是nand启动,还是nor启动
-
*返回值:0:nand启动,1:nor启动
-
*/
-
int isBootFromNorFlash(void)
-
{
-
volatile int *p = (volatile int *)0;
-
int val;
-
-
val = *p; /*先把0地址的值,保存到中间变量val*/
-
*p = 0x12345678; /*试图向0地址赋值*/
-
if(*p == 0x12345678) /*向0地址写数据成功*/
-
{
-
/* 写成功, 是nand启动 */
-
*p = val;
-
return 0;
-
}
-
else /*向0地址写数据失败*/
-
{
-
/* NOR不能像内存一样写 */
-
return 1;
-
}
-
}
-
-
/*
-
*功能:重启
-
*/
-
void nand_reset()
-
{
-
/*每操作NandFlash之前必须先片选*/
-
NF_CE_L();
-
/*发送命令之前要先清除RB*/
-
NF_CLEAR_RB();
-
/*向NandFlash写命令:即发送复位命令*/
-
NF_CMD(CMD_RESET);
-
/*注意:命令执行期间,NandFlash存储器忙,所以一直要等待为1:不忙*/
-
NF_WAIT_RB();
-
/*关闭片选*/
-
NF_CE_H();
-
}
-
-
/*
-
*功能:NANDFLASH初始化
-
*注意:1,本程序使用的是ARM9 2440处理器,需要设置 NFCONF,NFCONT这2个寄存器
-
* 2,NandFlash的芯片是K9F2G08U0A。由芯片手册可知:
-
* 容量:(256M+8M)字节,后面的8M叫OOB空间,坏块管理会用到它。
-
* 8bit
-
* 2K个块,每个块有64个page页
-
* 一个page页 =(2K+64)字节
-
* 所以,一个块 = 64页 = 64*(2K+64)字节 =(128K+2K)字节
-
* 一个Nand器件 = 2K个块 = 2K*64个page页 = 128K页
-
* 一个Nand器件 = 128K页 = 128K*(2K+64)字节 = (256M+8M)字节
-
* 3,片内内存+SDRAM+网卡+寄存器:是由CPU统一编址的,由CPU发出地址。
-
* 但是NandFlash不是由CPU统一编址的,所以NandFlash的0地址和片内内存的0地址,不是一回事。
-
* 4,NandFlash中的OOB不参与编址,读取NandFlash上的2049这个地址,不是读取的0页上的地址,是1页上的。
-
* 5,NandFlash控制器的各个控制引脚的时序,不需要程序员来驱动这些引脚,
-
* 程序员只需要设置相关寄存器之后,控制器会自动发出控制信号。
-
*/
-
void nand_init()
-
{
-
//char ret;
-
//char ch;
-
/*NANDFLASH配置寄存器 : NFCONF
-
[13:12]=01:时序图中CLE,ALE持续值设置,1个HCLK时钟周期,如HCLK是100Mhz,则,1个HCLK时钟周期=10ns。
-
[10:8]=011:时序图中TWRPH0持续值设置,3个HCLK时钟周期
-
[6:4]=100:时序图中TWRPH1持续值设置,0个HCLK时钟周期
-
*/
-
rNFCONF = ((TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4));
-
/*NANDFLASH控制寄存器 : NFCONT
-
其他位基本都是初始值或者不需要设置
-
[1]=1:禁止片选,CE引脚输出为1
-
[0]=1:MODE位,使能NandFlash控制器,命令使能CLE引脚设置为1
-
*/
-
rNFCONT = (1<<4)|(1<<1)|(1<<0);
-
nand_reset();
-
-
/* 下面串口打印函数不能用了,因为连接脚本将nand.o放在4k内,uart.o在4k外,不能调用了
-
uart_sendByte('\n');
-
uart_sendByte('w');
-
uart_sendByte('x');
-
uart_sendByte('c');
-
uart_sendByte('\n');
-
-
ret = nand_readId();
-
uart_sendByte('\n');
-
uart_sendString("nand_readId = ");
-
uart_sendByte_hex(ret);
-
uart_sendByte('\n');
-
-
uart_sendString("please input:\n");
-
ch = uart_getch();
-
uart_sendString("your input is:");
-
uart_sendByte(ch);
-
uart_sendByte('\n');
-
*/
-
-
return ;
-
}
-
-
-
/*功能:读取芯片的ID号
-
*注意:查看NandFlash的相关配置是否正确,可以去检查芯片的ID号。如果ID读取正确了,则说明配置正确了。
-
*第1个周期:厂商编号 : 0xEC
-
*第2个周期:设备编号:相同型号的芯片,有相同的ID号 : 0xDA,这个必须正确
-
*第3个周期:内部芯片编号 : 0x10
-
*第4个周期:页的大小 : 0x95
-
*第5个周期:一个page另外带的融错空间的大小 : 0x44
-
*/
-
char nand_readId()
-
{
-
int i;
-
U8 cyc1;
-
U8 cyc2;
-
U8 cyc3;
-
U8 cyc4;
-
U8 cyc5;
-
//片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
//发送读ID命令
-
NF_CMD(CMD_READID);
-
//发送地址值:需要读取这个地址上的值,读ID号,规定就是这个地址,看的时序图,只需发送一个地址.
-
NF_ADDR(0x0);
-
for(i=0; i<1000; i++);
-
//从nandflash中读取数据:读5次,看时序图
-
//注意:每次只能读取8位数据,保存在数据寄存器中。
-
cyc1 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc2 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc3 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc4 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
cyc5 = NF_RDDATA8();
-
for(i=0; i<100; i++);
-
-
//等待命令执行完毕
-
NF_WAIT_RB();
-
//关闭片选
-
NF_CE_H();
-
-
return cyc2;
-
}
-
-
-
/*功能 : 发出地址
-
*注意: 这个地址,不是页编号,是具体的读取NandFlash的起始地址,包括页内地址
-
*/
-
void write_addr(unsigned int addr)
-
{
-
//写地址(要读取数据的起始地址) :
-
//包括 : 列(页内)地址,是低位地址,共A0~A11,在具体的page页内寻址。一个page页=(2K+64)字节
-
// 行(页)地址,是高位地址,共A12~A28,可以寻址到page页。一个Nand器件=2K*64个page页 = 128K页
-
// 高位地址中,A12~A17,可以寻址到block块下面的page页。一个block块=64个page页
-
// 高位地址中,A18~A28,可以寻址到block块。一个Nand器件=2K个block块
-
//要写全部的5个周期的地址
-
-
//32位的page_number : 低29位有效 :
-
//| 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 |
-
// 对应最后3个地址周期的页内地址的高11位 :
-
//|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|
-
//29位在5个地址周期中应是如下的分布 :
-
//| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
-
//| L | L | L | L | L | L | L | A28 |
-
//| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
-
//| A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 |
-
//| L | L | L | L | A11 | A10 | A9 | A8 |
-
//| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
-
-
int i;
-
int col, page;
-
-
col = addr & (2048 -1); //截取低位:取2048的原因:本程序使用的nand芯片的一个page页是2048字节
-
page = addr / 2048; //截取高位
-
-
NF_ADDR(col & 0xFF); //列(页内)地址 A0~A7
-
for(i=0; i<100; i++);
-
NF_ADDR((col >> 8) & 0x0F); //列(页内)地址 A8~A11
-
for(i=0; i<100; i++);
-
-
NF_ADDR(page & 0xFF); //行(页)地址 A12~A19
-
for(i=0; i<100; i++);
-
NF_ADDR((page >> 8) & 0xFF); //行(页)地址 A20~A27
-
for(i=0; i<100; i++);
-
NF_ADDR((page >> 16) & 0x03); //行(页)地址 A28~A29
-
for(i=0; i<100; i++);
-
}
-
-
/*
-
*功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
-
* 数据方向:(Nand的start_addr->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* start_addr : 不是页编号,也就是说,不是每一个page页的开始地址处理,
-
* 是具体的读取NandFlash的起始地址,包括页内地址:0x1003=4099
-
* size : 复制的字节数,可以是页空间整数倍,也可以不是,也就是说,size可以小于2K,也可以大于2K。0x810,0x800=2048
-
*注意 : read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
-
*/
-
void nand_read(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
U32 i = 0, j = 0;
-
//片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
//读NandFlash时的第1个命令
-
//0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
-
//会自动满足时序图的要求,不再需要人工参与了。
-
//这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
-
NF_CMD(CMD_READ1);
-
//写地址
-
write_addr(i);
-
//读NandFlash时的第2个命令
-
NF_CMD(CMD_READ2);
-
//等待NandFlash不忙
-
NF_WAIT_RB();
-
-
//读数据 : 一般1次读1页的数据(1页=2K + 64字节),可能跨了2页才能读取一页的数据。
-
//也可以只读一个page页的一部分数据,只是多了几次下面的循环。
-
for(j=0; j<2048; j++, i++) //小于2048也行
-
{
-
//1次从nandflash的8位IO口上读取8位数据。
-
//注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
-
//前一次读操作未完成,是不会启动下一次读操作的。
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
uart_sendString("#"); //2个nand read函数:目的是:main之后才初始化uart,可以使用这个带打印#的函数
-
}
-
//关闭片选
-
NF_CE_H();
-
-
}
-
-
/*
-
*功能 : 从NandFlash位置start_addr开始读取数据,将数据复制到指定的SDRAM地址buf处,共复制size个字节。
-
* 数据方向:(Nand的start_addr->SDRAM的buf)
-
*参数 : buf : SDRAM的缓冲区起始地址:0x30001000
-
* start_addr : 不是页编号,也就是说,不是每一个page页的开始地址处理,
-
* 是具体的读取NandFlash的起始地址,包括页内地址:0x1003=4099
-
* size : 复制的字节数,可以是页空间整数倍,也可以不是,也就是说,size可以小于2K,也可以大于2K。0x810,0x800=2048
-
*注意 : read是相对于cpu说的,cpu读取nand中的程序代码,拷贝到SDRAM中去执行。
-
*/
-
void nand_read_1(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
U32 i = 0, j = 0;
-
//片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
//读NandFlash时的第1个命令
-
//0x00命令发出后,即相当于向NFCMD寄存器中写入了0,nandflash控制器会自动发出各种控制信号,
-
//会自动满足时序图的要求,不再需要人工参与了。
-
//这样,nandflash控制器提供的几个寄存器,就简化了对nandflash的操作。
-
NF_CMD(CMD_READ1);
-
//写地址
-
write_addr(i);
-
//读NandFlash时的第2个命令
-
NF_CMD(CMD_READ2);
-
//等待NandFlash不忙
-
NF_WAIT_RB();
-
-
//读数据 : 一般1次读1页的数据(1页=2K + 64字节),可能跨了2页才能读取一页的数据。
-
//也可以只读一个page页的一部分数据,只是多了几次下面的循环。
-
for(j=0; j<2048; j++, i++) //小于2048也行
-
{
-
//1次从nandflash的8位IO口上读取8位数据。
-
//注意:每次读此寄存器,即执行一次下面语句,就会启动对nandflash的1次读操作。
-
//前一次读操作未完成,是不会启动下一次读操作的。
-
*buf = NF_RDDATA8();
-
buf++;
-
}
-
}
-
//关闭片选
-
NF_CE_H();
-
-
}
-
-
-
/*
-
* buf : 目标地址 = 0x30000000,这是SDRAM的起始地址
-
* start_addr : 源地址 = 4096,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处
-
* size : 复制长度 = 600K,对于本实验,这是足够了,没有图片的话,16K足够了,有图片了,得300K
-
*/
-
int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
int i = 0;
-
-
/* 如果是NOR启动 */
-
if (isBootFromNorFlash())
-
{
-
while (i < size)
-
{
-
buf[i] = *((char *)start_addr);
-
i++;
-
start_addr++;
-
}
-
}
-
else
-
{
-
nand_read_1(buf, start_addr, size);
-
}
-
-
return 0;
-
}
-
-
-
/*
-
*功能 : 擦除指定的块
-
*参数 : block_number : 块号
-
*注意 : 1,一个NandFlash块有2K个块。
-
* 2,块被擦除后nand对应的块中的数据全部是1
-
* 3,命令字分为2个阶段
-
U8 nand_erase(U32 block_number)
-
{
-
int i;
-
U8 stat;
-
//片选
-
NF_CE_L();
-
//清除RB
-
NF_CLEAR_RB();
-
//写入第1个擦除命令
-
NF_CMD(CMD_ERASE1);
-
//写块的地址 : 注意 : 只写块的地址就行,块地址都在行(页)地址的高位上,
-
//所以不需要写前面2个周期的列(页内)地址,写后面3个周期的页地址的高11位就行。
-
//列地址 : 就是页内地址, 共12位
-
//行地址 : 就是页地址, 共17位 : 高11位(A28-A18)是2K的块地址,低6位是64的页地址。
-
-
//32位block_number:低11位有效 :
-
// | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
-
// 对应最后3个地址周期的页内地址的高11位 :
-
//| A28 | A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 | A19 | A18 |
-
//高11位在后面3个地址周期中应是如下的分布 :
-
//| I7 | I6 | I5 | I4 | I3 | I2 | I1 | I0 |
-
//| | | | | | | | A28 |
-
//| A27 | A26 | A25 | A24 | A23 | A22 | A21 | A20 |
-
//| A19 | A18 | | | | | | |
-
//
-
NF_ADDR((block_number << 6) & 0xFF);
-
NF_ADDR((block_number >> 2) & 0xFF);
-
NF_ADDR((block_number >> 10) & 0xFF);
-
//写入第2个擦除命令
-
NF_CMD(CMD_ERASE2);
-
-
for(i=0; i<1000; i++);
-
//写入读状态寄存器的命令
-
NF_CMD(CMD_STATUS);
-
do
-
{
-
//读取8位状态数据
-
stat = NF_RDDATA8();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示读取数据完成。第0位为0,表示擦除成功
-
//关闭片选
-
NF_CE_H();
-
-
//约定用0x66来表示擦除成功,或者写成功
-
return 0x66;
-
}
-
*/
-
-
-
/*功能 : 将buf处开始的数据,写入NandFlash的start_addr地址处,共写入size个字节。
-
* 数据方向:(SDRAM的buf->Nand的start_addr)
-
*参数 : buf : SDRAM的缓冲区起始地址
-
* start_addr : 不是页编号,也就是说,不是每一个page页的开始地址处理,
-
* 是具体的读取NandFlash的起始地址,包括页内地址:0x1003=4099
-
* size : 复制的字节数,可以是页空间整数倍,也可以不是,也就是说,size可以小于2K,也可以大于2K。0x810,0x800=2048
-
*注意 : write是相对于cpu说的,cpu写数据到nand中,SDRAM中的数据拷贝到Nand中。
-
U8 nand_write(unsigned char *buf, unsigned long start_addr, int size)
-
{
-
int i,j;
-
U8 stat;
-
//片选
-
NF_CE_L();
-
-
for(i=start_addr; i < (start_addr + size);)
-
{
-
//清除RB
-
NF_CLEAR_RB();
-
//写NandFlash时的第1个命令 : NandFlash写准备
-
NF_CMD(CMD_WRITE1);
-
//写地址
-
write_addr(i);
-
//写数据 : 一般1次写1页的数据 : 1页=2K字节+64字节,可能跨了2页才能写一页的数据。
-
//也可以只写一个page页的一部分数据,只是多了几次下面的循环。
-
for(j=0; j<2048; j++, i++)
-
{
-
//1次写8位的数据给NandFlash的8位IO口
-
//注意:每次写这个寄存器,即执行一次下面的语句,都会启动一次nandflash的写数据操作,
-
//前一次写数据操作不完成,是不会启动下一次写数据操作的
-
NF_WRDATA8(*buf);
-
buf++;
-
}
-
-
//写NandFlash时的第2个命令 : 启动写操作
-
//此时,Flash内部会自动完成写,校验操作。
-
NF_CMD(CMD_WRITE2);
-
for(j=0; j<100; j++);
-
//写入读状态寄存器的命令 : 用来判断当前写操作是否完成,是否成功
-
NF_CMD(CMD_STATUS);
-
do
-
{
-
//读取8位状态数据
-
stat = NF_RDDATA8();
-
}
-
while(!(stat & 0x40)); //等到第6位为1 : 表示写数据完成。第0位为0,表示写数据成功
-
}
-
//关闭片选
-
NF_CE_H();
-
-
return 0x66;
-
}
-
*/
nand.h文件
-
#ifndef _NAND_H_
-
#define _NAND_H_
-
-
/*NANDFLASH配置寄存器 : NFCONF:用来设置时序参数宽度,数据位宽*/
-
#define rNFCONF (*(volatile unsigned long *)0x4E000000)
-
/*NANDFLASH控制寄存器 : NFCONT:用来禁止/使能nandflash控制器,使能/禁止片选信号*/
-
#define rNFCONT (*(volatile unsigned long *)0x4E000004)
-
/*NANDFLASH命令集寄存器 : NFCMMD:用来向nandflash发出命令信号*/
-
#define rNFCMMD (*(volatile unsigned long *)0x4E000008)
-
/*NANDFLASH地址集寄存器 : NFADDR:用来向nandflash发出地址信号*/
-
#define rNFADDR (*(volatile unsigned long *)0x4E00000C)
-
/*NANDFLASH数据寄存器 : NFDATA,32位=0x4E000010+0x4E000011+0x4E000012+0x4E000013*/
-
#define rNFDATA (*(volatile unsigned long *)0x4E000010)
-
/*截取NANDFLASH数据寄存器的8位 : 仅0x4E000010,实际上每次读写也只用到低8位,不可能用到32位。
-
每次读写此寄存器,就将启动一次对nandflash的读数据写数据的操作。*/
-
#define rNFDATA8 (*(volatile unsigned char *)0x4E000010)
-
/*NANDFLASH运行状态寄存器 : NFSTAT:只用了最低位,0:busy,1:ready*/
-
#define rNFSTAT (*(volatile unsigned long *)0x4E000020)
-
-
/*相关命令如下 : */
-
/*读NandFlash页时的命令1*/
-
#define CMD_READ1 0x00
-
/*读NandFlash页时的命令2*/
-
#define CMD_READ2 0x30
-
-
/*页编程 : 写NandFlash页时的命令1*/
-
#define CMD_WRITE1 0x80
-
/*页编程 : 写NandFlash页时的命令2*/
-
#define CMD_WRITE2 0x10
-
-
/*擦除NandFlash块的命令1*/
-
#define CMD_ERASE1 0x60
-
/*擦除NandFlash块的命令2*/
-
#define CMD_ERASE2 0xD0
-
-
/*读状态寄存器的命令*/
-
#define CMD_STATUS 0x70
-
/*读取芯片ID的命令*/
-
#define CMD_READID 0x90
-
/*重启命令*/
-
#define CMD_RESET 0xFF
-
-
#define NF_CE_L() {(rNFCONT) &= (~(1<<1));}
-
/*禁止nandfalsh : 控制寄存器NFCONT[1]=1*/
-
#define NF_CE_H() {(rNFCONT) |= (1<<1);}
-
-
/*向存储器发出命令 : 命令寄存器NFCMMD[7:0]*/
-
#define NF_CMD(data) {(rNFCMMD) = (data);}
-
/*向存储器发出地址值 : 地址寄存器NFADDR[7:0]*/
-
#define NF_ADDR(data) {(rNFADDR) = (data);}
-
-
/*等待NandFlash准备就绪(即不忙) : 一直等到状态寄存器NFSTAT[0]=1,循环才退出*/
-
#define NF_WAIT_RB() {while(!((rNFSTAT) & (1<<0)));}
-
/*清除RB:即检测RB传输*/
-
#define NF_CLEAR_RB() {(rNFSTAT) |= (1<<2);}
-
/*等待检测RnB : 一直等到状态寄存器NFSTAT[2]=1,循环才退出*/
-
#define NF_DETECT_RB {while(!((rNDSTA) & (1<<2)));}
-
-
/*从NANDFLASH的IO口中读数据(32位) : 数据寄存器NFDATA*/
-
#define NF_RDDATA() (rNFDATA)
-
/*从NANDFLASH的IO口中读数据(8位) : 数据寄存器NFDATA*/
-
#define NF_RDDATA8() (rNFDATA8)
-
/*向NANDFLASH的IO口中写数据(32位) : 数据寄存器NFDATA*/
-
#define NF_WRDATA(data) {(rNFDATA) = (data);}
-
/*向NANDFLASH的IO口中写数据(8位) : 数据寄存器NFDATA*/
-
#define NF_WRDATA8(data) {(rNFDATA8) = (data);}
-
-
/*时序信号参数的宽度*/
-
#define TACLS 1
-
#define TWRPH0 3
-
#define TWRPH1 0
-
-
int isBootFromNorFlash(void);
-
void nand_reset();
-
void nand_init();
-
char nand_readId();
-
void write_addr(unsigned int addr);
-
void nand_read(unsigned char *buf, unsigned long start_addr, int size);
-
void nand_read_1(unsigned char *buf, unsigned long start_addr, int size);
-
int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size);
-
//U8 nand_erase(U32 block_number);
-
//U8 nand_write(unsigned char *buf, unsigned long start_addr, int size);
-
-
#endif
setup.c文件
-
#include "setup.h"
-
-
static struct tag *params;
-
-
int mystrlen(char *str)
-
{
-
int i = 0;
-
while (str[i])
-
{
-
i++;
-
}
-
return i;
-
}
-
-
void mystrcpy(char *dest, char *src)
-
{
-
while ((*dest++ = *src++) != '\0');
-
}
-
-
void setup_start_tag(void)
-
{
-
params = (struct tag *)0x30000100;
-
-
params->hdr.tag = ATAG_CORE;
-
params->hdr.size = tag_size (tag_core);
-
-
params->u.core.flags = 0;
-
params->u.core.pagesize = 0;
-
params->u.core.rootdev = 0;
-
-
params = tag_next (params);
-
}
-
-
void setup_memory_tags(void)
-
{
-
params->hdr.tag = ATAG_MEM;
-
params->hdr.size = tag_size (tag_mem32);
-
-
params->u.mem.start = 0x30000000;
-
params->u.mem.size = 64*1024*1024;
-
-
params = tag_next (params);
-
}
-
-
void setup_commandline_tag(char *cmdline)
-
{
-
int len = mystrlen(cmdline) + 1;
-
-
params->hdr.tag = ATAG_CMDLINE;
-
params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;
-
-
mystrcpy (params->u.cmdline.cmdline, cmdline);
-
-
params = tag_next (params);
-
}
-
-
void setup_end_tag(void)
-
{
-
params->hdr.tag = ATAG_NONE;
-
params->hdr.size = 0;
-
}
-
-
-
/*
-
*功能:使能ICache
-
*/
-
void icache_enable()
-
{
-
unsigned int temp = 1<<12;
-
-
asm(
-
"mrc p15,0,r0,c1,c0,0\n" /*读出控制寄存器c1的值到r0中*/
-
"orr r0,r0,%0\n" /*第12位I位置1,即使能ICache*/
-
"mcr p15,0,r0,c1,c0,0\n" /*将修改后的值写入控制寄存器*/
-
:
-
:"r"(temp)
-
:"r0"
-
);
-
}
-
-
-
/*
-
*功能:禁止ICache
-
*/
-
void icache_disable()
-
{
-
unsigned int temp = 1<<12;
-
-
asm( /*GCC内联汇编函数*/
-
"mrc p15,0,r0,c1,c0,0\n"
-
"bic r0,r0,%0\n" /*第12位I位置0,即禁止ICache*/
-
"mcr p15,0,r0,c1,c0,0\n"
-
:
-
:"r"(temp)
-
:"r0"
-
);
-
}
setup.h文件
setup1.S
-
@*************************************************************************
-
@ File:step1.S bootloader第1阶段的代码
-
@ 功能:关watch_dog,初始化clock,初始化SDRAM,初始化NAND Flash,代码重定位到SDRAM
-
@ 然后跳到SDRAM,继续执行第2阶段的代码
-
@*************************************************************************
-
-
.text @text表示是代码段
-
.global _start
-
_start: /*_start在这里表示.text段的链接地址。对应于:链接脚本中指定的.text段的起始地址=0x31000000*/
-
@函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
-
ldr sp, =0x34000000 @设置栈
-
bl disable_watch_dog @关WATCH DOG
-
bl clock_init @设置MPLL,改变FCLK、HCLK、PCLK
-
bl memsetup @初始化SDRAM
-
@bl init_uart @初始化UART,如果代码比较大,这个函数不一定会在4k内,所以在main函数中调用
-
bl nand_init @初始化NAND Flash
-
-
@ 复制nand中4k后的代码到SDRAM中
-
ldr r0, =_start @ 1. 目标地址 = 0x31000000,这是在SDRAM中的链接地址
-
ldr r1, =0x0 @ 2. 源地址 = 0,运行地址在SDRAM中的代码保存在NAND Flash 0地址开始处,或者norflash的0地址处
-
ldr r2, =__bss_start @ 3. 复制长度
-
sub r2, r2, r0
-
bl CopyCode2SDRAM @ 调用C函数CopyCode2SDRAM
-
-
bl clean_bss @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段
-
-
ldr lr, =my_loop @设置返回地址
-
ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
-
my_loop:
-
b my_loop
setup2.c
-
#include "def.h"
-
#include "init.h"
-
#include "nand.h"
-
#include "uart.h"
-
#include "setup.h"
-
-
int main()
-
{
-
unsigned long nand_read_dec_addr = 0x32000000; /*uImage的加载地址0x32000000*/
-
unsigned long nand_read_src_addr = 0x200000; /*uImage在nand中的地址*/
-
unsigned long nand_read_len_addr = 0x300000; /*读取nand的长度*/
-
-
unsigned long kernel_enter_addr = 0x32000040; /*内核的入口地址 : uImage的加载地址0x32000000之后是64的头部=zImage的入口地址0x32000040*/
-
unsigned long kernel_flag = 0; /*固定的值*/
-
unsigned long kernel_board_id = 168; /*单板的机器ID*/
-
unsigned long kernel_tag_addr = 0x30000100; /*内核与uboot传递参数的地址*/
-
-
void (*theKernel)(int zero, int arch, unsigned int params); /*定义内核启动函数,这个由内核固定的*/
-
-
icache_enable();
-
/* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口
-
其实在第1阶段的代码中已经初始化的串口,这里可以不要 */
-
init_uart();
-
-
uart_sendString("\n\n");
-
uart_sendString("My BootLader start:\n\n");
-
-
/* 1. 从NAND FLASH里把内核读入内存 */
-
uart_sendString("Copy kernel from NAND to SDRAM\n");
-
uart_printf("nand_read(0x%x 0x%x 0x%x)\n", nand_read_dec_addr, nand_read_src_addr, nand_read_len_addr);
-
nand_read((unsigned char *)(nand_read_dec_addr), nand_read_src_addr, nand_read_len_addr);
-
uart_printf("\nnand_read OK!\n\n");
-
-
/* 2. 设置参数 */
-
uart_sendString("Set boot params\n\n");
-
setup_start_tag(); //告诉内核,启动参数存放的地址
-
setup_memory_tags(); //告诉内核,我的内存是多大
-
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");
-
setup_end_tag(); //告诉内核,标记列表结束了
-
-
/* 3. 跳转执行 */
-
uart_printf("theKernel = (void (*)(int, int, unsigned int))(0x%x)\n", kernel_enter_addr);
-
theKernel = (void (*)(int, int, unsigned int))(kernel_enter_addr);
-
/*这里必须使用内核的入口地址 : uImage的加载地址0x32000000之后是64的头部=zImage的入口地址0x32000040*/
-
-
/*theKernel函数参数解析:
-
0 : 是固定的值
-
168:单板的机器ID,由内核指定的固定值
-
0x30000100:启动参数保存的地址,必须和setup_start_tag中指定的起始地址一致。可以使用其他地址
-
*/
-
uart_printf("theKernel(%d, %d, 0x%x)\n", kernel_flag, kernel_board_id, kernel_tag_addr);
-
uart_sendString("Boot kernel ...... \n\n\n");
-
theKernel(kernel_flag, kernel_board_id, kernel_tag_addr);
-
-
/*theKernel是真正启动内核的函数。跳到内核的入口地址去执行了。
-
内核启动时,读取了一些参数,这些参数是在uboot时设置的,如何传递给内核?
-
最简单的方法是:在sdram中预留出来一块指定的地址,按某种格式,存放这个启动需要的一些参数。
-
uboot时,去填充这个地址,内核时,去读取这个地址就行了。
-
theKernel函数开始执行之后,再也不会返回了。uboot就正式结束了,uboot的任务也就全部完成了!!
-
*/
-
-
uart_sendString("Error!\n\r");
-
/* 如果一切正常, 不会执行到这里 */
-
//可以启动了,但是启动之后lcd上的图片和uboot启动时有些不一样,应该是还有一些参数没有传递给内核?????
-
-
return -1;
-
}
-
-
-
/*
-
详细测试过程 :
-
-
-
*/
uart.c文件
-
#include "uart.h"
-
-
/***********************************************************************************/
-
//串口相关函数
-
/*功能:初始化串口UART0
-
*轮询方式来使用串口
-
*/
-
void init_uart(void)
-
{
-
/*GPHCON寄存器
-
[7:6]=10,GPH2作为TXD0,串口发送数据引脚
-
[5:4]=10,GPH3作为RXD0,串口接受数据引脚
-
GPH2,GPH3控制串口通道0
-
GPH4,GPH5控制串口通道1
-
GPH6,GPH7控制串口通道2
-
*/
-
rGPHCON = ((2<<6)|(2<<4)|(0<<2)|(0<<0));
-
rGPHUP = ((1<<3)|(1<<2)); /*禁止上拉*/
-
-
/*ULCON0串口线路控制寄存器:设置传输格式
-
[6]=0,普通模式,非红外工作模式
-
[5:3]=000,无奇偶校验位,100=奇校验,101=偶校验,110=固定校验位为1,111=固定校验位为0
-
[2]=0,每帧1个停止位,1:每帧2个停止位
-
[1:0]=11,8位,每帧发送或接受的数据位的个数。00=5位,01=6位,10=7位
-
*/
-
rULCON0 = 0x3;
-
-
/*UCON0:串口控制寄存器:用于选择UART时钟源,设置UART中断方式
-
[15:12]: 当[11:10]选择PCLK时,这4位无效。选择FCLK/n时,表示n值。
-
[11:10]=00: 选择PCLK给UART比特率,10:PCLK, 01:UEXTCLK, 11:FCLK/n。
-
[9]=1: 中断请求类型
-
[8]=0: 中断请求类型
-
[7]=0: 禁止超时中断,1:使能超时中断
-
[6]=1: 产生接受错误状态中断,1:不产生接受错误状态中断
-
[5]=0: 正常操作,非回环模式。发送引脚发送的数据,直接到达接受引脚,一般是测试用。
-
[4]=0: 正常操作,不发送断电信号
-
[3:2]=01: 发送模式,中断或者轮询,如果中断寄存器不设置,就是轮询。
-
[1:0]=01: 接受模式,中断或者轮询
-
*/
-
rUCON0 = 0x5;
-
-
/*UFCON:串口FIFO控制寄存器:用于设置是否使用FIFO,设置各FIFO的触发阀值。
-
[7:6]=发送FIFO的触发深度,即在发送时,发送FIFO中还剩有多少个数据时,才触发中断,告诉CPU可以继续发送了。
-
[5:4]=接受FIFO的触发深度,即在接受时,接受FIFO中接受了多少个数据后,才触发中断,告诉CPU已经有接受到的数据了。
-
[3]=空
-
[2]=TX的FIFO是否复位
-
[1]=RX的FIFO是否复位
-
[0]=0,不使用FIFO,非FIFO模式,相当于接受和发送时,不使用缓冲寄存器,上面的设置无效。如果这里是1,则上面的位肯定有对应的设置。
-
*/
-
rUFCON0 = 0x0;
-
-
/*UMCON:串口MODEM控制寄存器:用于流量控制
-
[7:5]=000,规定必须为0
-
[4]=0:禁止自动流控制(AFC),即不使用流控。一般不使用这种硬件自动流控制手段。
-
[3:1]=000:规定必须为0
-
[0]=0:高电平(撤销nRTS),1:低电平(激活nRTS),因为,AFC位禁止,nRTS必须由软件控制。如果AFC使能,则忽略这个位。
-
*/
-
rUMCON0 = 0x0;
-
-
/*UBRDIV:波特率分频寄存器:用于设置波特率
-
[15:0],波特率分频值。使用UEXTCLK作为输入时钟时,可以设置UBRDIV为0.
-
UBRDIV = (int)(UART时钟/(波特率*16))-1
-
*/
-
rUBRDIV0 = UART_BRD;
-
}
-
-
/*功能:向串口发送一个字符
-
*参数:待发送的字符
-
-
UTRSTAT0:用来表明数据是否已经发送完毕,是否已经接受到数据
-
串口收发TX/RX状态寄存器,是CPU自动更新的,程序员检测它的状态就行。
-
[2]:发送移位寄存器空标志。
-
检测为0:表示发送移位寄存器非空,或发送缓冲寄存器非空。
-
检测为1:表示发送移位寄存器为空,且发送缓冲寄存器为空,程序员可以继续发送数据了。
-
[1]:发送缓冲区空标志位。
-
检测为0:表示发送缓冲寄存器非空,即上次要发送的数据还没有发完。不能太快,否则会覆盖掉。
-
检测为1:表示发送缓冲寄存器为空,程序员可以继续发送数据了。(非FIFO模式,非请求中断,非DMA)
-
注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
-
[0]:接收缓冲器数据就绪标志位。
-
检测为0:表示接收缓冲器为空。
-
检测为1:表示接收缓冲器接收到有效数据,程序员可以取数据了。(非FIFO模式,非请求中断,非DMA)
-
注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
-
-
UTXH0:串口发送缓冲寄存器
-
[7:0]:串口要发送的数据
-
*/
-
void uart_sendByte(int data)
-
{
-
if(data == '\n')
-
{
-
while(!(rUTRSTAT0 &0x2));
-
WrUTXH0('\r'); /*回车不换行*/
-
}
-
/*等待,直到发送缓冲区中的数据已经全部发送出去
-
这里也可以检测UTRSTAT0[2]位*/
-
while(!(rUTRSTAT0 &0x2));
-
/*向UTXH0寄存器中写入数据,UART即自动将它发送出去
-
#define rUTXH0 (*(volatile unsigned char *)0x50000020) //UART 0 Transmission Hold
-
#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
-
所以:WrUTXH0(data); 相当于 rUTXH0 = data;
-
*/
-
WrUTXH0(data);
-
}
-
-
/*功能:向串口打印一个字符串
-
*参数:待打印的字符串
-
*/
-
void uart_sendString(char *p)
-
{
-
while(*p)
-
uart_sendByte(*p++);
-
}
-
-
/*功能:向串口打印一个字符的16进制格式
-
*参数:待打印的字符
-
* 数字0=0x30,数字9=0x39
-
*/
-
void uart_sendByte_hex(unsigned long val)
-
{
-
// val = 0x1234ABCD
-
unsigned long c;
-
int i = 0;
-
-
uart_sendByte('0');
-
uart_sendByte('x');
-
-
for (i = 0; i < 8; i++)
-
{
-
c = (val >> ((7-i)*4)) & 0xf;
-
if((c >= 0) && (c <= 9))
-
{
-
c = '0' + c;
-
}
-
else if ((c >= 0xA) && (c <= 0xF))
-
{
-
c = 'A' + (c - 0xA);
-
}
-
uart_sendByte((int)c);
-
}
-
}
-
-
/*功能:从串口接受一个字符
-
*返回值:接受到的字符
-
-
UTRSTAT0: 串口收发TX/RX状态寄存器
-
[0]:接收缓冲器数据就绪标志位。
-
0:接收缓冲器为空
-
1:接收缓冲器接收到有效数据(非FIFO模式,非请求中断,非DMA)
-
注意:如果UART使用FIFO,用户应该使用UFSTAT寄存器中的Rx FIFO计数位 和 Rx FIFO满位取代对此位的检查。
-
-
URXH0:串口接受缓冲寄存器
-
[7:0]:串口接受到的数据
-
*/
-
char uart_getch(void)
-
{
-
/*等待,直到接受缓冲区中有数据*/
-
while(!(rUTRSTAT0 & 0x1));
-
/*直接读取URXH0寄存器,即可以获得接受到的数据
-
#define rURXH0 (*(volatile unsigned char *)0x50000024) //UART 0 Receive buffer
-
#define RdURXH0() (*(volatile unsigned char *)0x50000024)
-
所以:return RdURXH0(); 相当于:return rURXH0;
-
*/
-
return RdURXH0();
-
}
-
-
/*功能:从串口接受一个字符串
-
*参数:输入的字符串
-
*/
-
void uart_getString(char *string)
-
{
-
char *string2 = string;
-
char c;
-
while((c=uart_getch()) != '\r')
-
{
-
if(c == '\b')
-
{
-
if((int)string2 < (int)string)
-
{
-
//uart_printf("\b\b");
-
string--;
-
}
-
}
-
else
-
{
-
*string++ = c;
-
uart_sendByte(c);
-
}
-
}
-
*string = '\0';
-
uart_sendByte('\n');
-
}
-
-
/***********************************************************************************/
-
/*
-
*功能:整型(int) 转化成 字符型(char)
-
*注意:不用 % / 符号的话,只能正确打印:0...9的数字对应的字符'0'...'9'
-
*/
-
void itoa(unsigned int n, char * buf)
-
{
-
int i;
-
-
if(n < 10){
-
buf[0] = n + '0';
-
buf[1] = '\0';
-
return;
-
}
-
itoa(n / 10, buf);
-
-
for(i=0; buf[i]!='\0'; i++);
-
-
buf[i] = (n % 10) + '0';
-
-
buf[i+1] = '\0';
-
}
-
-
/*
-
*功能:16进制字(0x) 转化成 字符型(char)
-
*注意:不用 % / 符号的话,只能正确打印,0...9..15的数字,对应的'0'...'9''A'...'F'
-
*注意:由于编译问题,这个函数,暂时由uart_sendByte_hex()函数替代
-
*/
-
void xtoa(unsigned int n, char * buf)
-
{
-
int i;
-
-
if(n < 16)
-
{
-
if(n < 10)
-
{
-
buf[0] = n + '0';
-
}
-
else
-
{
-
buf[0] = n - 10 + 'a';
-
}
-
buf[1] = '\0';
-
return;
-
}
-
xtoa(n / 16, buf);
-
-
for(i = 0; buf[i] != '\0'; i++);
-
-
if((n % 16) < 10)
-
{
-
buf[i] = (n % 16) + '0';
-
}
-
else
-
{
-
buf[i] = (n % 16) - 10 + 'a';
-
}
-
buf[i + 1] = '\0';
-
}
-
-
/*******************************************************************************************
-
为了支持求余求模,需要:
-
1,修改makefile如下:增加libgcc的库
-
arm-linux-ld -Tuart.lds -o uart_elf $^ $(shell arm-linux-gcc -msoft-float -print-libgcc-file-name)
-
上面一句展开后,其实就是下面的一句:
-
## 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
-
2,自己手写libgcc的库。
-
这个在uart3实现
-
*******************************************************************************************/
-
-
/*功能:向串口格式化打印一个字符串
-
*参数:格式化的字符串
-
*注意:由于求模求余的问题没有解决,所以这里%d输出时,只能输出0~9,10和10以上的不能输出
-
*/
-
int uart_printf(const char *fmt, ...)
-
{
-
int count = 0;
-
char c;
-
char *s;
-
int n;
-
char buf[65];
-
va_list ap;
-
-
va_start(ap, fmt);
-
-
while(*fmt != '\0')
-
{
-
if(*fmt == '%')
-
{
-
fmt++;
-
switch(*fmt)
-
{
-
case 'd': /*整型*/
-
n = va_arg(ap, int);
-
if(n < 0)
-
{
-
//uputchar('-');
-
uart_sendByte('-');
-
n = -n;
-
}
-
itoa(n, buf);
-
//_uputs(buf);
-
uart_sendString(buf);
-
break;
-
case 'c': /*字符型*/
-
c = va_arg(ap, int);
-
uart_sendByte(c);
-
break;
-
case 'x': /*16进制*/
-
n = va_arg(ap, int);
-
//uart_sendByte_hex(n); /*由于求模求余编译有问题,所以用uart_sendByte_hex来替代下面的2行代码*/
-
xtoa(n, buf);
-
uart_sendString(buf);
-
break;
-
case 's': /*字符串*/
-
s = va_arg(ap, char *);
-
uart_sendString(s);
-
break;
-
case '%': /*输出%*/
-
uart_sendByte('%');
-
break;
-
default:
-
break;
-
}
-
}
-
else
-
{
-
uart_sendByte(*fmt);
-
if(*fmt == '\n')
-
{
-
//uart_sendByte('\r');
-
}
-
}
-
fmt++;
-
}
-
-
va_end(ap);
-
-
return count;
-
}
uart.h文件
-
#ifndef _UART_H_
-
#define _UART_H_
-
-
/**************************************************************************/
-
//串口相关寄存器
-
#define rULCON0 (*(volatile unsigned *)0x50000000)
-
#define rUCON0 (*(volatile unsigned *)0x50000004)
-
#define rUFCON0 (*(volatile unsigned *)0x50000008)
-
#define rUMCON0 (*(volatile unsigned *)0x5000000c)
-
#define rUTRSTAT0 (*(volatile unsigned *)0x50000010)
-
#define rUBRDIV0 (*(volatile unsigned *)0x50000028)
-
-
#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
-
#define RdURXH0() (*(volatile unsigned char *)0x50000024)
-
-
#define rGPHCON (*(volatile unsigned *)0x56000070)
-
#define rGPHUP (*(volatile unsigned *)0x56000078)
-
-
#define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
-
#define UART_CLK PCLK // UART0的时钟源设为PCLK
-
#define UART_BAUD_RATE 115200 // 波特率
-
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
-
-
typedef char * va_list;
-
#define va_start(ap,p) (ap = (char *) (&(p)+1))
-
#define va_arg(ap, type) ((type *) (ap += sizeof(type)))[-1]
-
#define va_argp(ap, type) ((type *) (ap += sizeof(type)))-1
-
#define va_end(ap)
-
-
void init_uart(void);
-
void uart_sendByte(int data);
-
void uart_sendString(char *p);
-
void uart_sendByte_hex(unsigned long val);
-
char uart_getch(void);
-
void uart_getString(char *string);
-
-
void itoa(unsigned int n, char * buf);
-
void xtoa(unsigned int n, char * buf);
-
int uart_printf(const char *fmt, ...);
-
/**************************************************************************/
-
-
#endif
阅读(831) | 评论(0) | 转发(0) |