历史上的今天
今天是:2024年11月05日(星期二)
2021年11月05日 | 基于s3c2440的简易bootloader实现
2021-11-05 来源:eefocus
一、目的
编写一个能够加载并启动OS内核的bootloader。
二、思路
第一阶段:
(1)arm920t的异常向量表有两种存放方式,一种是低端存放(从0x00000000处开始存放),另一种是高端存放(从0xfff000000处开始存放)。选择低端存放,建立异常向量表。
(2)s3c2440的看门狗在上电启动时默认是开启的,所有要先把看门狗关了先。免得代码运行还没完成就强制复位。
(3)屏蔽掉所有中断。
(4)初始化时钟。
(5)初始化内存。
(6)清零bss段。
(7)设置好各个模式下的栈空间。
(8)重定位代码,使得代码的运行地址与链接地址相对应,之后就可以直接使用绝对地址。
(9)使用绝对跳转指令跳转到第二阶段,用c语言来实现。
第二阶段:
(1)初始化串口0,一方面方便我们的调试,一方面也为内核启动时打印信息做好初始化。
(2)初始化nand flash,需要把nand flash里的内核镜像拷贝到内存。
(3)把内核镜像拷贝到内存指定位置。
(4)设置好传递给内核的参数,并放置在约定的位置。
(5)跳转到内核起始地址处开始启动内核。
完毕。
三、流程图设计

四、代码树结构
(1)drivers里的src目录放置与外围设备相关配置的编程文件,inc目录放置相关头文件。对应的makefile放在drivers目录里。
①drivers/Makefile
#把src目录下的所有.c文件的字符串替换成.o字符串
TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) )
TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )
all:$(TARGET)
echo "$(TARGET) 编译完成"
GPATH=$(PWD)
vpath %.c ./src
vpath %.S ./src
%.o:%.c
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o
②drivers/inc
nand.h
#ifndef _NAND_H
#define _NAND_H
/**
*提取出页内列号,块内页号,块号
*/
#define dnand_addr2ColAddr(addr) (addr & 0x7FF)
#define dnand_addr2RowAddr(addr) (( addr & 0xFC00 ) >> 11)
#define dnand_addr2BlockAddr(addr) (addr >> 17)
//初始化NAND Flash , 时钟频率改变了这个一定要记得改
#define dnand_init()
do{
NFCONT = 0x73;
NFCONF = (3<<12) | (1<<8) | (1<<4);
}while(0)
//复位
void fnand_reset(void);
//等待NAND Flash就绪
#define dnand_waitReady() while(!(NFSTAT & 0x1))
//发出片选信号
#define dnand_enable() (NFCONT &= ~(1<<1))
//取消片选信号
#define dnand_disable() (NFCONT |= (1<<1))
//发出命令
#define dnand_writeCmd(cmd) (NFCMMD = cmd)
//读数据
#define dnand_readData() (*(volatile unsigned char *)&NFDATA)
//写数据
#define dnand_writeData(data) *(volatile unsigned char *)&NFDATA = data
//写地址
#define dnand_writeAddr(addr)
do{ NFADDR = addr & 0xff;
NFADDR = (addr >> 8) & 0x0f;
NFADDR = (addr >> 11) & 0xff;
NFADDR = (addr >> 19) & 0xff;
}while(0)
//擦除块
unsigned char fnand_eraseBlocks(unsigned int startBlockNum , unsigned int blockCount);
//从nand闪存里的sourAddr地址处读取size大小的数据到内存destAddr处。
void fnand_readOfSize(unsigned char *destAddr , unsigned int sourAddr , unsigned int size);
//。从内存sourAddr处读取size大小的数据到nand闪存里的destAddr地址处
unsigned char fnand_writeOfSize(unsigned char *sourAddr , unsigned int destAddr , unsigned int size);
#endif
uart0.h
#ifndef UART0_H
#define UART0_H
#define TXD0_READY (0x01<<1)
#define UBRDIV0_VAL (101250000UL/(115200*16)-1)
/**********初始化uart0中断的配置***********/
void uart0_init(void);
/*******把string消息通过uart0发送出去******/
void uart0_sent_msg(char *string);
void uart0_sent_byte(char byte);
/*打印hex数据*/
void uart0_sent_hex_word(unsigned int val);
#endif
③drivers/src
nand.c
#include "nand.h"
#include "s3c2440.h"
/**
* 复位
*/
void fnand_reset(void)
{
dnand_enable();
dnand_writeCmd(0xff); // 复位命令
dnand_waitReady();
dnand_disable();
}
/**
*块擦除函数
*/
unsigned char fnand_eraseBlocks(unsigned int startBlockNum , unsigned int blockCount)
{
unsigned int i;
dnand_enable();
for( i = 0 ; i < blockCount ; i++ ){
//发送擦除命令
dnand_writeCmd( 0x60 );
dnand_writeAddr( startBlockNum << 6 );
dnand_writeCmd(0xD0);
dnand_waitReady();
//读取状态
dnand_writeCmd( 0x70 );
if ( ( dnand_readData() & 0x01 ) ){
return 1;
}
startBlockNum += 1;
}
dnand_disable();
return 0;
}
void fnand_readOfSize(unsigned char *destAddr , unsigned int sourAddr , unsigned int size)
{
unsigned int j , col;
col = sourAddr & 0x7FF ; //该地址可能不是从页的0地址开始读 ,所以要先取出列地址
dnand_enable();
for( j = 0 ; j < size ; ){
//发出read命令
dnand_writeCmd( 0x00 );
dnand_writeAddr( sourAddr );
dnand_writeCmd( 0x30 );
dnand_waitReady( );
//开始读一页数据到destAddr里 ,
for( ; (col < 2048)&&(j
j++;
sourAddr++;
}
col = 0;
}
dnand_disable();
}
/**
*从内存的sourAddr处写入pageCount页数据到nand flash的destAddr地址处
*/
unsigned char fnand_writeOfSize(unsigned char *sourAddr , unsigned int destAddr , unsigned int size)
{
unsigned int col, j;
unsigned int startBlockNum , blockCount ,pageCount;
col = destAddr & 0x7FF ;
pageCount = size/2048 + ( (col)? 1 : 0 ) ;
startBlockNum = dnand_addr2BlockAddr( destAddr ) ;
blockCount = ( dnand_addr2RowAddr( destAddr ) + pageCount ) >> 6 ;
if ( ( dnand_addr2RowAddr( destAddr ) + pageCount ) & 0x3F ){
blockCount++;
}
if ( fnand_eraseBlocks( startBlockNum , blockCount ) ){
return 1;
}
dnand_enable();
for( j = 0 ; j < size ; ){
//发出read命令
dnand_writeCmd( 0x80 );
dnand_writeAddr( destAddr );
//开始写一页数据到destAddr里
for( ; (col < 2048)&&(j < size) ; col++ ){
dnand_writeData( *sourAddr++ );
destAddr++;
j++;
}
col = 0;
dnand_writeCmd( 0x10 );
dnand_waitReady();
//发送读取状态命令
dnand_writeCmd( 0x70 );
if ( ( dnand_readData() & 0x01 ) ){
return 1;
}
}
dnand_disable();
return 0;
}
uart0.c
#include "Uart0.h"
#include "s3c2440.h"
/*
****************************************
初始化uart0中断的配置
****************************************
*/
void uart0_init(void)
{
GPHCON |= 0xa0;
GPHUP |= 0x0f;
ULCON0 = 0x03; //普通模式,禁止奇偶校验,1个结束位,8-bit字长
UBRDIV0 = UBRDIV0_VAL; //波特率选择115200,所以UBRDIV0=54
UCON0 = 0x005; //时钟源=PCLK
//Rx,Tx水平触发,禁止接收超时中断,禁止接收错误状态中断,
//不使用回路模式,不发送break信号
//中断方式发送接收数据到缓冲寄存器
}
/*
*****************************************************
*uart0发送消息
***************************************************
*/
void uart0_sent_msg(char *string)
{
do {
while( !(UTRSTAT0&TXD0_READY) );
UTXH0 = *string++;
}while(*string != '




