(嵌入式Linux 1 )第一个C程序

资源来自 韦东山嵌入式

目录

2440框架

汇编

汇编代码:.s

烧写步骤

反汇编代码

C语言(点亮led)

C语言实现

ATPCS使用规则

反汇编

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

C语言实现

启动文件start.s

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

c语言实现

启动文件


2440框架

(嵌入式Linux 1 )第一个C程序_第1张图片

 

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

(嵌入式Linux 1 )第一个C程序_第2张图片

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

(嵌入式Linux 1 )第一个C程序_第3张图片

(嵌入式Linux 1 )第一个C程序_第4张图片

反汇编

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

(嵌入式Linux 1 )第一个C程序_第5张图片

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

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

(嵌入式Linux 1 )第一个C程序_第6张图片

程序解析:

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的内存空间。

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

 

你可能感兴趣的:(嵌入式)