第二季-专题10-C语言环境初始化

专题10-C语言环境初始化

    Bootloader的前半部分需要汇编语言的使用,后半部分需要 C语言的使用。

第一课.栈的初始化

 

一.概念解析

栈是一种具有后进先出性质的数据组织方式,也就是说后存放的先取出,先存放的后取出。栈底是第一个进栈的数据所处的位置,栈顶是最后一个进栈的数据所处的位置。

  1. 根据SP指针指向的位置,栈可以分为满栈和空栈

(1)满栈:当堆栈指针SP总是指向最后压入堆栈的数据

(2)空栈:堆栈指针SP指向下一个将要放入数据的空位置

ARM采用满栈。

  1. 升降栈

根据SP指针的移动方向,栈可以分为升栈和降栈

(1)升栈:随着数据的入栈,SP指针从低地址指向高地址。

(2)降栈:随着数据的入栈,SP指针从高地址指向低地址。

ARM采用降栈

  1. 栈帧

简单的讲,栈帧(stack frame)就是一个函数所使用的的那部分栈,所有函数的栈帧串起来就是一个完整的栈。栈帧的两个边界分别由fp(r11)和sp(r13)来限定。

二. 栈的作用

(1)保存局部变量

#include <stdio.h>

int main()

{

    int a;

    a++;

    return a;

}

(2)参数传递

#include <stdio.h>

void func1(int a,int b,int c,int d,int e,int f)

{

    int k;

    k=e+f;

}

int main()

{

    func1(1,2,3,4,5,6);

    return 0;

}

(3)保存寄存器变量

#include <stdio.h>

void func2(int a,int b)

{

    int k;

    k=a+b;

}

void func1(int a,int b)

{

    int c;

    func2(3,4);

    c=a+b;

}

int main()

{

    func1(1,2);

    return 0;

}

三.写代码

reset:

    bl set_svc

    bl disable_watchdog

    bl disable_interrupt

    bl disable_mmu

    bl init_clock

    bl init_sdram

    bl copy_to_ram

    bl init_stack

    bl light_led

init_stack:    @2440,内存地址加64Mb

    ldr sp, =0x34000000

    mov pc ,lr

init_stack:    @6410,内存地址加64Mb

    ldr sp, =0x54000000

    mov pc ,lr

init_stack:    @210,内存地址加64Mb

    ldr sp, =0x24000000

    mov pc ,lr

 

第二课.初始化Bss段

·Bss段的作用

初始化的全局变量存在数据段,局部变量存在栈,malloc申请的数据存在堆,未初始化全局变量存放在bss段。找到bss段的起始地址和结束地址,往其中放的数都是0,在链接器脚本中有bss_start记录起始地址和bss_end终止地址。

bl clean_bss   @2440、6410、210通用

clean_bss:

         ldr r0, =bss_start

         ldr r1, =bss_end

         cmp r0, r1  @若相等的话

         moveq pc, lr  @直接返回,什么不做

clean_loop:     @不相等,执行循环语句

         mov r2, #0

         str r2, [r0], #4

         cmp r0, r1

         bne clean_loop

         mov pc, lr

第三课.一跃进入C大门

这一阶段开始从汇编语言转换成C语言来进行

我们思考的两个问题:

         采用什么方式跳转?检验是否跳转成功?

一. 跳转

当前我们的程序运行在垫脚石中,我们希望跳转到内存中的main函数,这种方式是绝对跳转。相对跳转需要两个位置之间的差值,相对跳转的会发现SRAM(垫脚石)之中与内存之中都有main函数,会直接跳转到垫脚石之中的main函数。所以我们采用绝对跳转。

代码:

.global _start

reset:

         bl set_svc

         bl disable_watchdog

         bl disable_interrupt

         bl disable_mmu

         bl init_clock

         bl init_sdram

         bl copy_to_ram

         bl init_stack

         bl clean_bss

         ldr pc, =gboot_main

@     bl light_led

在C语言文件中,写下如下的代码:

#define GPBCON (volatile unsigned long*)0x56000010

#define GPBDAT (volatile unsigned long*)0x56000014

int gboot_main()

{

    *(GPBCON) = 0x15400;

    *(GPBDAT) = 0x6BF;

    return 0;   

}

Makefile中修改:

all: start.o main.o

    arm-linux-ld -Tgboot.lds -o gboot.elf $^

    arm-linux-objcopy -O binary gboot.elf gboot.bin

   

%.o : %.S

    arm-linux-gcc -g -c $^

   

%.o : %.c

    arm-linux-gcc -g -c $^

   

.PHONY: clean

clean:

    rm *.o *.elf *.bin

二. 使用C语言点亮led

         将汇编语言中的点亮led操作注销了,将这部分代码导入到C文件中。

#define GPBCON (volatile unsigned long*)0x56000010

#define GPBDAT (volatile unsigned long*)0x56000014

int gboot_main()

{

    *(GPBCON) = 0x15400; //控制结存器

    *(GPBDAT) = 0x6BF;   //数据寄存器

    return 0;   

}

C语言中的地址都是unsigned long*,为了减少不必要的优化,外加volatile unsigned long,给地址赋值在前面加星号,加入汇编代码的值就好。

注意:

使用210开发板编写的代码,是运行不出来的。在使用210的时候,我们加了16字节的头,这是给bootloader使用的。当把垫脚石中的代码复制到内存的时候,这个头就不需要使用的,需要将这个头文件去掉。210的垫脚石复制从第16个字节开始,其中内存初始化程序是:

         copy_to_ram:

    ldr r0, =0xd0020010

    ldr r1, =0x20008000

    add r3, r0, #1024*4

第四课.第4课-C与汇编混合编程

C语言与汇编的混合编程不仅出现在,我们的bootloader设计中,出现在驱动中,在其他的嵌入式开发中也是需要的。下面我们看一下两种语言各自的优点。

汇编语言:执行效率高;编写繁琐。

C语言:可读性强,移植性好,调试方便。

在一些执行效率需要很高,能更直接地控制处理器的地方,我们必须要使用汇编语言。

 

  1. 汇编调用C函数

若是下汇编语言下调用C函数,直接在程序中,将函数名给pc指针就好。我们之前写的代码就是汇编调用C函数。

  1. C语言调用汇编

将汇编语言的函数名放在C语言中,单在这之前要把调用的函数升级为全局的。也就是在相应的函数之前加上.global。

  1. C语言内嵌汇编语言

这部分我们不是进行引用,而是直接在C语言中写汇编语言。

(1)格式

_asm_(

汇编语言部分

:输出部分

:输入部分

:破坏描述部分

);

C语言内嵌汇编以关键字“_asm_”或者“asm”开始,下辖四个部分,各部分之间使用“:”分开,第一部分是必须写的,后面的三部分是可以省略的,但是分号:不能省略!

l  汇编语句部分;汇编语句的集合,可以包含多条汇编语句,每条语句之间需要使用换行符“\n”合开或者分号“;”隔开。

l  输出部分:在汇编中被修改的C变量列表。

l  输出部分:作为参数输入到会汇编中的变量列表。

l  破坏描述部分:执行汇编指令会被破坏的寄存器描述

(2)范例

void write_p51_c1 (unsigned long value)

{       

_asm_(

“mcr p15, 0, %0, c1, c0, 0\n”   @其中%0表示0号参数

:

:”r”(value)@编译器选择一个R*寄存器  将value的值给%0

:”memory”);

}

Unsigned long read_p15_c1 (void)

{

unsigned long value;

_asm_(

“mcr p15, 0, %0, c1, c0, 0\n”

: ”=r” (value) @ ’=’ 表示只写操作数,用于输出部

: “memry”);
         return value;

}

 

unsigned long old;

unsigned long temp; 

_asm_ volatile(

“mrs  %0,  cpsr  \n”

“orr  %1,  %0,  #128  \n”

“msr  cpsr_c,  %1\n”

: ”=r” (old), “=r” (temp)

:

: “memory”);

使用volatile来告诉编译器,不要对接下来的这部分代码进行优化。

 

转载于:https://www.cnblogs.com/free-1122/p/11452038.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值