04 点亮LED C

1 C语言控制LED

前述汇编中,写地址0x56000050(GPFCON)和0x56000054(GPFDAT),相当于C中的指针操作。

void main(void)
{
	unsigned int * pGPFCON = 0x56000050;
	unsigned int * pGPFDAT = 0x56000054;

	/* 配置GPF4为输出引脚 */
	*pGPFCON = 0x100;

	/* 设置GPF4输出0 */
	*pGPFDAT = 0;
}

问题来了。1 我们写出了main函数,谁来调用它?  2  main函数中的变量,保存在内存中,内存地址是多少?

解决办法:我们还需要写一个汇编代码,给main函数设置内存,调用main函数

代码如下:

led.c

int main(void)
{
	unsigned int * pGPFCON = (unsigned int *)(0x56000050);
	unsigned int * pGPFDAT = (unsigned int *)(0x56000054);

	/* 配置GPF4为输出引脚 */
	*pGPFCON = 0x100;

	/* 设置GPF4输出0 */
	*pGPFDAT = 0;

	return 0;
}

start.S


.text
.global _start

_start:

	/* 设置内存: sp栈 */
	ldr sp, =4096	/* nand启动 */
	// ldr sp, =0x40000000 + 4096	/* nor启动 */

	/* 调用main */
	bl main

halt:
	b halt

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

汇编指令中,bl跳转和b的区别(bl会在lr寄存器中,保存当前pc - 4,就是当前指令的下一条指令):

2 C语言分析

几条汇编指令:

2.1  add r0, r1, r2  或 add r0, r1, #4 

2.2  sub r0, r1, r2 或 sub r0, r1, #4

3 ldm  stm  传送多个数据

按照图示,堆栈操作中,常使用的是ldmia(先读取栈数据,再增加栈地址)和stmdb(先减少栈地址,然后保存到栈中)。

如图所示,stmdb sp!, {fp, ip, lr, pc},大括号里面的寄存器顺序可以随便写,无所谓。

假设sp = 4096:(1)sp=sp-4  pc寄存器的值,被保存在4092~4095内存中;(2)sp=sp-4;lr寄存器的值被保存在4088~4091内存中。。。

到这里,说白了,stmdb就是把寄存器的值保存到内存(栈)中。

最后这个感叹号sp!,表示sp为修改后的值4080;如果不加!,则sp为原始值4096。

ldmia指令

C语言内部机制分析

Start.S做了哪些事情?

1 设置栈;2 调用main函数,把返回地址保存在lr中;

led.c main函数做了什么?

1 定义两个局部变量;2 设置变量; 3 return 0;

问题来了。为什么要设置栈?因为C函数要用。怎么使用?

栈的作用:1 保存局部变量;2 保存lr等寄存器(子函数调用);

调用者如何串参数给被调用者?被调用者如何传返回值给调用者?

怎么从栈中恢复寄存器?

1 ATPCS规则

2 汇编代码与bin文件

可以看到,bin文件完全就是汇编代码。

再次说明:关于BL跳转与LR返回地址的解释:

内存布局:

汇编代码操作寄存器如下:

通过r0寄存器传递参数

led.c

void delay(int cnt)
{
	while (cnt--);
}

int led_on(int which)
{
	unsigned int * pGPFCON = (unsigned int *)(0x56000050);
	unsigned int * pGPFDAT = (unsigned int *)(0x56000054);

	if (4 == which)
	{
		/* 配置GPF4为输出引脚 */
		*pGPFCON = 0x100;
	}
	else if (5 == which)
	{
		/* 配置GPF5为输出引脚 */
		*pGPFCON = 0x400;
	}
	
	/* 设置GPF4/5输出0 */
	*pGPFDAT = 0;

	return 0;
}

start.S


.text
.global _start

_start:

	/* 设置内存: sp栈 */
	ldr sp, =4096	/* nand启动 */
	// ldr sp, =0x40000000 + 4096	/* nor启动 */

	// 使用r0传递参数
	mov r0, #4
	bl led_on

	ldr r0, =100000
	bl delay

	mov r0, #5
	bl led_on

halt:
	b halt

编译得到的汇编文件如下:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值