用机器的视角思考,单片机是怎么执行程序的
P.S. 因为这些工作确实很简单,在网上找份教程看看就会了。
一般刚开始学一种单片机的时候,写的第一个程序都是“点亮第一个LED”。这个程序很经典,它代表你已经成功学会操控单片机的IO端口,学51单片机亦是如此。代码如下(我使用ATMEL 公司的AT89S52):
#include
sbit LED = P1^0 ;
void Delay(unsigned int t)
{
}
void main(void)
{
}
单片机会从main函数开始执行,所以我们把思绪拉到main函数。
一开始用了“
首先,所有的CPU,它们在执行指令的时候都是从程序段的0地址(也就是程序最开始的地方)开始的,而且CPU永远只做两件事情,一是从程序区里取出指令,二是执行这条指令,然后再回去取指令。。
这样说很简单嘛,把main函数的代码从程序区的0地址开始一条条存放不就行了吗。其实不是这样的,一般0地址里存放的都不会是main函数的真实代码,它会放一条跳转指令(就是一条指令后面跟一个地址,告诉CPU要跳到那个地方去工作),这条指令跟着的是main函数的入口地址,把单片机指到main函数真正的地址去执行。为什么要这样做?看下面的图:
程序存储区的固定前面几个地址是要用来存放中断服务程序的地址的(中断?后面会讲,先不管),称之为“中断向量”。程序在执行过程中遇到中断的时候,它就会根据中断信号的类型跳回到这些固定的地址,再由这些地址里面存储的指令指引,跳转到中断服务程序真正开始的地方去执行。所以,最开始的一系列地址是不能存放另的东西的,不然程序会乱掉。如果你把main函数定义在这里,没遇到中断之前当然可以正常运行,但如果你在程序中使用了中断,后果就不堪设想了。总而言之,main不是放在最开始的地方的!
那它放在哪里?理论上只要避开了中断向量地址的冲突,你可以放在任何地方,你用C语言编写的时候编译器会自动处理这个问题,不用你操心,如果以后你要用汇编写了,你就必须自己定义main函数的地址了。
好了,那我们就进入main函数里面看看吧。
根据这段代码,你觉得第一句应该执行的语句应该是“
错了,进入main函数之后首先要做的事情是初始化相关寄存器。因为芯片刚开始工作的时候,寄存器都处于一种未知的状态,你必须首先赋给它一个初值才行,在这个main函数中没有使用变量,所以可以不用初始化内存区,但至少CPU必须初始化一个很重要的寄存器:SP堆栈指针。关于这家伙以后再讲,总之就是先初始化。
初始化完毕之后才开始执行你的真正的代码,先让LED设置为1,然后进入一个循环结构,调用延时函数Delay,等待它执行完毕之后再回来把LED取反,然后返回继续周而复始地执行。
等等,有一个很重要的问题:单片机是怎么延时的?在延时的时间里它都在干什么?
我们知道,单片机的一个主要性能就是执行速度,也就是一秒钟能执行多少条指令,一般速度越快代表性能越好,比如51单片机如果你在外面给它接上一个12M Hz的晶振,它的CPU就会以一百万条/秒的速度工作,即是说它执行一条指令要花费一百万分之一秒的时候即一微秒(是不是觉得很快?其实以现在的标准来说已经算很慢了,慢得像乌龟)。按照这个道理,如果我们想要让单片机延时,比如延时1ms,我们可以让CPU空转1000次,因为CPU空转一次也需要一条指令的时间。让它转上1000次之后结束,那么就相当于它延迟了1ms,延时函数就是这样写出来的,工作原理如果下图:
这就是Delay函数的作用,但是这样的延时不能做到很精确,因为在这段时间里CPU要执行判断、赋值等等一系列指令,在C语言写的代码里面你不能准确预测到编译器会把你这段代码转换为怎么样的汇编指令,所以你也就无法计算出精确的变量值,只能在一个大概的范围里面选龋虽然C语言写起程序来很方便,但同时它的缺点也显现在这里:无法控制编译器写出精确的延迟函数,在有些对时间要求很高
单片机执行程 相关文章:
- 单片机执行程序过程(11-13)
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)