【原创】ARM入门的开始---2008.2

       原文地址:

http://www.ouravr.com/bbs/bbs_content.jspbbs_sn=916955&bbs_page_no=1&search_mode=3&search_text=lynnlase&bbs_id=9999

到目前为止,断断续续的搞了阵ARM,毕竟自己买的板不能就这样浪费了,而个人的理念是一定要摆脱单片机,毕竟谁都会搞点单片机,如果你是软件工程师,而你仅仅停留在单片机的话,我想绝对不够的,其实我算是很菜的,只是比较喜欢自学,ARM也是,觉得目前手持数码设备的繁荣离不开ARM,这点足以使很多向我这样的要转型了。o(∩_∩)o...,
可能很多人买来开发板不会用,我也是一样啊,呵呵,所以在这里先声明一下,我只是刚刚会,也仅仅是当单片机用。希望还没有整ARM的兄弟能有帮助,而高手请指点我的错误,觉得ARM区比较冷,而ARM这么热?因为我公司只有我一个写程序的,真的希望有高手指点我,让小弟少走些弯路。谢谢先。。。。
步入正题。。。。
全部文档程序:
点击此处下载ourdev_214378.rar(文件大小:196K)

ARM是目前手持设备上用得最广泛的处理器了,其出色的性能低廉的价格占据了该市场的大半壁江山。用智能机的会更清楚。那些
系统就是建立在ARM上。
我们抛开系统,如何让其简单的做单片机的事,如跑马灯。。。。
这就是我要说的BOOT.
当你买了个板(S3C44B0X)的板来了之后,用了光盘资料的程序跑了跑,里面基本就是跑马灯啊,定时啊,等的小程序,小是小啊,但是,在AVR,51等单片机中不是这样的哦,为什麽有一大串的汇编呢?
这个汇编程序就是ARM的初始程序,类似于电脑的BIOS。
下面讲讲BOOT.
建议用AXD一步一步试着理解。
1.上电后从0X00开始,这个都是一样的。
AREA Init,CODE,READONLY

ENTRY
0x00 b ResetHandler ;for debug
0x04 b HandlerUndef ;handlerUndef
0x08 b HandlerSWI ;SWI interrupt handler
0x0c b HandlerPabort ;handlerPAbort
0x10 b HandlerDabort ;handlerDAbort
0x14 b . ;handlerReserved
0x18 b HandlerIRQ
0x1c b HandlerFIQ

上电即执行b ResetHandler,即跳到初始化程序
2.初始化程序
这里对很多内容进行初始化。
ResetHandler
ldr r0,=WTCON ;禁止看门狗
ldr r1,=0x0
str r1,[r0]

ldr r0,=INTMSK
ldr r1,=0x07ffffff ;禁止所有中断
str r1,[r0]

;以下三段设置时钟控制寄存器,设置输出时钟,输入10M,输出可以自己配置,当然后面在C中可以再次配置。

ldr r0,=LOCKTIME
ldr r1,=0xfff
str r1,[r0]

[ PLLONSTART
ldr r0,=PLLCON ;temporary setting of PLL
ldr r1,=((M_DIV<<12)+(P_DIV<<4)+S_DIV)
str r1,[r0]
]

ldr r0,=CLKCON
ldr r1,=0x7ff8 ;All unit block CLK enable
str r1,[r0]

;****************************************************
;change BDMACON reset value for BDMA *
;****************************************************

ldr r0,=BDIDES0
ldr r1,=0x40000000 ;BDIDESn reset value should be 0x40000000
str r1,[r0]

ldr r0,=BDIDES1
ldr r1,=0x40000000 ;BDIDESn reset value should be 0x40000000
str r1,[r0]

;****************************************************
;设定存储器控制寄存器 13个寄存器内容全部定义在后面 SMRDATA中 *
;****************************************************
ldr r0,=SMRDATA
ldmia r0,{r1-r13}
ldr r0,=0x01c80000 ;BWSCON Address
stmia r0,{r1-r13}

;****************************************************
;初始化堆栈 对每个模式下的堆栈都进行初始化,空间也在后面申请了
切记这个_ISR_STARTADDRESS 的重要性,你的堆栈,你的中断指针全以他为标准 *
;****************************************************
ldr sp, =SVCStack ;复位后位SVC模式
bl InitStacks

;****************************************************
;设置中断处理 对你的非向量中断赋函数入口地址 *
;****************************************************
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]

;****************************************************
;把RW复制到DRAM中,同时ZI段清零 *
;****************************************************
LDR r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
LDR r1, =|Image$$RW$$Base| ; and RAM copy
LDR r3, =|Image$$ZI$$Base|
;Zero init base => top of initialised data

CMP r0, r1 ; Check that they are different
BEQ %F1
0
CMP r1, r3 ; Copy init data
LDRCC r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4
STRCC r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4
BCC %B0
1
LDR r1, =|Image$$ZI$$Limit| ; Top of zero init segment
MOV r2, #0
2
CMP r3, r1 ;Zero init
STRCC r2, [r3], #4
BCC %B2
;;完成上面的初始化即进来MAIN函数,这里处理是T还是ARM。前面已经判断
[ :LNOT:THUMBCODE
bl Main ;Don't use main() because ......
b .
]

[ THUMBCODE ;for start-up code for Thumb mode
orr lr,pc,#1
bx lr
CODE16
bl Main ;Don't use main() because ......
b .
CODE32
]

3.进入主函数
下面即进入主函数MAIN,上面的整个过程只是初始化,而初始化完后(公版的已经把相关设置设好,基本你用了这个BOOT,下面就可以进入和AVR一样的C了,当然注意这里是32BIT,同时注意他的寄存器。I/O等。
4.中断
boot中不单单是初始化,同时还要处理中断
ARM的中断处理有两种方式,向量中断,非向量中断。参考STUDY ARM STEP BY STEP.里面很详细了。
向量中断由硬件跳到对应地址,第一个为0X20-----0XE0.这里都是硬件控制跳转。
;中断向量表
VECTOR_BRANCH
ldr pc,=HandlerEINT0 ;mGA 0x20
ldr pc,=HandlerEINT1 ;
ldr pc,=HandlerEINT2 ;
ldr pc,=HandlerEINT3 ;
ldr pc,=HandlerEINT4567 ;
ldr pc,=HandlerTICK ;mGA 0x34
b .
b .
ldr pc,=HandlerZDMA0 ;mGB 0x40
ldr pc,=HandlerZDMA1 ;
ldr pc,=HandlerBDMA0 ;
ldr pc,=HandlerBDMA1 ;
ldr pc,=HandlerWDT ;
ldr pc,=HandlerUERR01 ;mGB 0x54
b .
b .
ldr pc,=HandlerTIMER0 ;mGC 0x60
ldr pc,=HandlerTIMER1 ;
ldr pc,=HandlerTIMER2 ;
ldr pc,=HandlerTIMER3 ;
ldr pc,=HandlerTIMER4 ;
ldr pc,=HandlerTIMER5 ;mGC 0x74
b .
b .
ldr pc,=HandlerURXD0 ;mGD 0x80
ldr pc,=HandlerURXD1 ;
ldr pc,=HandlerIIC ;
ldr pc,=HandlerSIO ;
ldr pc,=HandlerUTXD0 ;
ldr pc,=HandlerUTXD1 ;mGD 0x94
b .
b .
ldr pc,=HandlerRTC ;mGKA 0xa0
b .
b .
b .
b .
b . ;mGKA
b .
b .
ldr pc,=HandlerADC ;mGKB 0xc0
b . ;
b . ;
b . ;
b . ;
b . ;mGKB
b .
b .
ldr pc,=EnterPWDN ;0xe0=EnterPWDN


你的工作就是编辑对应处理函数(FIQ不支持向量中断模式)。而非向量中断不会这样,当你选择非向量中断时,全部跳到0X18处执行,而前面我们初始化时已经给他一个中断函数处理入口。这样他会进入
IsrIRQ ;using I_ISPR register.
sub sp,sp,#4 ;预留返回指针的存储位置
stmfd sp!,{r8-r9}

ldr r9,=I_ISPR
ldr r9,[r9] ;载入I_ISPR

cmp r9, #0x0 ;If the IDLE mode work-around is used,r9 may be 0 sometimes.
beq %F2 ;无可处理中断,返回

mov r8,#0x0 ;r8为偏移量,清零

0
movs r9,r9,lsr #1 ;从右向左逐位检验
bcs %F1
add r8,r8,#4 ;偏移量累加
b %B0

1
ldr r9,=HandleADC ;中断处理表的首址
add r9,r9,r8 ;计算中断处理表的入口地址 r9+r8,即装载中断处理函数的指针
ldr r9,[r9] ;装载中断处理函数的地址
str r9,[sp,#8] ;将中断处理函数的地址存入刚才预留的位置,r8和r9的上面
ldmfd sp!,{r8-r9,pc} ;出栈后,pc指向的既是中断处理函数的地址

2
ldmfd sp!,{r8-r9} ;恢复r8,r9
add sp,sp,#4 ;恢复栈指针
subs pc,lr,#4 ;返回


通过硬件记录中断I_ISPR寄存器就可以判断哪个中断,直接进入中断函数。
相比之下向量中断省时间。
看看几个相应寄存器:
INTCON: 默认值为0X07
[2]: 该位决定中断处理模式,向量还是非向量,当IRQ时才可以选择向量中断,1=非向量。0=向量
[1]: 该位开启IRQ中断允许,当你对某个中断源选择IRQ中断,则一定要开IRQ中断允许。1为关闭,0为开启
[0]: 该位开启FIQ中断允许,当你对某个中断源选择FIQ中断,则一定要开FIQ中断允许。1为关闭,0为开启
INTPND: 默认为0X000
这个寄存器为中断请求寄存器,当你有中断行为时,这里对应中断请求,1=有中断请求
INTMOD: 默认为0
这个寄存器对应某个中断使用IRQ,还是FIQ中断,0=IRQ,1=FIQ;
INTMSK: 默认为0X07FFFFFF 即关闭全部中断,对应位为0即开启中断允许。
其他请参阅S3C44B0X的DATASHEET.
要弄懂上面的内容,建议把ARM体系结构那本书看下。起码汇编指令要懂。
还有一个就是存储结构。

讲了这么多,这都是关于BOOT的功能,那么如何真正写个可以运行的程序呢。
下面看看我写的个EINT4567的中断处理程序。
#include "inc\44b0.h"


#define EXT_OSC_CLK 10000000
void __irq lynn(void);
void delay(unsigned int dwUs);

#define led0 (1<<1)
#define led1 (1<<2)
#define led2 (1<<3)
#define led_off 0x0e
volatile unsigned int which_int=0;

unsigned int g_dwMCLK = 20000000; //初始值20MHz




void __irq lynn(void)
{
which_int=rEXTINTPND;
delay(100000);
if(which_int==rEXTINTPND)
{
switch(which_int)
{
case 1:
rPDATC&=!led_off;
rPDATC|=led0;
break;
case 2:
rPDATC&=!led_off;
rPDATC|=led1;
break;
case 4:
rPDATC&=!led_off;
rPDATC|=led2;
break;
case 8:
rPDATC&=!led_off;
break;
default:
break;
}
}
delay(100000);
rEXTINTPND=0XF; //作用是清楚中断标志
rI_ISPC=BIT_EINT4567; //清楚中断申请位,很重要
}



void delay(unsigned int dwUs)
{
unsigned int dwCnt;
dwCnt = dwUs * (g_dwMCLK / 4000000);
while(dwCnt--);
}


int sysUtilsSetPllValue (int nMDiv, int nPDiv, int nSDiv)
{
int i = 1;

rPLLCON = (nMDiv<<12)|(nPDiv<<4)|nSDiv;

while(nSDiv--)
i *= 2;

g_dwMCLK = (EXT_OSC_CLK*(nMDiv+8))/((nPDiv+2)*i);

return 0;
}




void Main()
{
sysUtilsSetPllValue (24, 6, 1); //在这里我们把时钟设置为20M;(24+8)*10m/(6+2)*2**1=20m

rPDATC = 0x0000; //PC口全部置低电平
rPCONC = 0x5f555555; //PC口除了串口外全部输出,因为存储系统全为16BIT。
rPUPC = 0x3000; //除了串口不置上拉,其余全部上拉。



rPCONG |= 0xff00; //引脚定义为外部中断引脚,选择引脚功能。
rPUPG=0; //PG的上拉电阻默认也是开启的即为0。

rINTMOD &= ~(1<<21); //外部中断4567为irq模式,如果为FIQ则INTCON要对应打开FIQ中断允许。
rINTCON &= ~(1<<1); //打开IRQ中断允许,默认是关闭的。同时选择非向量中断模式。


rEXTINT = 0x33330000; //选择触发方式,我们选择下降沿触发。
rINTPND=0; //这个其实不用管他,但注意中断程序里要对其清零。

pISR_EINT4567=(int)lynn; //这里就是给中断函数入口地址了,注意对应相对中断。

rINTMSK=~(BIT_GLOBAL|BIT_EINT4567); //开启全局中断及外部中断4567.


for(;;);

}


就是处理一个在开发板上按键处理的中断程序,当然如果你会了这个跑马灯那就不用说了,o(∩_∩)o...
最后ADS的设置了。
上面仅仅是程序,那么如何编译,要怎么样设置呢。
请看图,关键在于加了BOOT后把其放在入口处。看看LAYOUT项,至于他的名字完全取决你的BOOT程序的文件名。

1

2

3


如果没有什么问题了,那么你基本可以和我一样把ARM当单片机用了,o(∩_∩)o...。我相信哪天我不会这么说了,你也是。
希望对和我一样入门时有些迷茫的人一些帮助,我坚信你和我一样,我们都可以在自己的努力下征服ARM。我们携手共进吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值