S3C2440 用C语言点亮LED

2024-07-25  

一、从汇编到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

        /* 跳到 C语言的main函数执行 */


00000008 :

   8: eafffffe b 8


0000000c

:

   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

    /* 死循环 */

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灯,就不贴图上来了。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。