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

资源来自 韦东山嵌入式

目录

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 <_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 <halt>:
  18:	eafffffe 	b	18 <halt>
  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 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000
   4:	eb000000 	bl	c <main>

00000008 <halt>:
   8:	eafffffe 	b	8 <halt>

0000000c <main>:
   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 <main>  存放程序入口返回地址到lr 0x08

00000008 <halt>:
   8:	eafffffe 	b	8 <halt>

0000000c <main>:
   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

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值