文号
t51638
阅读
6280
回复
7
收藏
7
自己写个简单的ARM启动代码,了解C语言程序运行前的那点小动作
2012/10/24
IP:四川
通过这个例子应该可以明白两个问题
     1、 在做单片机和arm程序的时候,都会遇到启动代码。在单片机编程中,很多时候启动代码都是已经由集成开发环境准备好了,并不要我们自己去写。但是在arm中,因为ram啊,flash啊,还有各种设备是根据电路设计来的,所以启动代码不自己写都得自己修改。
      2、为什么不用C语言来写启动代码而必须是汇编?为什么不能在程序一开始就使用C语言的程序?C语言程序运行所需要的基本环境是什么?其实C语言程序并不一定要从main函数执行,也可以用自己定义的函数名,在例子后就会明白。

       其实上面的两个问题对于用汇编来写程序的人来说,都是理所当然的,但是更多的人是从C语言开始学习编程的,其约定俗成的都是从main函数开始执行的,很多地方也把这个main函数解释为程序的入口,听起来貌似这个入口就很特别。其实这个main函数和普通函数是差不多的,都是被调用了才会执行的,但是如果不统一的话,你的程序入口是main(),我的程序入口是start(),就很难统一,所以约定俗成的就是这个函数都叫main,当然也有不用main的,比如MFC。既然main函数和其它函数一样,那么他在被调用的时候就会有个动作,压栈,但是现在寄存器SP,也就是栈指针是指向哪里的?对了,这个指针还没有被初始化!也就是说,如果你想调用C语言的函数,就会用到堆栈,但是堆栈还没初始化,在一开始就不能调用C语言的程序,于是,这个时候就只能使用汇编语言的程序了。
以下是一个在算S3C2440上执行的最简单的启动代码:



这就是一个简单的启动代码了,在设置完栈后,跳到了一个名叫primula的函数,这个函数就相当于是我们的入口函数,就是一般用的那个main函数啦,再来看看C语言的程序:



#define rGPBCON (*(volatile unsigned long*)0x56000010)
#define rGPBDAT (*(volatile unsigned long*)0x56000014)

void primula()
{
    rGPBCON = 0x00015400;
    rGPBDAT = 0x00000140;  
}


这里面就只有两个关于寄存器的宏定义,和一个primula函数,将这两个文件编译连接后,就可以把生成的二进制文件下载到flash或者ram中运行试试了,在我的开发板上,led接在了GPIOB的5678端口上,这个primula函数中通过设置相关寄存器点亮了两个LED,但这个点led不是重点。重点是没有main函数但确实执行了primula这个函数并且把我的两个灯点亮了。没有main函数,没有include。

关于编译,可以写个makefile脚本(linux下)
我的脚本是这样写的:


c_led.bin : startup.s  led.c
    arm-linux-gcc -nostdlib -g -c -o startup.o startup.s
    arm-linux-gcc -nostdlib -g -c -o led.o led.c
    arm-linux-ld -Ttext 0x00000000 -g startup.o led.o -o led_elf
    arm-linux-objcopy -O binary -S led_elf c_led.bin
clean:
    rm -f c_led.dis c_led.bin led_elf *.o


其中-nostdlib 这个选项是告诉编译器不连接系统标准启动文件和标准库文件,在有的gcc版本下貌似不用加上都不会出问题,但是我的编译器爆出了“undefined reference to `__aeabi_unwind_cpp”这样的错误,主要原因就是因为一般的C语言编译器,都会为了能够顺利执行你所写的C语言函数,而在你的程序前面加上一段代码,使得你的main函数会在运行时被自动调用,但是在这里,我们有自己的启动代码,去调用自己的函数,程序里连个main都木有!
+50
科创币
乖乖乖
2012/10/25
高科技
+1
科创币
布布卡
2013/08/15
汇编我一点也不懂。。
13年7个月前IP:未同步
533427
1楼
受教,谢了!
引用
评论
13年7个月前IP:未同步
533640
2楼
这个就是所谓bootloader吗?
引用
评论
13年7个月前IP:未同步
533641
3楼
LZ来尝试下汇编吧,Cortex-M3的指令集很有意思的.我努力了很久还是没用汇编点亮LED.
还有LZ不用初始化时钟中断向量表之类的东西的?
引用
评论
13年6个月前IP:未同步
536704
4楼
Makefile为什么要这样写?有特殊的要求吗?
通常不是这样写吗?

c_led.bin : led_elf
    arm-linux-objcopy -O binary -S led_elf c_led.bin

led_elf : startup.o led.o
    arm-linux-ld -Ttext 0x00000000 -g startup.o led.o -o led_elf

startup.o : startup.s
    arm-linux-gcc -nostdlib -g -c -o startup.o startup.s

led.o : led.c
    arm-linux-gcc -nostdlib -g -c -o led.o led.c

clean:
    -rm -f c_led.dis c_led.bin led_elf *.o
引用
评论
13年4个月前IP:未同步
560477
5楼
跟卫东山ARM好像哦。。。。。。。。。。。。
引用
评论
13年4个月前IP:未同步
560512
6楼
和函数名关联不大,函数名只是告诉编译器的link在连接的时候将obj的动态指针表定位,对于mcu来说更简单,首字节加一个跳转,跳到哪个函数就是哪个函数执行
引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的