一、
1 @****************************************************************************** 2 @ File:head.S 3 @ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数 4 @****************************************************************************** 5 .extern main 6 .text 7 .global _start 8 _start: 9 10 @***************************************** 11 HandleDataAbort: 12 b HandleDataAbort 13 @************* 14 @ 0x14: 保留 @ 15 @************* 16 HandleNotUsed: 17 b HandleNotUsed 18 @*************************** 19 @ 0x18: 中断模式的向量地址 @ 20 @*************************** 21 b HandleIRQ 22 @***************************** 23 @ 0x1c: 快中断模式的向量地址 @ 24 @***************************** 25 HandleFIQ: 26 b HandleFIQ 27 @************************************************************************************************************************************************************************************* 28 @ 子程序部分,复位和中断 @ 29 @************************************************************************************************************************************************************************************* 30 @************* 31 @ 复位子程序 @ 32 @************* 33 Reset: 34 ldr sp, =4096 @ 下面都是C语言,要先设置栈指针;这些代码直接在stepping中运行,所以先设置栈指针指向4K 35 bl disable_watch_dog @ 关看门狗 36 37 msr cpsr_c, #0xd2 @ MSR: 通用寄存器到状态寄存器的传送指令。 38 @ 指令格式:MSR{<cond>} CPSR_<fields>, #<immediate> ; MSR{<cond>} CPSR_<fields>, <Rm> ; 39 @ <cond>为指令执行的条件码,当<cond>忽略时指令为无条件执行;<fields>设置状态寄存器中需要操作的位。状态寄存器的32位可以分为4个8位的域: f: 指示bits[31 : 24],又名条件标志位域, 40 @ s: 指示bits[23 : 16],又名状态标志位域, x: 指示bits[15 : 8], 又名扩展位域,c: 指示bits[7 : 0],又名控制位;后面就是立即数,或者<Rm>寄存器包含将要传送到状态寄存器中的数据。 41 @ MRS: 状态寄存器到通用寄存器的传送指令。 42 @ 指令格式:MRS{<cond>}<Rd>, CPSR;<cond>为指令执行的条件码,当<cond>忽略时指令为无条件执行;<Rd>为目标寄存器。 43 @ 在这里设置状态寄存器CPSR,选择工作模式为中断模式 44 @ 状态寄存器CPST各位:N[31]结果是否为负数,Z[30]结果是否为0,C[29]进位/借位/移位溢出,V[28]溢出标志,[27:8]保留,I[7]中断禁止位,F[6]快速中断禁止位,T[5]CPU状态位,M4-M0[4:0]选择工作模式 45 @ M4-M0[4:0]工作模式选择:10000用户模式,10001快中断模式,10010中断模式,10011管理模式,11011未定义指令中止模式,11111系统模式 46 @ 在这里选择中断模式(IRQ),I、F均置位 47 ldr sp, =3072 @ 设置中断模式栈指针,应该是取一段就行,取的3K 48 49 msr cpsr_c, #0xd3 @ 进入管理模式0011 50 ldr sp, =4096 @ 设置系统模式栈指针,其实复位之后,CPU就处于管理模式,前面的“ldr sp, =4096”完成同样的功能,此句可省略 51 @ SPSR程序状态保存寄存器,当切换进入这些工作模式时,SPSR中保存前一个工作模式的CPSR值,这样,当返回前一个工作模式时,可以将SPSR的值回复到CPSR中。 52 53 54 bl init_led @ 初始化LED的GPIO管脚,在文件init.c中,设置三个LED对应的引脚为输出 55 bl init_irq @ 调用中断初始化函数,在init.c中,用来设置中断的初始化,包括:1、设置开关对应的引脚状态为中断2、对于外部中断EINT11需要设置寄存器使能3、设置优先级4、三个均使能设置 56 msr cpsr_c, #0x5f @ 设置I[7]=0,I[7]为中断禁止位,设置1为禁止中断模式IRQ,设置0则开启中断模式IRQ 57 58 ldr lr, =halt_loop @ 设置返回地址;lr也就是r14连接寄存器,用来保存子程序的返回地址;在汇编中调用c函数时,如果C函数中有返回值,那么调用前就一定要先配置lr的值,也就是执行完C函数之后pc指针指向的地址; 59 @ ldr lr, =halt_loop,这里的意思就是说,执行C函数之前,先把死循环那里的地址放到lr中,执行完C之后,PC指针就指向了Lr的值,也就死循环的地址,也就是说,执行完C之后,就接着运行死循环啦; 60 @ 当然,C函数中没有返回值时,也就不必先设置lr的值了,它没有返回,就自然执行完了,就执行下一条指令,当然也就没必要设置了 61 ldr pc, =main @ 也就相当于跳转到main函数,b指令和bl指令只能跳转32M的范围,所以这里使用向pc赋值的方法进行跳转;b和bl指令只能跳转32M的范围,因为寄存器是32位的,此时的值是24位有符号数,所以是32M 62 halt_loop: @死循环;死循环的作用:在最后加个死循环,也就是让程序停留在这里了,这样是很有用的,一方面可以在这个时候查看各个存储器的值,以便检查自己的程序是否正确,另一方面,比如如果是C函数比如用到printf 63 b halt_loop @这样之类的函数,如果不加死循环,输出会瞬间消失 64 @************* 65 @ 中断子程序 @ 66 @************* 67 HandleIRQ: 68 sub lr, lr, #4 @ 减法指令sub:SUB R0,R1,R2 R0=R1-R2,SUB R0,R1,#3 R0=R1-3;这里就是lr=lr-4;保存上一次执行的地址,为等会中断完成后返回地址作准备 69 stmdb sp!, { r0-r12,lr } @ 保存被中断程序的运行环境,即各个寄存器;sp是中断模式的栈(3072);r0-r12和lr保存在中断模式的栈中 70 71 ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址,在汇编中调用c函数时,如果C函数中有返回值,那么调用前就一定要先配置lr的值,也就是执行完C函数之后pc指针指向的地址; 72 ldr pc, =EINT_Handle @ 也就是跳转去执行中断处理的函数EINT_Handle;EINT_Handle函数在interrupt.c中,这就是中断的核心函数,根据按键的不同,去点亮不同的灯 73 int_return: 74 ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr 75 @ CPSR:程序状态寄存器,cpsr在用户级编程时用于存储条件码。CPSR包含条件码标志,中断禁止位,当前处理器模式以及其他状态和控制信息。 76 @ SPSR:程序状态保存寄存器。SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。
二、
1 /* 2 * init.c: 进行一些初始化 3 */ 4 /************************************************************************************************************************************************************************************ 5 函数:init.c 6 ************************************************************************************************************************************************************************************/ 7 初始部分 8 /******************/ 9 #include "s3c24xx.h" 10 11 /*LED1,LED2,LED4对应GPF4、GPF5、GPF6,低电平时灯亮*/ 12 #define GPF4_out (1<<(4*2)) //GPF4[9:8]=01 ,GPF4设置为输出 13 #define GPF5_out (1<<(5*2)) //GPF5[11:10]=01 ,GPF5设置为输出 14 #define GPF6_out (1<<(6*2)) //GPF6[13:12]=01 ,GPF6设置为输出 15 16 #define GPF4_msk (3<<(4*2)) //[9::8]=11 17 #define GPF5_msk (3<<(5*2)) //[11:10]=11 18 #define GPF6_msk (3<<(6*2)) //[13:12]=11 19 20 /* S2,S3,S4对应GPF0、GPF2、GPG3,开关按下时为低电平*/ 21 #define GPF0_eint (0x2<<(0*2)) //GPF[1:0]=10, GPF0设置为输出 22 #define GPF2_eint (0x2<<(2*2)) //GPF[5:4]=10, GPF2设置为输出 23 #define GPG3_eint (0x2<<(3*2)) //GPF[7:6]=10, GPF3设置为输出 24 25 #define GPF0_msk (3<<(0*2)) //[1:0]=11 26 #define GPF2_msk (3<<(2*2)) //[5:4]=11 27 #define GPG3_msk (3<<(3*2)) //[7:6]=11 28 29 /**************************************** 30 关闭WATCHDOG子程序:disable_watch_dog() 31 ****************************************/ 32 void disable_watch_dog(void) 33 { 34 WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可 35 } 36 /**************************************** 37 设置LED对应引脚为输出子函数:init_led() 38 ****************************************/ 39 void init_led(void) 40 { 41 /* LED1,LED2,LED4对应的3根引脚设为输出 */ 42 GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk); //设置输出口是[13:8]进行设置,首先,这些位全部取0; 43 GPFCON |= GPF4_out | GPF5_out | GPF6_out; //所需要设置的位,全部取0后跟对应的取1设置的相或就行 44 } 45 46 /* 47 * 初始化GPIO引脚为外部中断 48 * GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD) 49 */ 50 /*************************** 51 中断初始化子函数:init_irq( ) 52 53 ***************************/ 54 void init_irq() 55 { 56 /*S2,S3对应的2根引脚设为中断引脚 EINT0,ENT2*/ 57 GPFCON &= ~(GPF0_msk | GPF2_msk); //将需要设置的那些位清0 58 GPFCON |= GPF0_eint | GPF2_eint; //设置引脚为中断 59 60 // S4对应的引脚设为中断引脚EINT11 61 GPGCON &= ~GPG3_msk; 62 GPGCON |= GPG3_eint; 63 64 // 对于EINT11,需要在EINTMASK寄存器中使能它 65 EINTMASK &= ~(1<<11); //对于外部中断4-23,需要设置EINTMASK寄存器数字相同对应的位,0使能中断,1禁止中断,因此这里需要设置第11位为0 66 67 /* 设定优先级:ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2,仲裁器1、6无需设置,最终:EINT0 > EINT2 > EINT11即K2 > K3 > K4 */ 68 PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ; //S2对应中断引脚EINT0,S3对应中断引脚EINT2,S4对应EINT11,EINT0即REQ1,EINT2即REQ3,,对应中断优先级仲裁器ARBITER0,EINT11即为REQ1,对应中断优先级仲裁器ARBITER1; 69 //这里设置ARBITER0[8:7]=00,ARB_MODE0=0中断优先级不变; 所以优先级:REQ1>REQ3,即:S2>S3>S4 70 /* EINT0、EINT2、EINT8_23使能 */ 71 INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5)); //INTMSK是中断屏蔽寄存器,为1时屏蔽,0使能中断,EINT0[0]=0,EINT2[2]=0,EINT8_23[5]=0 72 }
三、
1 /************************************************************************************************************************************************************************************ 2 中断程序部分 3 ************************************************************************************************************************************************************************************/ 4 #include "s3c24xx.h" //调用头文件中定义的地址 5 6 /******************************* 7 中断处理函数:EINT_Handle() 8 *******************************/ 9 void EINT_Handle() 10 { 11 unsigned long oft = INTOFFSET; //中断偏移寄存器:INTOFFSET,中断偏移寄存器中的值表明了是哪个 IRQ 模式的中断请求在 INTPND 寄存器中。此位可以通过清除SRCPND和 INTPND 自动清除 12 unsigned long val; 13 /* 14 switch语法基础: 15 1、基本格式: switch(表达式) 16 { 17 case 常量表达式1: 18 语句1; 19 break; 20 21 case 常量表达式2: 22 语句2; 23 break; 24 25 …… 26 case 常量表达式n: 27 语句n; 28 break; 29 30 default: 31 语句n+1; 32 break; 33 } 34 2、各部分说明: 35 (1)关于表达式: 表达式可以是整型、字符型等表达式。 有一个确定的值(不是逻辑值). 36 (2)关于常量表达式1~n: 只起到一个标号的作用,根据表达式的值来判断, 找到一个相匹配的入口处,程序往下执行。各个case后的常量表达式的值必须互不相同. 37 (3)每个case分支可有多条语句,可不用花括号{ }。 38 (4)各个case 语句只是一个入口标号,并不确定执行的终止点. 39 (5)一般在各个case 语句最后应该加一break语句, 可使程序流程跳出Switch结构.否则会从入口处一直向下执行. 40 (6)各个case和default出现的先后次序,并不影响执行结果.default可以没有. 41 */ 42 switch( oft ) //oft即为中断偏移寄存器INTOFFSET的值,EINT0偏移量0,EINT2偏移量2,EINT8_23偏移量5,偏移量即偏移寄存器的值 43 { 44 // S2被按下 45 case 0: //偏移量为0,中断EINT0, 46 { 47 GPFDAT |= (0x7<<4); // 所有LED熄灭 48 GPFDAT &= ~(1<<4); // LED1点亮 49 break; 50 } 51 52 // S3被按下 53 case 2: // 偏移量为2,中断EINT2 54 { 55 GPFDAT |= (0x7<<4); // 所有LED熄灭 56 GPFDAT &= ~(1<<5); // LED2点亮 57 break; 58 } 59 60 // K4被按下 61 case 5: // 偏移量为5,中断EINT8_23 62 { 63 GPFDAT |= (0x7<<4); // 所有LED熄灭 64 GPFDAT &= ~(1<<6); // LED4点亮 65 break; 66 } 67 68 default: 69 break; 70 } 71 72 //清中断,此位可以通过清除 SRCPND和 INTPND 自动清除。 73 if( oft == 5 ) 74 EINTPEND = (1<<11); // EINTPEND(外部中断挂起寄存器),EINT23-EINT4[23:4],0使能中断,1禁止中断,往相对应位写1,清除此位,这里是清除EINT11 75 76 SRCPND = 1<<oft; // SRCPND(源挂起寄存器),EINT0[0]、EINT2 [2]:0未请求,1请求;这里应该是说最后如果是EINT0或EINT2话,往对应位写1就清除此位, 77 INTPND = 1<<oft; // INTMOD(中断模式寄存器),EINT0[0]、EINT2 [2]0 = IRQ ,1 = FIQ,如果某个指定为被设置为 1 ,则在 FIQ(快中断)模式中处理相应中断。否则则在 IRQ 模式中处理;清除,所以写1 78 }
四、
int main() { while(1); return 0; }
五、
1 objs := head.o init.o interrupt.o main.o #定义变量objs为包含的所有.o的合集,以后直接$(objs)就相当于使用罗列了所有的它包含的.o文件 2 int.bin: $(objs) #生成的int.bin依赖于objs中的.o文件 3 arm-linux-ld -Ttext 0x00000000 -o int_elf $^ #arm-linux-ld用于将多个目标文件、库文件连接成可执行文件;$^表示所有依赖的名字,名字间用空格分开,这里就表示objs中所有的.o文件 4 #arm-linux-ld 后面接:-Ttext addr(代码段)、-Tdata addr(数据段)、-Tbss addr(bbs段); 5 arm-linux-objcopy -O binary -S int_elf $@ #arm-linux-objcopy用来复制一个目标文件的内容到另一个文件中,可以使用不同源文件的格式来输出目的文件,即可以进行格式转换 6 #-O binary表示输出的格式是二进制.bin的文件;-S表示补充源文件中复制重定位信息和符号信息到目标文件中去; 7 #$@表示规则的目标文件名, 也就是指的int.bin 8 arm-linux-objdump -D -m arm int_elf > int.dis #arm-linux-objdump用于显示二进制文件信息,这里用来查看反汇编 9 #-D对所有的段执行反汇编;-m arm指反汇编结构为arm反汇编; 10 11 %.o:%.c #这里意思就是说,生成的.o文件依赖于.c文件;%是通配符,也就是可以代替任意的文件名; 12 arm-linux-gcc -Wall -O2 -c -o $@ $< #编译.c生成.o文件;-Wall是编译后显示警告信息;-O2进行一些优化;-c编译生成.o;s@就是int.bin;$<就是表示第一个依赖的文件名,这里就是所有.c的文件 13 14 %.o:%.S #将.S的文件全部转换成.o 15 arm-linux-gcc -Wall -O2 -c -o $@ $< #这里$<表示所有的.S的文件 16 17 clean: #clean用于清除上一次处理的文件; 18 rm -f int.bin int_elf int.dis *.o # *.o表示所有的.o文件 19
六、
/************************************************************************************************************************************************************************************ 用于将所有寄存器及变量宏定义地址指针,宏定义处理后,直接使用或处理这个变量,就相当于直接使用或处理了对应的地址的内容 ************************************************************************************************************************************************************************************/ 看门狗定时器控制寄存器 /**********************/ #define WTCON (*(volatile unsigned long *)0x53000000) /******************* 存储控制器和SDRAM *******************/ #define MEM_CTL_BASE 0x48000000 #define SDRAM_BASE 0x30000000 /********************** NAND Flash registers **********************/ #define NFCONF (*(volatile unsigned int *)0x4e000000) #define NFCMD (*(volatile unsigned char *)0x4e000004) #define NFADDR (*(volatile unsigned char *)0x4e000008) #define NFDATA (*(volatile unsigned char *)0x4e00000c) #define NFSTAT (*(volatile unsigned char *)0x4e000010) /******************* GPIO registers *******************/ #define GPBCON (*(volatile unsigned long *)0x56000010) #define GPBDAT (*(volatile unsigned long *)0x56000014) #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) #define GPFUP (*(volatile unsigned long *)0x56000058) #define GPGCON (*(volatile unsigned long *)0x56000060) #define GPGDAT (*(volatile unsigned long *)0x56000064) #define GPGUP (*(volatile unsigned long *)0x56000068) #define GPHCON (*(volatile unsigned long *)0x56000070) #define GPHDAT (*(volatile unsigned long *)0x56000074) #define GPHUP (*(volatile unsigned long *)0x56000078) /***************** UART registers *****************/ #define ULCON0 (*(volatile unsigned long *)0x50000000) #define UCON0 (*(volatile unsigned long *)0x50000004) #define UFCON0 (*(volatile unsigned long *)0x50000008) #define UMCON0 (*(volatile unsigned long *)0x5000000c) #define UTRSTAT0 (*(volatile unsigned long *)0x50000010) #define UTXH0 (*(volatile unsigned char *)0x50000020) #define URXH0 (*(volatile unsigned char *)0x50000024) #define UBRDIV0 (*(volatile unsigned long *)0x50000028) /******************* interrupt registes *******************/ #define SRCPND (*(volatile unsigned long *)0x4A000000) #define INTMOD (*(volatile unsigned long *)0x4A000004) #define INTMSK (*(volatile unsigned long *)0x4A000008) #define PRIORITY (*(volatile unsigned long *)0x4A00000c) #define INTPND (*(volatile unsigned long *)0x4A000010) #define INTOFFSET (*(volatile unsigned long *)0x4A000014) #define SUBSRCPND (*(volatile unsigned long *)0x4A000018) #define INTSUBMSK (*(volatile unsigned long *)0x4A00001c) /****************************** external interrupt registers *******************************/ #define EINTMASK (*(volatile unsigned long *)0x560000a4) #define EINTPEND (*(volatile unsigned long *)0x560000a8)