1:源头
2:例程实现的功能
初始化LPC2220芯片:
1,实现LPC2220中断向量表。
2,设置ARM芯片各个模式运行时所需的堆栈空间。本例程中将系统模式的堆栈设置在LPC2220内部RAM的最顶端,即0x40004000。这里是因为芯片默认的运行模式就是系
统模式,也即我们的用户程序都是在系统模式下运行的。又因为ARM芯片默认是从高地址向低地址使用堆栈,因此将LPC2220内部RAM的最顶端设置为系统模式下的堆栈
指针。其他模式的堆栈设置在0x400000000开始的内部RAM中。具体怎么实现的,我们下面例程代码中会讲解。
3,初始化目标板,主要设置各种总线时钟、向量表映射、存储BANK设置、存储器加速模块、实时时钟等系统控制模块。
4:将RW段从NorFlash COPY到SDram中,清零ZI段。
5:跳转到main函数执行。
6:使用标准库并实现printf,用于调试。
7:利用printf打印全局变量,局部变量内容,地址。用来说明全局变量,局部变量以及链接器链接等相关知识。
3:过程中遇到的困难
第一个是我想使用LPC2220的外部BANK1,我使用的硬件板,BANK1上接的是RAM,一开始程序怎么也调试不过,以为是链接脚本书写错误。但后来发现是对LPC2220的引脚功能设置的问题。这不是什么知识点,但让我在这上面浪费了不少时间,特此记录一下,以安慰我那颗受伤的心。所以,记得一些硬件资源要先看datasheet初始化好后再使用。
第二个是编译代码的时候,出现一堆的undefinedreference to `__umoddi3'之类的错误,而且我还发现了只要我代码里有取整取余等操作时就会报上述错误,真是百思不得其解,后来到网上去查资料,得出这个错误确实是因为使用了除法导致的。而且,ARM7不支持除法指令,需要软件进行辅助除法运算,而一般是通过标准库的形式提供。我们使用ADS1.2,keil等集成IDE开发程序时,它们自带的库里有对除法的支持。而我现在使用的编译链是从网上下载的别人编译好的arm-linux-gcc3.4.1,可能不支持除法、软浮点支持。我不知道怎么解决这个问题,所以,干脆我使用了我自己使用源码编译的交叉编译链(4.6.0版本的)。我编译交叉连的时候选择了除法、软浮点支持。问题得以解决。具体交叉编译链的制作,参考:制作S3C6410的交叉编译链(arm-linux-gcc4.6.0)。
第三个问题是编译的时候提示undefinedreference to `__exidx_start' undefined reference to`__exidx_end'等错误,而且是当我使用sprintf等相关格式化字符串的时候就提示这些错误,我没有找到合适的解决方案。所以,我参考网络资源自己实现了简单的格式化函数。这不是根本解决之道,我对使用gnu开发工具链进行嵌入式开发如何使用标准库函数也存在很多疑点。
4:部分例程代码
@******************************************************************************
@ 文件名 :startup.s
@ 功 能:初始化LPC2220:初始化各种运行模式的堆栈空间,各种exception
@ 入口。
@ 说明 :此工程为gcc for arm的工程,书写此工程启动代码目的是学习嵌入式开发过程中
@ 的一些要点知识。gcc for arm 是开源的编译,链接器,可以学习程序开发的更多
@ 细节。其他商业开发工具隐藏太多细节,不利于其初学者学习。
@ 备注 :大家可以根据工程需要修改其内容。
@ 作者 :张连聘
@ 创建时间:2014-07-27
@******************************************************************************
@define the stack size for each mode
@定义各种运行模式堆栈大小
.equ FIQ_STACK_LEGTH ,256
.equ IRQ_STACK_LEGTH ,9*8
.equ ABT_STACK_LEGTH ,256
.equ UND_STACK_LEGTH ,256
.equ NoInt ,0x80
.equ NoFIQ ,0x40
.equ USR32Mode ,0x10
.equ SVC32Mode ,0x13
.equ SYS32Mode ,0x1f
.equ IRQ32Mode ,0x12
.equ FIQ32Mode ,0x11
.equ PINSEL2 ,0xE002C014
.equ BCFG0 ,0xFFE00000
.equ BCFG1 ,0xFFE00004
.equ BCFG2 ,0xFFE00008
.equ BCFG3 ,0xFFE0000C
@The imported labels
@引入的外部标号在这声明
.extern FIQ_Exception @Fast interrupt exceptions handler 快速中断异常处理程序
.extern main @The entry point to the main function C语言主程序入口
.extern TargetResetInit @initialize the target board 目标板基本初始化
.extern SoftwareInterrupt
.extern Copydata
.extern ClearBssData
.global Reset
.text
@interrupt vectors
@中断向量表
Reset:
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
.word 0xb9205f80
LDR PC, [PC, #-0xff0]
LDR PC, FIQ_Addr
ResetAddr:
.word ResetInit
UndefinedAddr:
.word Undefined
SWI_Addr:
.word SoftwareInterrupt
PrefetchAddr:
.word PrefetchAbort
DataAbortAddr:
.word DataAbort
Nouse: .word 0
IRQ_Addr:
.word 0
FIQ_Addr:
.word FIQ_Handler
@未定义指令
Undefined:
B Undefined
@取指令中止
PrefetchAbort:
B PrefetchAbort
@取数据中止
DataAbort:
B DataAbort
@快速中断
FIQ_Handler:
STMFD SP!, {R0-R3, LR}
LDR PC, =FIQ_Exception
LDMFD SP!, {R0-R3, LR}
SUBS PC, LR, #4
/*********************************************************************************************************
**函数名称: InitStack
**功能描述: Initialize the stacks 初始化堆栈
**输 入: None
**输 出 : None
**全局变量: None
**调用模块: None
********************************************************************************************************/
InitStack:
MOV R0, LR
@Build the SVC stack
@设置中断模式堆栈
MSR CPSR_c, #0xd2
LDR SP, StackIrq
@Build the FIQ stack
@设置快速中断模式堆栈
MSR CPSR_c, #0xd1
LDR SP, StackFiq
@Build the DATAABORT stack
@设置中止模式堆栈
MSR CPSR_c, #0xd7
LDR SP, StackAbt
@Build the UDF stack
@设置未定义模式堆栈
MSR CPSR_c, #0xdb
LDR SP, StackUnd
@Build the SYS stack
@设置系统模式堆栈
MSR CPSR_c, #0xdf
LDR SP, =StackUsr
BX R0
/*********************************************************************************************************
**函数名称: ResetInit
**功能描述: RESET 复位入口
**输 入: None
**输 出 : None
**全局变量: None
**调用模块: None
**-------------------------------------------------------------------------------------------------------
********************************************************************************************************/
ResetInit:
@Initial the extenal bus controller
@初始化外部总线控制器,根据目标板决定配置
LDR R0, =PINSEL2
LDR R1, =0x0f814914
STR R1, [R0]
LDR R1, =0x0f814914
LDR R0, =BCFG0
LDR R1, =0x1000ffef
STR R1, [R0]
LDR R0, =BCFG1
LDR R1, =0x1000ffef
STR R1, [R0]
LDR R0, =BCFG2
LDR R1, =0x0000fbef
STR R1, [R0]
LDR R0, =BCFG3
LDR R1, =0x10001460
STR R1, [R0]
BL InitStack @ Initialize the stack 初始化堆栈
BL TargetResetInit @ Initialize the target board 目标板基本初始化
BL Copydata
BL ClearBssData
B main @ Jump to the entry point of C program 跳转到c语言入口
StackIrq:
.word IrqStackSpace +(IRQ_STACK_LEGTH - 1)*4
StackFiq:
.word FiqStackSpace +(FIQ_STACK_LEGTH - 1)*4
StackAbt: .word AbtStackSpace +(ABT_STACK_LEGTH - 1)*4
StackUnd: .word UndtStackSpace +(UND_STACK_LEGTH - 1)*4
/* 分配堆栈空间 */
.bss
.align 4
IrqStackSpace:
.space IRQ_STACK_LEGTH * 4 @Stack spaces for Interrupt ReQuest Mode 中断模式堆栈空间
FiqStackSpace:
.space FIQ_STACK_LEGTH * 4 @Stack spaces for Fast Interrupt reQuest Mode 快速中断模式堆栈空间
AbtStackSpace:
.space ABT_STACK_LEGTH * 4 @Stack spaces for Suspend Mode 中止义模式堆栈空间
UndtStackSpace:
.space UND_STACK_LEGTH * 4 @Stack spaces for Undefined Mode 未定义模式堆栈
.end
main.c
/******************************************************************************
* 文件名 :main.c
* 功 能:初始化系统后,利用P2.28控制led灯闪烁
*
* 作者 :张连聘
* 创建时间:2014-07-27
*******************************************************************************/
#include "LPC2220.h"
#include "uart0.h"
#include "print.h"
#define LEDCON (1<<28)
void Delay(int ms);
void myprintf(char *fmt,...);
int globalvalue = 8;
int main(void)
{
int localvalue =88;
IO2DIR = LEDCON; //配置P2.28为输出口
PINSEL0 = 0x00000005; // 设置I/O连接到UART0
UART0_Init();
while (1)
{
myprintf("the globalvalue is %d\r\n",globalvalue);
myprintf("the address of globalvalue is 0X%x\r\n",&globalvalue);
myprintf("the localvalue is %d\r\n",localvalue);
myprintf("the address of localvalue is 0X%x\r\n",&localvalue);
myprintf("\n\n\n\n");
Delay(50);
IO2SET = LEDCON; //点亮LED灯
Delay(10);
IO2CLR = LEDCON; //熄灭LED灯
Delay(10);
//UART0_SendStr((unsigned char const *)str);
}
return 0;
}
/******************************************************************************
* 名 称:Delay
* 功 能:软件延时
* 入口参数:ms
* 出口参数:无
******************************************************************************/
void Delay(int ms)
{
int i,j;
for(i=0;i<5000;i++)
for(j=0;j<ms;j++)
;
}
void myprintf(char *fmt,...)
{
va_list ap;
char string[256];
va_start(ap,fmt);
myvsprintf(string,fmt,ap);
UART0_SendStr(string);
va_end(ap);
}
void FIQ_Exception(void)
{
}
link_script.lds
/*链接脚本文件。*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(Reset)
MEMORY
{
rom (rx) : ORIGIN = 0x80000000, LENGTH = 2M
ram (!rx) : ORIGIN = 0x81000000, LENGTH = 16M
stack (!rx) : ORIGIN = 0x40000000, LENGTH = 64K
}
SECTIONS
{
. = 0x80000000 ;
.text :
{
startup.o(.text)
*(.text)
_end_text = .;
} >rom
.rodata :
{
*(.rodata)
} >rom
. = 0x40000000;
.MYSTACK :
{
_datastack_start = . ;
startup.o(.bss)
} >stack
. = 0x40004000 ;
StackUsr = . ;
_datastack_end = . ;
. = 0x81000000 ;
.data :
AT (ADDR(.rodata)+SIZEOF(.rodata))
{
_data_src = ADDR(.rodata)+SIZEOF(.rodata);
_start_data = . ;
*(.data)
_end_data = . ;
} >ram
.bss :
AT (ADDR(.data)+SIZEOF(.data))
{
_bss_start = . ;
*(.bss)
_bss_end = . ;
} >ram
}
Makefile
CC=arm-linux-gcc
LD=arm-linux-ld
CFLAGS := -Wall -c -g -mcpu=arm7tdmi
OBJCOPY=arm-linux-objcopy
OBJDUMP=arm-s3c6410-linux-gnueabi-objcopy
OBJECTS :=startup.o target.o uart0.o print.o main.o
control_led.bin:control_led_elf
$(OBJDUMP) -O binary -S $^ $@
control_led_elf:$(OBJECTS)
$(LD) -Bstatic -Tlink_script.lds $^ -Map control_led_elf.map -o $@ \
-L/usr/local/S3C6410/arm-s3c6410-linux-gnueabi/lib/gcc/arm-s3c6410-linux-gnueabi/4.6.0 \
-L/usr/local/S3C6410/arm-s3c6410-linux-gnueabi/lib \
-L/usr/local/S3C6410/arm-s3c6410-linux-gnueabi/arm-s3c6410-linux-gnueabi/sysroot/usr/lib \
--start-group -lgcc -lgcc_eh -lgcov -lc --end-group
%.o:%.s
$(CC) $(CFLAGS) -o $@ $<
%.o:%.c
$(CC) $(CFLAGS) -o $@ $<
.PHONY : clean
clean:
rm -f control_led.bin control_led_elf *.o
5:重点知识概要
AT (ADDR(.rodata)+SIZEOF(.rodata))
{
_data_src = ADDR(.rodata)+SIZEOF(.rodata);
_start_data = . ;
*(.data)
_end_data = . ;
} >ram
{
extern char _data_src,_start_data,_end_data;
char * data_src=&_data_src;
char * data_des=&_start_data;
char * data_end=&_end_data;
int len =data_end - data_des;
while(len>=0)
{
*data_des++= *data_src++;
len --;
}
int main(void)
{
int localvalue =88;
IO2DIR = LEDCON; //配置P2.28为输出口
PINSEL0 = 0x00000005; // 设置I/O连接到UART0
UART0_Init();
while (1)
{
myprintf("the globalvalue is %d\r\n",globalvalue);
myprintf("the address of globalvalue is 0X%x\r\n",&globalvalue);
myprintf("the localvalue is %d\r\n",localvalue);
myprintf("the address of localvalue is 0X%x\r\n",&localvalue);
myprintf("\n\n\n\n");
the address of globalvalue is 0X81000000
the localvalue is 88
the address of localvalue is 0X40003ff4
6:未实现的功能
7:总结
8:附录
本例程中源码下载: