代码进行模块化编程
启动文件
start.S
.text
.global _start
_start:
b Reset
@ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中断模式的向量地址
b HandleIRQ
@ 0x1c: 快中断模式的向量地址
HandleFIQ:
b HandleFIQ
Reset:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
* 然后CPU工作于新的频率FCLK
*/
/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */
bl uart0_init
mrs r0, cpsr /* 读出cpsr */
bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */
bic r0, r0, #(1<<7) /* 清除I位, 使能中断 */
msr cpsr, r0
ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数
halt_loop:
b halt_loop
HandleIRQ :
ldr sp,=4096
sub lr ,lr,#4 /*计算返回地址*/
stmdb sp!, { r0-r12,lr } /*保存使用的寄存器*/
ldr lr ,=return_halt
ldr pc ,=handle_irq_c /*调用中断服务程序*/
return_halt:
ldmia sp!, { r0-r12,pc }^ /*^表示将spsr的值复制到cpsr */
makefile
objs:=start.o uart.o interrupt.o init.o main.o
key.bin:$(objs)
arm-linux-ld -Ttext 0x00000000 -o key_elf $^
arm-linux-objcopy -O binary -S key_elf $@
arm-linux-objdump -D -m arm key_elf>key.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
.PHONY:clean
clean:
rm -f timer_elf key.bin key.dis *.o
外部中断初始化代码
init.c
#include "s3c2440_soc.h"#include"s3c24xx.h"
#define GPF4_out (1<<(4*2))
#define GPF5_out (1<<(5*2))
#define GPF6_out (1<<(6*2))
void led_init()
{
GPFCON&=~(GPF4_out| GPF5_out|GPF6_out);
GPFCON|=(GPF4_out| GPF5_out|GPF6_out);
}
void init_irq () /*中断的初始化*/
{
/*将引脚设置为输出模式*/
GPFCON&=~(3<<0|3<<4);
GPFCON|=(2<<0|2<<4);
GPGCON&=~(3<<6);
GPGCON|=(2<<6);
// 对于EINT11,需要在EINTMASK寄存器中使能它
EINTMASK &= ~(1<<11);
//设定优先级
PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;
//进行使能
INTMSK&=~(1<<0|1<<2|1<<5);
}
串口初始化
uart.c
#include "s3c2440_soc.h"
/* 115200,8n1 */
void uart0_init()
{
/* 设置引脚用于串口 */
/* GPH2,3用于TxD0, RxD0 */
GPHCON &= ~((3<<4) | (3<<6));
GPHCON |= ((2<<4) | (2<<6));
GPHUP &= ~((1<<2) | (1<<3)); /* 使能内部上拉 */
/* 设置波特率 */
/* UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
* UART clock = 50M
* UBRDIVn = (int)( 50000000 / ( 115200 x 16) ) –1 = 26
*/
UCON0 = 0x00000005; /* PCLK,中断/查询模式 */
UBRDIV0 = 26;
/* 设置数据格式 */
ULCON0 = 0x00000003; /* 8n1: 8个数据位, 无较验位, 1个停止位 */
/* */
}
int putchar(int c)
{
/* UTRSTAT0 */
/* UTXH0 */
while (!(UTRSTAT0 & (1<<2)));
UTXH0 = (unsigned char)c;
}
int getchar(void)
{
while (!(UTRSTAT0 & (1<<0)));
return URXH0;
}
int puts(const char *s)
{
while (*s)
{
putchar(*s);
s++;
}
}
中断代码
interrupt.c
#include "s3c2440_soc.h"
#include "uart.h"
void handle_irq_c()
{
unsigned long oft = INTOFFSET;
//判断的中断源
switch( oft)
{
case 0:
{
GPFDAT |= (0x7<<4); // 所有LED熄灭
GPFDAT &= ~(1<<4); // LED1点亮
puts("hello world\n\r");
break;
}
case 2:
{
GPFDAT |= (0x7<<4); // 所有LED熄灭
GPFDAT &= ~(1<<5); // LED2点亮
break;
}
case 5:
{
GPFDAT |= (0x7<<4); // 所有LED熄灭
GPFDAT &= ~(1<<6); // LED4点亮
break;
}
default: break;
}
//清中断
if( oft == 5 )
EINTPEND = (1<<11); // EINT8_23合用IRQ5
SRCPND=1<<oft;
INTPND=1<<oft;
}
主函数
#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"
int main(void)
{
led_init();
init_irq ();
while(1);
return 0;
}