S3C2440 用C语言点亮LED

一、从汇编到C函数

1、设置栈

为什么从汇编调用C函数需要设置栈?
1、因为arm汇编调用C函数参数要遵循APCS规则。即参数小于等于4个可以用R0-R3来传参;多于4个的参数,前四个参数用R0-R3传参,第五个参数开始使用栈来传参
2、C函数内部使用到的局部变量,它所存储的空间就是栈空间
3、C函数里面调用C函数时,需要用栈来保存返回地址(LR寄存器的值)。

二、怎么设置堆栈

1、设置SP寄存器

设置栈,本质就是将SP(堆栈寄存器)指向一块内存。在S3C2440处理器里,要指向那块内存呢?
由于S3C2440在上电启动的时候,外部内存还没有初始化,我们只能将SP指向它内部4Kb的SRAM
又因为S3C2440支持nand flash启动和nor flash启动两种方式启动,而且两种方式的启动,内部SRAM的映射地址不一样,所以里两种方式设置地址不一样

2、nand flash启动

从下面的内存映射图可以看出;从nand flash启动的时候,内部SRAM被映射到了0x0000 0000地址;因为编译器编译的使用的是递减栈变编译的,所以通常我们将SP指向内部SRAM的最高地址处0x1000 (4096)

2、nor flash启动

从下面的内存映射图可以看出;从nand flash启动的时候,内部SRAM被映射到了0x4000 0000地址,所以通常我们将SP指向内部SRAM的最高地址处0x40001000 (0x40000000+0x1000)

三、程序

下面还是以点亮LED为例程,通过调用C函数来点亮一盏LED灯,在S3C2440启动流程已经给出了硬件原理图,这里就不贴出来了

1、启动程序(汇编程序 Startup.S)



.text                     /*定义代码段*/
.global _start            /*标号_start是GNU连接器用来指定第一个要执行所必须的(只能出现在一个模块),.global将_start声明为全局可见*/

_start:
    /* 设置栈 */
    /* nand flash 启动的设置方法 */
    ldr sp, =0x1000  /* 将栈设置在4k处 */
    /* nor flash 启动的设置方法 */
    //ldr sp, =0x40000000+0x1000  /* 将栈设置在0x40000000 + 4k处 (本程序使用nand flash启动,所以这里注释掉)*/
    /*调用C函数*/
    b main
loop:
    b loop

2、C函数(main.c)

/* 定义GPFCON 寄存器 */
#define  GPFCON    *((volatile unsigned long*)0x56000050)

/* 定义GPFDAT 寄存器 */
#define  GPFDAT    *((volatile unsigned long*)0x56000054)
int main(){
	
	/* 清零 */
	GPFCON &= ~(0x03 << 8);
	/* 设置为output */
	GPFCON |= (0x01<<8);
	
	/* 将 GPF4 输出低电平 */
	GPFDAT &= ~(1<<4);
	while(1);
	
	return 0;
}

3、Makefile

all:
	arm-linux-gcc -c Startup.S -o Startup.o
	arm-linux-gcc -c main.c -o main.o
	arm-linux-ld -Ttext 0 Startup.o main.o -o Led.elf
	arm-linux-objcopy -O binary -S Led.elf Led.bin
	arm-linux-objdump -D Led.elf > led.dis
clean:
	rm *.o *.elf *.bin *.dis

4、反汇编程序

在看反汇编的时候,可能会遇到sl,ip,fp,a1-a4,v1-v6这些寄存器,这些寄存器只是在APCS规则下的别名。他们对应的关系: sl->r10, fp->r11, ip->r12, a1-a4->r0-r3, v1-v6->r4-r9

Led.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000   /* 设置sp */
   4:	ea000000 	b	c <main>        /* 跳到 C语言的main函数执行 */

00000008 <loop>:
   8:	eafffffe 	b	8 <loop>

0000000c <main>:
   c:	e1a0c00d 	mov	ip, sp          /* 将sp的值保存起来,这里的ip寄存器其实是R12 */
  10:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc} /* 先将pc,lr,ip, fp 寄存器的值压入栈 */
  14:	e24cb004 	sub	fp, ip, #4	; 0x4 
  18:	e3a02456 	mov	r2, #1442840576	; 0x56000000    /* 下面的汇编语言就是操作GPF的寄存器的操作了 */
  1c:	e2822050 	add	r2, r2, #80	; 0x50
  20:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  24:	e2833050 	add	r3, r3, #80	; 0x50
  28:	e5933000 	ldr	r3, [r3]
  2c:	e3c33c03 	bic	r3, r3, #768	; 0x300
  30:	e5823000 	str	r3, [r2]                       
  34:	e3a02456 	mov	r2, #1442840576	; 0x56000000
  38:	e2822050 	add	r2, r2, #80	; 0x50
  3c:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  40:	e2833050 	add	r3, r3, #80	; 0x50
  44:	e5933000 	ldr	r3, [r3]
  48:	e3833c01 	orr	r3, r3, #256	; 0x100
  4c:	e5823000 	str	r3, [r2]
  50:	e3a02456 	mov	r2, #1442840576	; 0x56000000
  54:	e2822054 	add	r2, r2, #84	; 0x54
  58:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  5c:	e2833054 	add	r3, r3, #84	; 0x54
  60:	e5933000 	ldr	r3, [r3]
  64:	e3c33010 	bic	r3, r3, #16	; 0x10
  68:	e5823000 	str	r3, [r2]
  6c:	eafffffe 	b	6c <main+0x60>    /* 死循环 */
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.

四、实验现象

实验现象也是点亮一盏LED灯,就不贴图上来了。

posted @ 2020-10-29 00:29  古澜  阅读(494)  评论(0编辑  收藏  举报