资源来自 韦东山嵌入式
目录
2440框架
汇编
汇编代码:.s
烧写步骤
反汇编代码
C语言(点亮led)
C语言实现
ATPCS使用规则
反汇编
C语言(交替点亮led,并传递参数)
C语言实现
启动文件start.s
C语言(交替点亮led,使用位序操作,自动区分nor和nand启动方式)
c语言实现
启动文件
启动过程:大多数ARM芯片从0地址启动
1.NOR启动:片内RAM基地址为0x40000000. 程序直接从NOR执行,NOR flash基地址为0,CPU读出NOR上第一个指令(前4字节),执行;CPU继续读出其他指令执行。
2.NAND启动:片内4kRAM基地址为0,NOR Flash不可访问 。2440硬件把Nand前4k内容复制到片内RAM,然后CPU从0地址取出第一条指令执行。
汇编 代码:002_led2_on
汇编代码:.s
```
/*
/*
点亮LED GPF4
*/
.text//代码段
.global _start
_start:
/*配置GPF4为输出引脚
* 0x100写入0x56000050
*/
ldr r1,=0x56000050//0x56000050比较复杂,不能确定是否是立即数,可以使用ldr
mov r0,#0x100
str r0,[r1]
/*配置GPF4为输出高电平
* 0x00写入0x56000054
*/
ldr r1,=0x56000054//0x56000050比较复杂,不能确定是否是立即数,可以使用ldr
mov r0,#0
str r0,[r1]
/*加入死循环,让程序不继续执行接下来的内存命令*/
halt:
b halt
1.开启虚拟机Ubuntul,用户名:book,密码:123456. ifconfig查看IP
2.串口工具MobaXterm_Personal_10.4远程登陆SSH。
3.打开文件存储位置 cd/work/ cd hardware/
4.软件FileZila传输文件,将文件led_on.S上传到linux.
5.建立Makefile
all:
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
arm-linux-objcopy -O binary -S led_on.elf led_on.bin
arm-linux-objdump -D led_on.elf > led_on.dis
clean:
rm *.bin *.o *.elf
6.串口工具中make编译生成.bin文件,将bin文件通过FileZila复制到windows
7.cmd打开bin文件目录``cd C:\Users\LYL\Desktop\韦东山开发板工具\core\001_led_on`` oflash打开bin``oflash led_on.bin``
8.选项0-1-0-0-0;拔掉oflash线,Nand启动,烧写完毕
反汇编代码:.dis
led_on.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e59f1014 ldr r1, [pc, #20] ; 1c <.text+0x1c>,pc值为当前地址0+8=8,8+20=0x1c,0x1c地址中存放的0x56000050,放入r1
4: e3a00c01 mov r0, #256 ; 0x100
8: e5810000 str r0, [r1] ;0x100写入r1对应的内存0x56000050
c: e59f100c ldr r1, [pc, #12] ; 20 <.text+0x20> pc的值为c+8=20,20+12=32(0x20),0x20地址中存放的0x56000054,放入r1
10: e3a00000 mov r0, #0 ; 0x0
14: e5810000 str r0, [r1]
00000018 :
18: eafffffe b 18
1c: 56000050 undefined
20: 56000054 undefined
ARM中寄存器
其中pc表示当前指令地址+8(ARM在执行当前地址A的指令时,已经在对A+4的指令进行译码,同时已经在读取A+8的指令)
lr表示程序返回地址
sp表示数据栈
子程序间利用r0-r3看来传递参数,利用r4-r11来保存局部变量
所以在上面反汇编代码中:
0.读取[pc+20+8]=28(0x1c),也就是地址0x1c的值0x5600050,存到r1寄存器中
4.将256存入寄存器r0中。
8.将r0的值写入地址r1中 c.读取[pc+12+8]=32(0x20),也就是0x20地址的值0x56000054,存到寄存器r1中
10将0写入到寄存器r0中
14.将r0的值写入到地址r1中
代码:003_led_c
```
int main()
{
unsigned int *pGPFCON=(unsigned int *)0x56000050;
unsigned int *pGPFDAT=(unsigned int *)0x56000054;
*pGPFCON=0x100;
*pGPFDAT=0;
return 0;
}
加入汇编start.s,调用main
.text
.global _start
_start:
/*设置内存栈 sp栈,局部变量保存在栈中*/
ldr sp,=4096;/*Nand启动,4K地址设置为栈,保存局部变量*/
ldr sp,=0x40000000+4096; /*Norflash地址从0x40000000开始,同样从地址4K存放局部变量,设置为栈*/
/*调用main 跳转执行main,并且保存返回地址到lr*/
bl main
halt:
b halt
栈的作用:1.保存局部变量。2.保存lr等寄存器的内容
加入makefile
all:
arm-linux-gcc -c -o led.o led.c
arm-linux-gcc -c -o start.o start.S
arm-linux-ld -Ttext 0 start.o led.o -o led.elf
arm-linux-objcopy -O binary -S led.elf led.bin
arm-linux-objdump -D led.elf > led.dis
clean:
rm *.bin *.o *.elf *.dis
编译成bin,反汇编:
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000
4: eb000000 bl c
00000008 :
8: eafffffe b 8
0000000c :
c: e1a0c00d mov ip, sp
10: e92dd800 stmdb sp!, {fp, ip, lr, pc}
14: e24cb004 sub fp, ip, #4 ; 0x4
18: e24dd008 sub sp, sp, #8 ; 0x8
1c: e3a03456 mov r3, #1442840576 ; 0x56000000
20: e2833050 add r3, r3, #80 ; 0x50
24: e50b3010 str r3, [fp, #-16]
28: e3a03456 mov r3, #1442840576 ; 0x56000000
2c: e2833054 add r3, r3, #84 ; 0x54
30: e50b3014 str r3, [fp, #-20]
34: e51b2010 ldr r2, [fp, #-16]
38: e3a03c01 mov r3, #256 ; 0x100
3c: e5823000 str r3, [r2]
40: e51b2014 ldr r2, [fp, #-20]
44: e3a03000 mov r3, #0 ; 0x0
48: e5823000 str r3, [r2]
4c: e3a03000 mov r3, #0 ; 0x0
50: e1a00003 mov r0, r3
54: e24bd00c sub sp, fp, #12 ; 0xc
58: e89da800 ldmia sp, {fp, sp, pc}
Disassembly of section .comment:
00000000 <.comment>:
0: 43434700 cmpmi r3, #0 ; 0x0
4: 4728203a undefined
8: 2029554e eorcs r5, r9, lr, asr #10
c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1}
10: Address 0x10 is out of bounds.
Nand内存中的程序会将前4k拷贝到内部RAM中运行 上面程序烧写进入4k内存分布:
程序写在地址0-58中,而数据栈在4k内存顶部,设置语句: `` ldr sp,=4096 //nand启动``
反汇编中为: ``` 0: e3a0da01 mov sp, #4096 ; 0x1000```
程序解析:
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000
4: eb000000 bl c 存放程序入口返回地址到lr 0x08
00000008 :
8: eafffffe b 8
0000000c :
c: e1a0c00d mov ip, sp //ip=sp=4096
10: e92dd800 stmdb sp!, {fp, ip, lr, pc} //先减后存 ,pc=当前地址+8=0x18,lr=8,ip=4096,fp未知,运算完毕后sp=4080
14: e24cb004 sub fp, ip, #4 ; 0x4 //fp=ip-4=4092
18: e24dd008 sub sp, sp, #8 ; 0x8 //sp=sp-8=4072
1c: e3a03456 mov r3, #1442840576 ; 0x56000000 //r3存放0x56000000
20: e2833050 add r3, r3, #80 ; 0x50 //r3=0x56000050
24: e50b3010 str r3, [fp, #-16] //r3存入fp-16=4092-16=4076
28: e3a03456 mov r3, #1442840576 ; 0x56000000 //
2c: e2833054 add r3, r3, #84 ; 0x54 //r3赋值=0x56000054
30: e50b3014 str r3, [fp, #-20] //r3存入fp-20=4072中
34: e51b2010 ldr r2, [fp, #-16] //读取内存[fp=4092-16=4076]=0x56000050到r2中
38: e3a03c01 mov r3, #256 ; 0x100//r3=0x100
3c: e5823000 str r3, [r2] //将0x100存入[0x56000050]中
40: e51b2014 ldr r2, [fp, #-20] //读取内存r2=[fp-20=4072]=0x56000054
44: e3a03000 mov r3, #0 ; 0x0 //r3=0
48: e5823000 str r3, [r2] //将0存入地址0x56000054中
4c: e3a03000 mov r3, #0 ; 0x0
50: e1a00003 mov r0, r3 //对应C语言中的return 0;
54: e24bd00c sub sp, fp, #12 ; 0xc //sp=fp-12=4080
58: e89da800 ldmia sp, {fp, sp, pc} //先存后增 fp=[4080]的值 ,sp=[4084]的值4096,pc=[4088]的值8,返回0x8的地址,也就是main返回的地址
Disassembly of section .comment:
00000000 <.comment>:
0: 43434700 cmpmi r3, #0 ; 0x0
4: 4728203a undefined
8: 2029554e eorcs r5, r9, lr, asr #10
c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1}
10: Address 0x10 is out of bounds.
所以栈就是sp所指向的内存,用来保存寄存器,函数返回前,恢复寄存器,保存局部变量。以上程序的栈就是地址4096向下到地址4072的内存空间。
代码:004_led_parmas
void delay(volatile int d)
{
while(d--);
}
int led_on(int which)
{
unsigned int *pGPFCON = 0x56000050;
unsigned int *pGPFDAT = 0x56000054;
if (which == 4)
{
*pGPFCON = 0x100;
}
else if (which == 5)
{
*pGPFCON = 0x400;
}
*pGPFDAT = 0;
}
.text
.global _start
_start:
/*设置内存栈 sp栈,局部变量保存在栈中*/
ldr sp,=4096;/*Nand启动,Nandflash前4K保存的指令,从4K开始设置为栈,保存局部变量*/
ldr sp,=0x40000000+4096; /*Norflash地址从0x40000000开始,同样从地址4K开始存放局部变量,设置为栈*/
/*调用r0传递参数*/
mov r0,#4
bl led_on
ldr r0,=100000
bl delay
mov r0,#5
bl led_on
halt:
b halt
程序运行结果led会交替闪烁,因为系统中带有看门狗会自动复位
代码:005_leds 001th
void delay(volatile int d)
{
while(d--);
}
int main(void)
{
volatile unsigned int *pGPFCON = 0x56000050;
volatile unsigned int *pGPFDAT = 0x56000054;
int val=0;
//设置GPFCON让GPF4,5,6配置为输出
*pGPFCON &= ~((3<<8)|(3<<10)|(3<<12)); //将bit[8:9][10:11][12:13]清零
*pGPFCON |= ((1<<8)|(1<<10)|(1<<12)); //将bit[8:9][10:11][12:13]写入01
/*循环点亮 pGPFDAT bit[4:6]*/
while(1)
{
*pGPFDAT &= ~(7<<4);//bit[4:6]清0
*pGPFDAT |= val<<4; //bit[4:6]循环+1
val++;
if(val==8)
val=0;
delay(100000);
}
*pGPFDAT = 0;
}
.text
.global _start
_start:
/*关闭看门狗,防止系统自动复位*/
ldr r0,=0x53000000
ldr r1,=0
str r1,[r0]
/*设置内存栈 sp栈,局部变量保存在栈中*/
/*分辨是nor还是nand启动(nor相当于硬盘,写入数据时有固定的格式,不能简单的写入)
*写0到0地址,再读出来
*如果得到0,则表示内存0地址内容被修改了,就是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] //如果前面比较相等,恢复[r1]的值,如果是nor启动,有固定的格式写入,前面的语句无法成功写入nor,所以原来[r1]中的值并没有变化
bl main
halt:
b halt