编写第一个嵌入式linux,(嵌入式Linux 1 )第一个C程序

资源来自 韦东山嵌入式

目录

2440框架

汇编

汇编代码:.s

烧写步骤

反汇编代码

C语言(点亮led)

C语言实现

ATPCS使用规则

反汇编

C语言(交替点亮led,并传递参数)

C语言实现

启动文件start.s

C语言(交替点亮led,使用位序操作,自动区分nor和nand启动方式)

c语言实现

启动文件

2440框架

a1d81b81bcb442645af928993d7bc2bc.png

启动过程:大多数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中寄存器

58574fa60f480363a7a944c4fc796ca3.png

其中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使用规则

6e67c436cf84ad3245b013652262f9d0.png

5fe784b89367e65278f22d5c4478ed17.png

反汇编

编译成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内存分布:

e76dfc51f04cc32d80a3f5ab4315edb8.png

程序写在地址0-58中,而数据栈在4k内存顶部,设置语句: `` ldr sp,=4096 //nand启动``

反汇编中为: ``` 0: e3a0da01 mov sp, #4096 ; 0x1000```

89026be20b677b892178866edf7abc7e.png

程序解析:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值