本帖最后由 liuyusheng86 于 2016-9-8 20:54 编辑
以前还以为自己单片机学得不错,没想到想深入学习的时候,才发现自己的基础是那么的薄弱,好了,整理一下今天一下午的成果吧! 先说下起因,我的本意是想深入的学习一下单片机技术,不在是写一些普通的代码,所以就看了一些关于在51上跑操作系统的书,这一看不要紧,头都是大的,什么ram不够,什么sp,我都模糊的记得,但是都说不上来~~~~~今天好好的看了一下午 终于有眉目了。。。。 -------------------------------------------------------------------------------内部存储------------------------------------------------------------------------------------------- 单片机按存储结构可分为二类:一类是哈佛结构,另一类是普林斯顿结构。
①哈佛结构
所谓哈佛结构是指程序存储器地址空间与数据存储器地址空间分开的单片机结构,如80C51单片机采用哈佛
结构,所以80C51单片机的程序存储器地址空间与数据存储器地址空间是分开的,各有64K存储空间。
②普林斯顿结构
所谓普林斯顿结构是指程序存储器地址空间与数据存储器地址空间合并的单片机结构,如MCS96单片机采用
普林斯顿结构,所以MCS96单片机的程序存储器地址空间与数据存储器地址空间是合并的,共有64K存储空间。 下面就贴个图: 右边的这个就是ROM(EEPROM,FLASH----混合了EPROM和EEPROM技术,比如U盘)了,也就是程序存储器。 大小是4K,地址从0000H到0FFFH ,程序计数器PC用于告诉单片机下一条指令的执行地址,所以PC也是16位的, 由于pc的缘故,即使扩展ROM,最大也只能到FFFFH,即64k。当单片机EA引脚接高电平,则访问片内ROM,当 PC的值是0FFFH的时候,在加1,就是1000H了,则自动转向片外ROM,不用人工自动。 当EA接低电平的时候, 直接访问外部ROM,除了程序代码存在这里,还可以在keil中用关键字code定义常数表,使其存在ROM中,减小 RAM的负担,访问时用MOVC A,@A+DPTR 其中的C即为code的意思。 注意:PC的值不能直接修改,但是可以使用跳转指令来改变PC的值。如LJMP 1234H,则PC=1234H。
左边的才是重头戏:RAM,也叫数据存储器,单片机上要运行操作系统,它是第一个不同意的,因为整个RAM的大小 才有可怜的256Byte,没错,连K都没上,连512都木有啊。。。。这里可以时候DPTR来访问,DPTR是16位的,所以, 如果扩展RAM的话,最大也只支持64k,想想都好~~~~~下面我们看一下各部分: 00H~1FH:这里是工作寄存器区,每组都包括R0~R7 8个,共4组,地址是00H~07H,、、、,18H~1FH。 20H~2FH:这里是位可寻址区,有128位,可以位寻址,操作时并不直接操作这16字节地址,而是通过地址映射来使操作 简单化,也就是说每一位都有自己的映射地址。从00H到7FH,使用位操作指令就可以直接操作了,比如SBIT 20H。这个区也可以当做一般的16字节来用,比如 MOV 22H,#80H 这里的22H不是映射地址,而是真实 地址。 30F~7FH:用户开放区~随心所欲的用吧~~~~~~虽然很少,有总比没有好~ 80F~FFH:SFR,虽然这些地址上并是不是所有的地址都被寄存器占用了,但是没占用的地址可能硬件上根本就没有,一般 来说,不应该把未占用四肢当成开放区来访问,更不可能向其内部写数据,SFR中有好多寄存器都支持位操作。 所以,给用户用的RAM只有20H~7FH,总共才96个字节,我滴天。。。。还能再少点吗?为什么这么说?RAM有什么重要 吗?当然很重要,SP你总知道吧,堆栈的设置就是在RAM中,在RTOS中,任务越多,占用的RAM越多,所以要想尽 一切办法解决RAM。复位时SP=07H,一般都要吧SP移动到用户开放区里,如MOV SP,#50H。
MOVX 访问片外存储器,ROM和RAM都可以 MOVC 查表指令 只能访问ROM 因为表在ROM里~~~~~ 这里还有一个小例子: #include <reg52.h> // 引用52包文件 sbit P0_0 = P0^0; // 定义P0第0个管脚 sbit P0_1 = 0x81; // 定义P0第1个管脚 sbit P0_2 = 0x80^2; // 定义P0第2个管脚
void main() { P0_0 = 0; // 点亮P0第1管脚连接的LED P0_1 = 0; P0_2 = 0; }
为什么用三种方式设置引脚都可以呢,这是因为P0口可以按位寻址
sbit关键字指定了后面的变量是要位寻址,赋值P0_1 = 0x81,系统会找到寄存器地址0x80,然后再找对应的位
在reg52.h中定义了 sfr SP = 0x81;那么,为什么代码中sbit P0_1 = 0x81和sfr SP = 0x81不冲突呢,这不是
sfr和sbit关键字的区别了,系统会自动根据不同的关键字处理不同的操作,举个简单的例子,你和邻居家都有8个柜子, 你家的门牌号是0x80,邻居家的是0x81,sbit P0_1 = 0x81就好比是先找到你家的地址0x80,再找到你家的第一个柜子的 位置,即0x80 + 0x01 = 0x81,而sfr SP = 0x81则仅仅是只到邻居家的地址; 有了上面的理解,我们可以在代码里验证一下,当增加代码sbit SP_0 = SP^0; 后编译 提示 light.c(8): error C146:'SP': invalid
base address,根本就编译不过去,也就是说,系统不可位寻址区是不能按位访问的: #include <reg52.h> // 引用52包文件 sbit P0_0 = P0^0; // 定义P0管脚1 sbit P0_1 = 0x81; // reg52.h中定义了 sfr SP = 0x81; sbit P0_2 = 0x80^2; sbit TCON_0 = TCON^0; sbit TCON_1 = 0x89; //sbit SP_0 = SP^0; // 提示 light.c(8): error C146: 'SP': invalid base address
void main() { P0_0 = 0; // 点亮P0第1管脚连接的LED P0_1 = 0; P0_2 = 0; TCON_0 = 0; TCON_1 = 0; SP = 0x81; // 可以给SP直接赋值 这时SP地址中的数据是0x81 }
参照上面的分布图,相信更好理解了~~~~加油!
注意:sbit可以寻址的范围是为可寻址区加上特殊功能寄存器里的可位寻址的地址,由于位可寻址区的映射地址只到7fH,而SFR开始地址是80H,所以不会混的。
-------------------------------------------------------------------------------指令运行-------------------------------------------------------------------------------------------
比如MOV A,32H 这条指令怎么运行的?首先,汇编器会把指令变成执行代码,MOV A指令的执行代码是E5,所以这条 代码最后就会变成E5H 32H存在ROM中的某个位置,当PC指到这里的时候,就会出现下面的流程 首先,当MCU在PC的移动中,读到了E5,则意识到要执行的操作是把下一个执行代码所指向的数据存储地址中的数据载入到 A中。然后MCU往下抓取一个执行代码,即32H,则MCU指到去RAM中取32H地址中的内容。接着读到了88H,最后把88H 装到ACC中,这条语句彻底结束,PC自动加1,接着往下执行ROM中的指令。
|