• 第二季-专题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  汇编语句部分;汇编语句的集合,可以包含多条汇编语句,每条语句之间需要使用换行符“ ”合开或者分号“;”隔开。

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

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

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

    (2)范例

    void write_p51_c1 (unsigned long value)

    {       

    _asm_(

    “mcr p15, 0, %0, c1, c0, 0 ”   @其中%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 ”

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

    : “memry”);
             return value;

    }

    unsigned long old;

    unsigned long temp; 

    _asm_ volatile(

    “mrs  %0,  cpsr  ”

    “orr  %1,  %0,  #128  ”

    “msr  cpsr_c,  %1 ”

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

    :

    : “memory”);

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

  • 相关阅读:
    redis配置文件 redis.conf
    CentOS安装 NodeJS 和 NPM
    Docker中运行redis报错误: Failed opening the RDB file root (in server root dir /etc/cron.d) for saving: Permission denied
    AllowControlAllowOrigin:谷歌跨域扩展插件下载
    uniapp 判断客户端环境是安卓还是ios
    Windows环境下查看某个端口被哪个应用程序占用并停止程序
    Oracle数据库快速入门
    Linux 使用vim命令编辑文件内容
    解决VMware Workstation客户机与宿主机无法复制文件和共享剪切板的问题
    Spring 中的事件机制 ApplicationEventPublisher
  • 原文地址:https://www.cnblogs.com/free-1122/p/11452038.html
Copyright © 2020-2023  润新知