资源来自 韦东山嵌入式
目录
2440框架
汇编
汇编代码:.s
烧写步骤
反汇编代码
C语言(点亮led)
C语言实现
ATPCS使用规则
反汇编
C语言(交替点亮led,并传递参数)
C语言实现
启动文件start.s
C语言(交替点亮led,使用位序操作,自动区分nor和nand启动方式)
c语言实现
启动文件
2440框架
启动过程:大多数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
汇编代码:.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 :
0:e59f1014 ldrr1, [pc, #20]; 1c ,pc值为当前地址0+8=8,8+20=0x1c,0x1c地址中存放的0x56000050,放入r1
4:e3a00c01 movr0, #256; 0x100
8:e5810000 strr0, [r1] ;0x100写入r1对应的内存0x56000050
c:e59f100c ldrr1, [pc, #12]; 20 pc的值为c+8=20,20+12=32(0x20),0x20地址中存放的0x56000054,放入r1
10:e3a00000 movr0, #0; 0x0
14:e5810000 strr0, [r1]
00000018 :
18:eafffffe b18 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中
C语言(点亮led)
代码:003_led_c
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
ATPCS使用规则
反汇编
编译成bin,反汇编:
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 :
0:e3a0da01 movsp, #4096; 0x1000
4:eb000000 blc 00000008 :
8:eafffffe b8 0000000c :
c:e1a0c00d movip, sp
10:e92dd800 stmdbsp!, {fp, ip, lr, pc}
14:e24cb004 subfp, ip, #4; 0x4
18:e24dd008 subsp, sp, #8; 0x8
1c:e3a03456 movr3, #1442840576; 0x56000000
20:e2833050 addr3, r3, #80; 0x50
24:e50b3010 strr3, [fp, #-16]
28:e3a03456 movr3, #1442840576; 0x56000000
2c:e2833054 addr3, r3, #84; 0x54
30:e50b3014 strr3, [fp, #-20]
34:e51b2010 ldrr2, [fp, #-16]
38:e3a03c01 movr3, #256; 0x100
3c:e5823000 strr3, [r2]
40:e51b2014 ldrr2, [fp, #-20]
44:e3a03000 movr3, #0; 0x0
48:e5823000 strr3, [r2]
4c:e3a03000 movr3, #0; 0x0
50:e1a00003 movr0, r3
54:e24bd00c subsp, fp, #12; 0xc
58:e89da800 ldmiasp, {fp, sp, pc}
Disassembly of section .comment:
00000000 :
0:43434700 cmpmir3, #0; 0x0
4:4728203a undefined
8:2029554e eorcsr5, r9, lr, asr #10
c:2e342e33 mrccs14, 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 :
0:e3a0da01 movsp, #4096; 0x1000
4:eb000000 blc 存放程序入口返回地址到lr 0x08
00000008 :
8:eafffffe b8 0000000c :
c:e1a0c00d movip, sp //ip=sp=4096
10:e92dd800 stmdbsp!, {fp, ip, lr, pc} //先减后存 ,pc=当前地址+8=0x18,lr=8,ip=4096,fp未知,运算完毕后sp=4080
14:e24cb004 subfp, ip, #4; 0x4 //fp=ip-4=4092
18:e24dd008 subsp, sp, #8; 0x8 //sp=sp-8=4072
1c:e3a03456 movr3, #1442840576; 0x56000000 //r3存放0x56000000
20:e2833050 addr3, r3, #80; 0x50 //r3=0x56000050
24:e50b3010 strr3, [fp, #-16] //r3存入fp-16=4092-16=4076
28:e3a03456 movr3, #1442840576; 0x56000000 //
2c:e2833054 addr3, r3, #84; 0x54 //r3赋值=0x56000054
30:e50b3014 strr3, [fp, #-20] //r3存入fp-20=4072中
34:e51b2010 ldrr2, [fp, #-16] //读取内存[fp=4092-16=4076]=0x56000050到r2中
38:e3a03c01 movr3, #256; 0x100//r3=0x100
3c:e5823000 strr3, [r2] //将0x100存入[0x56000050]中
40:e51b2014 ldrr2, [fp, #-20]//读取内存r2=[fp-20=4072]=0x56000054
44:e3a03000 movr3, #0; 0x0 //r3=0
48:e5823000 strr3, [r2] //将0存入地址0x56000054中
4c:e3a03000 movr3, #0; 0x0
50:e1a00003 movr0, r3//对应C语言中的return 0;
54:e24bd00c subsp, fp, #12; 0xc //sp=fp-12=4080
58:e89da800 ldmiasp, {fp, sp, pc} //先存后增 fp=[4080]的值 ,sp=[4084]的值4096,pc=[4088]的值8,返回0x8的地址,也就是main返回的地址
Disassembly of section .comment:
00000000 :
0:43434700 cmpmir3, #0; 0x0
4:4728203a undefined
8:2029554e eorcsr5, r9, lr, asr #10
c:2e342e33 mrccs14, 1, r2, cr4, cr3, {1}
10:Address 0x10 is out of bounds.
所以栈就是sp所指向的内存,用来保存寄存器,函数返回前,恢复寄存器,保存局部变量。以上程序的栈就是地址4096向下到地址4072的内存空间。
C语言(交替点亮led,并传递参数)
代码:004_led_parmas
C语言实现
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;
}
启动文件start.s
.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会交替闪烁,因为系统中带有看门狗会自动复位
C语言(交替点亮led,使用位序操作,自动区分nor和nand启动方式)
代码:005_leds 001th
c语言实现
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