历史上的今天
返回首页

历史上的今天

今天是:2025年03月28日(星期五)

正在发生

2020年03月28日 | ARM-bootloader-C语言环境设计

2020-03-28 来源:eefocus

一、栈初始化

1、概念解析


1.1栈


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


1.2满栈和空栈


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


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


2、空栈:当堆栈指针SP总是指向下一个将要放入数据的空位置


ARM采用的是满栈


1.3、升/降栈


1、升栈:随着数据的入栈,SP指针从低地址->高地址移动


2、降栈:随着数据的入栈,SP指针从高地址->低地址移动


ARM采用的是降栈。有时候我们会说ARM采用的是满降栈。


1.4、栈帧


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

2、栈的作用


2.1、保存局部变量


#include

 

int main()

{

    int a;

 

    a++;

 

    return a;

}

反汇编后代码:

stack1:     file format elf32-littlearm

 

Disassembly of section .text:

 

00000000

:

#include

 

int main()

{

   0: e52db004 push {fp} ; (str fp, [sp, #-4]!)

   4: e28db000 add fp, sp, #0 ; 0x0

   8: e24dd00c sub sp, sp, #12 ; 0xc

    int a;

 

    a++;

   c: e51b3008 ldr r3, [fp, #-8]   //显然变量a存放在了栈中。

  10: e2833001 add r3, r3, #1 ; 0x1

  14: e50b3008 str r3, [fp, #-8]

 

    return a;

  18: e51b3008 ldr r3, [fp, #-8]

}

  1c: e1a00003 mov r0, r3

  20: e28bd000 add sp, fp, #0 ; 0x0

  24: e8bd0800 pop {fp}

  28: e12fff1e bx lr

从反汇编代码可以看出:局部变量a存放在了栈中。


2.2、参数传递


#include

 

 

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;

}

反汇编代码:

stack2:     file format elf32-littlearm

 

Disassembly of section .text:

 

00000000 :

#include

 

 

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

{

   0: e52db004 push {fp} ; (str fp, [sp, #-4]!)

   4: e28db000 add fp, sp, #0 ; 0x0

   8: e24dd01c sub sp, sp, #28 ; 0x1c

   c: e50b0010 str r0, [fp, #-16]

  10: e50b1014 str r1, [fp, #-20]

  14: e50b2018 str r2, [fp, #-24]

  18: e50b301c str r3, [fp, #-28]

int k;

k=e+f;

  1c: e59b3004 ldr r3, [fp, #4]

  20: e59b2008 ldr r2, [fp, #8]

  24: e0833002 add r3, r3, r2

  28: e50b3008 str r3, [fp, #-8]

}

  2c: e28bd000 add sp, fp, #0 ; 0x0

  30: e8bd0800 pop {fp}

  34: e12fff1e bx lr

 

00000038

:

 

int main()

{

  38: e92d4800 push {fp, lr}

  3c: e28db004 add fp, sp, #4 ; 0x4

  40: e24dd008 sub sp, sp, #8 ; 0x8

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

  44: e3a03005 mov r3, #5 ; 0x5

  48: e58d3000 str r3, [sp]

  4c: e3a03006 mov r3, #6 ; 0x6

  50: e58d3004 str r3, [sp, #4]

  54: e3a00001 mov r0, #1 ; 0x1

  58: e3a01002 mov r1, #2 ; 0x2

  5c: e3a02003 mov r2, #3 ; 0x3

  60: e3a03004 mov r3, #4 ; 0x4   //    当参数<=4时,可以用通用寄存器来传递参数,否则就要用到栈。

  64: ebfffffe bl 0

    return 0;

  68: e3a03000 mov r3, #0 ; 0x0

}

  6c: e1a00003 mov r0, r3

  70: e24bd004 sub sp, fp, #4 ; 0x4

  74: e8bd4800 pop {fp, lr}

  78: e12fff1e bx lr

从反汇编代码可以看出:栈可以传递参数,当参数不大于4时,可以用寄存器来传递,一旦大于4就把剩余的参数,用栈来传递。


2.3、保存寄存器值


#include

 

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;

}

反汇编:

stack3:     file format elf32-littlearm

 

Disassembly of section .text:

 

00000000 :

#include

 

void func2(int a,int b)

{

   0: e52db004 push {fp} ; (str fp, [sp, #-4]!)

   4: e28db000 add fp, sp, #0 ; 0x0

   8: e24dd014 sub sp, sp, #20 ; 0x14

   c: e50b0010 str r0, [fp, #-16]

  10: e50b1014 str r1, [fp, #-20]

    int k;

    k=a+b;

  14: e51b3010 ldr r3, [fp, #-16]

  18: e51b2014 ldr r2, [fp, #-20]

  1c: e0833002 add r3, r3, r2

  20: e50b3008 str r3, [fp, #-8]

}

  24: e28bd000 add sp, fp, #0 ; 0x0

  28: e8bd0800 pop {fp}

  2c: e12fff1e bx lr

 

00000030 :

 

void func1(int a,int b)

{

  30: e92d4800 push {fp, lr}

  34: e28db004 add fp, sp, #4 ; 0x4

  38: e24dd010 sub sp, sp, #16 ; 0x10

  3c: e50b0010 str r0, [fp, #-16]

  40: e50b1014 str r1, [fp, #-20] //保存了寄存器的值

int c;

func2(3,4);

  44: e3a00003 mov r0, #3 ; 0x3

  48: e3a01004 mov r1, #4 ; 0x4

  4c: ebfffffe bl 0

c=a+b;

  50: e51b3010 ldr r3, [fp, #-16]

  54: e51b2014 ldr r2, [fp, #-20]

  58: e0833002 add r3, r3, r2

  5c: e50b3008 str r3, [fp, #-8]

}

  60: e24bd004 sub sp, fp, #4 ; 0x4

  64: e8bd4800 pop {fp, lr}

  68: e12fff1e bx lr

 

0000006c

:

 

int main()

{

  6c: e92d4800 push {fp, lr}

  70: e28db004 add fp, sp, #4 ; 0x4

    func1(1,2);

  74: e3a00001 mov r0, #1 ; 0x1

  78: e3a01002 mov r1, #2 ; 0x2

  7c: ebfffffe bl 30

    return 0;

  80: e3a03000 mov r3, #0 ; 0x0

}

  84: e1a00003 mov r0, r3

  88: e24bd004 sub sp, fp, #4 ; 0x4

  8c: e8bd4800 pop {fp, lr}

  90: e12fff1e bx lr

反汇编代码中:栈可以用来保存寄存器中值。


3、初始化堆栈


通过上面的代码反汇编代码,我们可以知道栈对C语言非常重要,所以我们要对栈进行初始化。然后就可以用C语言来编程了。


无论是2440、6410还是210它们的SP指针都指向内存64M位置处。


2440内存:64MB


6410内存:256MB


210内存:512MB或1GB


所以它们的SP地址分别为


2440:0x34000000


6410:0x54000000


210:0x24000000


以6410为例进行编写:


init_stack:

ldr sp, =0x54000000

mov pc, lr


二、初始化BSS段

1、BSS段的作用


小知识:初始化的全局变量,存放在数据段。 局部变量存放在栈中。 malloc分配空间来自堆。 未初始化的全局变量存在BSS段。


实例:


# include

int year;

int main()

{

year = 1024;

return year;

}

使用交叉编译:arm-linux-gcc -g year.c -o year

阅读elf文件:arm-linux-readelf -a year >readelf


进入elf文件查找year存放位置


    89: 0001052c     4 OBJECT  GLOBAL DEFAULT   23 year  /*很显然year位于bss起始地址与终止之间,也就是说year在bss段*/

    90: 00010530     0 NOTYPE  GLOBAL DEFAULT  ABS __end__  

    91: 00008380   116 FUNC    GLOBAL DEFAULT   12 __libc_csu_init

    92: 00010530     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__  /*bss终止地址*/

    93: 00010528     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start   /*bss起始地址*/


2、为什么要初始化bss段

我们都知道在编写C语言代码过程中,有时候我们定义全局变量时,并不会同时进行初始化,而是到使用时再进行初始化。但有时候我们会忘记进行初始化。所以程序员就希望我们存放在bss段未初始化的全局变量都赋给0x0,如果我们一看到这个值就知道,我们没有对全局变量赋值,这样就会避免很多错误的出现。当然bss段初始化就由我们的系统工程师来完成。


3、编写代码


对BSS段清零操作


init_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语言


1、采用什么方式跳转


这里我们采用的是绝对跳转的方式。因为我们知道,main()函数是从SRAM中拷贝过去的,所以说相对跳转回调到SRAM中的main(),而我们要去内存中运行main()函数。


main.c


int gboot_main()


{


return 0;


}


然后还要在makefie中添加上main.o;


在start.S中添加代码为:


ldr pc, gboot_main 跳转到gboot_main函数处执行C函数。


2、代码的编写


main.c


#define GPKCON (volatile unsigned long*)0x7f008800 /*volatile是避免优化忽略,起保护作用*/

#define GPKDAT (volatile unsigned long*)0x7f008808

 

int gboot_main()

{

*(GPKCON) = 0x11110000;

*(GPKDAT) = 0xa0;

return 0;

}

使用make编译后,生产.bin文件,然后下载到开发板,运行。

注:对于210开发板,还有一个地方要修改。210有16个字节的头,所以copy_sram时,要跳过16个字节后进行复制操作。


四、C与汇编混合编程


1、为什么要使用混合编程


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


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


这样可以提高执行效率,同时可以更直接的控制cpu的内部寄存器。


2、混合编程的类型


一共有三种:1、汇编调用C语言 2、c调用汇编语言 3、c内嵌汇编


2.1、汇编调用c语言(已经在上面部分讲过,不再赘述)


2.2、C调用汇编函数


汇编函数:


#define GPKCON0 0x7f008800

#define GPKDAT 0x7f008808

.global light_led

light_led:

ldr r0, =GPKCON0

ldr r1, =0x11110000

str r1, [r0]

ldr r0, =GPKDAT

ldr r1, =0xa0

str r1, [r0]

mov pc, lr

C语言:

int gboot_main()

{

light_led();

return 0;

}

2.3、C内嵌汇编

格式:


asm(_asm_)(

汇编语言部分

:输出部分

:输入部分

:修改描述部分

 

);

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

输出语句:在汇编中被修改的C变量列表


输入语句:作为参数输入到汇编中的变量列表


修改描述语句:执行汇编指令会修改的寄存器描述


下面是C内嵌汇编的范例


void write_p15_c1(unsigned long value)

{

asm(

"mcr p15, 0, %0, c1, c0, 0n"

:

:"r"(value)@编译器选择了一个R*寄存器value为R中的值

:"memory"

);

}

unsigned long read_p15_c1(void)

{

unsigned long value;

asm(

"mrc p15, 0, %0, c1, c0, 0n"

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

:"memory"

);

return value;

}

下面有关一个优化问题:

unsigned long old;

unsigned long temp;

asm volatile(

"mrs %0, cpsrn"

"orr %1, %0, #128n"

"msr cpsr_c, %1n"

:"=r"(old), "=r"(temp)

:

:"memory"

);

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

下面是用C内嵌汇编完成的点亮led灯


#define GPKCON 0x7f008800

#define GPKDAT 0x7f008808

 

int gboot_main()

{

asm(

"ldr r1, =0x11110000n"

"str r1, [%0]n"

"ldr r1, =0xa0n"

"str r1, [%1]n"

"mov pc, lrn"

:

:"r"(GPKCON),"r"(GPKDAT)

:"r1"

);

return 0;

}

推荐阅读

史海拾趣

友盟(AP)公司的发展小趣事

友盟(AP)公司的发展,也离不开与众多合作伙伴的紧密合作。公司始终坚持开放、合作的理念,积极寻求与各行各业的合作机会。通过与手机厂商、应用开发者、广告商等建立合作关系,友盟成功将自身的技术优势和数据分析能力转化为商业价值。同时,友盟也通过合作伙伴的支持和反馈,不断优化产品和服务,实现了与合作伙伴的共赢发展。

港源(GANGYUAN)公司的发展小趣事

友盟(AP)公司深知人才是企业发展的核心动力。因此,公司一直注重人才团队的建设和发展。通过招聘优秀人才、提供完善的培训体系和激励机制,友盟成功打造了一支高素质、专业化的团队。这支团队不仅具备深厚的技术功底和创新能力,还具备敏锐的市场洞察力和执行力,为公司的快速发展提供了有力保障。

Hitachi Chemical Co America Ltd公司的发展小趣事

友盟(AP)公司深知人才是企业发展的核心动力。因此,公司一直注重人才团队的建设和发展。通过招聘优秀人才、提供完善的培训体系和激励机制,友盟成功打造了一支高素质、专业化的团队。这支团队不仅具备深厚的技术功底和创新能力,还具备敏锐的市场洞察力和执行力,为公司的快速发展提供了有力保障。

远阳(FLYOUNG)公司的发展小趣事

福建国光新业科技股份有限公司,作为全球领先的聚合物片式叠层铝电解电容器(MLPC)制造商,自成立之初便致力于国产高端被动元器件的技术开发与产品创新。面对高端电容器被国外厂商长期垄断的局面,国光新业经过长期技术积累与研发,成功打破了国际垄断,实现了MLPC电容器的国产替代。这一成就不仅填补了国内空白,还使公司在细分领域发明专利授权数量上跃居全球第一,荣获多项国家级荣誉称号。

捷嘉电子(Chequers Electronic)公司的发展小趣事

随着市场需求的不断变化,捷嘉电子意识到只有不断创新才能保持竞争力。于是,公司投入大量资源进行技术研发,特别是在智能控制器PCBA制造及智能产品研发方面取得了显著成果。其中,公司研发的一款新型智能家居控制器,以其卓越的稳定性和易用性受到了市场的热烈欢迎。这款产品的成功,不仅提升了捷嘉电子的品牌知名度,也为其后续发展奠定了坚实的基础。

Barry Industries Inc公司的发展小趣事

随着电子行业的快速发展,Barry Industries Inc意识到只有不断创新才能在市场中立足。公司加大了对研发的投入,引进了一批高素质的研发人才,并建立了完善的研发体系。经过多年的努力,Barry成功突破了微波半导体封装技术的多项关键技术,推出了多款性能卓越、具有创新性的产品。这些产品不仅广泛应用于军事、航天、通信等领域,还为公司赢得了市场的广泛认可。

问答坊 | AI 解惑

LC46芯片用何种设备能够读写

我是一只菜鸟。遇到一个问题,烦请大侠帮忙解决。有一个喷墨墨盒,上有一芯片,记录打印次数,如果超过限制,就不让使用,想解开此芯片,把新墨盒的数据写到老墨盒上,在灌上代用墨汁,就不用买原装的了。本菜鸟在很偏僻的树林(沈阳)没有找到办法 ...…

查看全部问答>

PCB设计的可测试性概念

产品设计的可测试性(De sign For Testability. OFT) 也是产品可制造性的主要内容从生产角度考虑也是设计的工艺性之一.它是指在设计时考虑产品性能能够检测的难易程度,也就是说设计产品时应考虑如何以最简单的方法对产品的性能和加工质量进行检测, ...…

查看全部问答>

DEBUGMSG(ZONE_TRACE, (TEXT("XXXX\r\n")));这样的语句在驱动里面怎么打印到调试串口?

DEBUGMSG(ZONE_TRACE, (TEXT(\"XXXX\\r\\n\")));这样的语句在驱动里面怎么打印到调试串口? …

查看全部问答>

想找个师父!指导下我嵌入式系统开发!

近期对嵌入式系统开发很有兴趣,像微软的WM,谷歌的android,苹果的iphone OS 本身我学习计算机语言就是想涉足计算机系统开发,但是开始学了C#,被微软的VS小黑盒子式编程给洗脑了...感觉编程好像比画图还简单!好想学了C#跟没学似地, 所以现在觉 ...…

查看全部问答>

请教2407与2812区别

请教一下TMS320LF2407A与TMS320F2812功能上与硬件上区别 才开始学习  很多不懂  请各位多多帮忙  谢谢了!!!…

查看全部问答>

MSP430与SHT温湿度传感器程序报错

程序的目的是把采集的温度通过串口显示,现在做的程度是我把硬件连接好后,用MSP430仿真器查看温湿度的数值不正确,比如humi_val.i用quick  watch查看value为62850这样的数据,单片机的txd用示波器观察没有波形,可能程序存在很大问题,现在比 ...…

查看全部问答>

赛灵思FPGA培训资料汇总,免费提供,欢迎索要

我这里有从各个研讨会搜集来的赛灵思大量FPGA的培训资料,如有需要请将您的邮箱发到:779861433,我会发到您的邮箱,谢谢!…

查看全部问答>

MSP430F477的SD16_A中断打开后单片机复位。

我把所有的程序都只注释掉了,只留IO初始化,SD16_A初始化,和一些延时子程序。 一运行到SD16_A初始化完成 中断允许后 单片机就复位了。这是什么情况 坐等高人解惑~~~~…

查看全部问答>

BB Black 入门基础之Eclipse C++ 控制LED灯(中)

本帖最后由 lonerzf 于 2014-1-13 10:57 编辑 接着上一篇。之前的地方设置是有问题的,先不讲可能是哪,大家帮忙给查个错呗。谢谢啦{:soso_e100:} 补充下,如果用 SSH Only方式进行远程部署,则在某些情况下方便得多。 还是在编译好之后选择绿 ...…

查看全部问答>