最简单的c程序(stm32版的helloworld)
最简单的 C 程序(STM32 版的 helloworld)
下面开始讨论如何在STM32上写一个最简单的程序,会谈到程序执行的细节,原理,如何编程,如何编译及链
接我们写的程序,如何通过OpenOCD把程序烧写到STM32芯片内部的Flash 上,如何执行等。
程序的运行方式
开始写之前,先说说最简单的C程序是如何运行的。
为了程序足够简单,我们可以让CPU直接从FLASH 上取指令并执行,而且程序中没有全局变量,因此编译出来
的目标文件中是数据段长度是0 ,这样避免了初始化RAM 的步骤,因为数据段是可读写的,如果目标文件中有数据
段,我们就必须在程序的启动过程中,将数据段复制到RAM 中,才能确保程序的正常工作。
程序的运行环境
我们来定义一下程序执行时的存储器映射(memory map)
FLASH
RAM
0 栈顶
0 0
(0
FLASH 的阴影区域表示保存的程序镜像,程序执行过程中的栈当然只能是在RAM 中,示意图中标出了栈顶指
针。
复位后程序的运行流程
处理器复位后,就会开始“取值->执行“的循环了。因此,PC 寄存器在复位后的值就很关键了。
查查书吧,在《The Definitive Guide To ARM Cortex M3》中,我们可以看到:
在离开复位状态后,Cortex M3 做的第一件事就是读取下列两个32位整数的值:
• 从地址0x0000,0000 处取出MSP 的初始值
• 从地址0x0000,0004 处取出PC 的初始值—这个值是复位向量,LSB 必须是1。然后从这个值所对应的
地址处取值。
以上是ARM 对CortexM3 核定义的行为,那ST 作为芯片的制造商,是如何实现的呢?再来查查ST 的参考手
册吧。
从ST 的Reference Manual 中,我们可以查到STM32 系列处理器的引导模式设置和不同的模式下处理器的
行为。我们所关心的就是最简单的情况,从内置的Flash 启动,也就是BOOT0=0 的情况。
刚才已经提到了,内置的Flash起始地址是0,这岂不是意味着Cortex M3 无法从Flash 中取得
复位后需要的MSP初始值和PC初始值了?STM32 对此的解决方案是地址别名,内置的Flash有两套地址空间,
除了能从0访问外,从别名(从0开始)也能访问。
基于以上的分析,我们来总结一下我们程序的image 该存放哪些信息。
• MSP (栈顶指针)初始值
• PC初始值(LSB 必须为1)
• 程序的代码段,数据段等
下面的代码就能达到我们的目的
__asm__(".word 0);
__asm__(".word main");
main()
{
在以上代码中,我们指定MSP为0,即栈大小为(000x1000),
对我们这么小的程序而言,4k 大小的栈应该绰绰有余了。需要指出的是,GNU 的tool chain 能自动帮我们处
好PC初始值LSB 必须为1 的问题,我们只要保证“main”是一个C 函数就行。其实,这个跟我们写PC上的程序
还是有点区别的,函数名字其实是可以任意起的,这里起为“main”其实也是为了方便理解起见。毕竟 ,我们没有
利用tool chain 的启动代码,也就没有必要按tool chain 的要求来命名主函数了。
接下来,就该分析实现功能的程序怎么干活的了。
程序都干了些啥
程序再简单,也不能没有输出啊。可是在嵌入式系统中,可没有屏幕输出helloworld ,因此我们只能落入
套,来玩点灯游戏吧。
为了点灯 ,程序需要操作那些外设呢?操作这些外设需要读写那些寄存器呢?
由于点灯程序“过于”简单,我们只需要操作STM32 的GPIO 就可达到目的。对GPIO 的操作涉及到的几个寄存器
我们可以查阅ST提供的使用手册得到具体信息,还要结合自己开发板的硬件电路来确定寄存器的值。在本人的开
发板上,D 号