本篇主要讲述单片机的FLASH内存映射,首先来看一下FLASH的内存映射表:
上图中启动地址是0x0000,也就是单片机上电后PC指向的这个位置,我们把需要执行的程序的首地址放在其中,对应的汇编语言如下图所示:
0地址去执行内部RAM的初始化工作,后面的代码控制寄存器指挥PC去取地址,以此来决定程序的运行!
在这里大家有没有觉得很奇怪:51单片机里面为什么没有时钟的初始化和中断向量表的初始化呢?其实原因很简单, 51单片机对12M晶振的时钟进行12分频,它就是固定的1M,因此就不需要设置时钟了。而STM32就不一样了,用户可以随意配置单片机的工作时钟。另外还有一个问题,单片机为什么不去中断向量表的注册呢?这是因为51在内部ROM中做了固定的映射,不需要再去重定向,所以在线升级就很难了。而STM32是支持中断向量表重定义的,它是将注册好的中断向量表搬运到SRAM中去,然后NVIC就会对中断作出更加快速的响应,显然51中断向量表在ROM中,中断反应就没有那么迅速了。这就是51单片机不需要做系统时钟的初始化和注册中断向量表的原因。
接下来要做的工作是初始化系统堆栈和可重入函数栈的初始化,下面是51系统堆栈初始化的汇编代码:
注意:汇编里面用;号来表示代码的注释。
栈的大小是可以重新设计的,因为SP初始化的时候它指向的地址是0x07,为了不让它与中断向量表和位寻址区重叠,我们可以将SP设置在30H以后(推荐0x60),即通用RAM区以后最安全。这是因为一般的C编译器是将局部变量放入栈中,而C51是将其放入内部RAM,开辟一片存储空间,多个函数共享该覆盖区。【是不是很怪异呢?】这样我们不得不留出一部分空间给局部变量。其实这并没什么好奇怪的,51只是单纯的想用SP传递参数和保护现场以及恢复现场等功能,这和一些单片机是有区别的,但是也导致了一些问题,各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。
如果一个函数同时被多个函数调用,可能会造成某些变量被冲掉,因此C51中的函数基本上都是不可重入的。也就说明一个问题,51没法调用递归函数,多任务调用同一个函数时会把一些参数覆盖掉。因为他们占用的内存地址是一样的,当然会被冲刷掉啦!!但我们也不必为之烦恼,51为我们提供了如下的解决方案,启动器文件中的可重入栈的设置和关键字reentrant。
上图是设置可重入栈空间的汇编代码,不过该栈是向上增长的,上面代码就是初始化了栈顶而已。
再看下图:
这样,关键字reentrant的使用就可以实现函数的递归调用,上图中的程序就是一个递归程序案例,以此来实现一个数的阶乘运算。
再看看我们的ROM是如何扩展的。
P0是地址总线的低八位,和数据总线共用同一个端口,ALE决定了你输出的是地址还是数据,幸运的是ALE的高低电平CPU会自动控制,不需要我们去操作。PSEN连接EPROM的片选。最后P2端口连接EPROM的高八位地址总线,这样就实现了内部ROM的扩展。
好了,让我们再来回顾总结一下以上内容吧,本篇主要讲述了三个方面的内容:第一,51单片机ROM向量表地址和启动地址。第二,系统栈以及可重入栈的初始化,可重入栈的重要作用(尤其是在可重入函数传递参数方面的使用)。其实这两个方面的内容也可以说是51单片机进入C世界所进行的大部分设置。除此之外,还讲述了51单片机在硬件电路设计上扩展ROM(最大可扩展64K.因为只有16跟地址线)的方式。(完)
相关文章