《例说51单片机(C语言版)(第3版)》一1.3 认识MCS-51的存储器结构
本节书摘来自异步社区《例说51单片机(C语言版)(第3版)》一书中的第1章,第1.3节,作者 张义和 , 王敏男 , 许宏昌 , 余春长,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.3 认识MCS-51的存储器结构
例说51单片机(C语言版)(第3版)除了无ROM型的8031及8032外,MCS-51的存储器包括程序存储器(ROM)与数据存储器(RAM)两部分,一般地这两部分是独立的个体。标准的8x51系列具有4KB程序存储器、128B数据存储器,而标准的8x52系列具有8KB、256B数据存储器,刚好是8x51系列的两倍。不管是8x51、8031、8032或8x52,其外部扩展的程序存储器或数据存储器最多为64KB。
虽然MCS-51的兼容单片机都扩展了其内部程序存储器与数据存储器,例如Atmel半导体公司的TS83C51RB2,其内部有16KB程序存储器、256B数据存储器;TS83C51RC2,其内部有32KB程序存储器、256B数据存储器;TS83C51RD2,其内部有64KB程序存储器、768KB数据存储器。尽管如此,在此仍探讨MCS-51单片机微控制器的标准存储器结构。
1.3.1 程序存储器
顾名思义,程序存储器(ROM)是存放程序的位置,而CPU将自动从程序存储器中读取所要执行的指令码。MCS-51可选择使用内部程序存储器或外部程序存储器(如图1-18所示),说明如下。
若使用8031或8032,由于内部没有程序存储器,一定要使用外部程序存储器,所以其overline {{text{EA}}} 引脚必须接地。
当overline {{text{EA}}} 引脚接高电平时,CPU将使用内部程序存储器,若程序超过4KB(8x51)或8KB(8x52)时,CPU会自动从外部程序存储器里,读取超过部分的程序代码。
当overline {{text{EA}}} 引脚接地时,CPU将自外部程序存储器读取所要执行的指令码,而CPU内部的程序存储器形同虚设。
说明:
4KB的程序存储器对于初学者而言,已是绰绰有余。坏掉的8x51/8x52,很可能是其中的程序存储器坏掉,可将其overline {{text{EA}}}引脚接地,改为外接程序存储器,即可当作8031/8032使用。当CPU复位后,程序将从程序存储器0000H地址位置开始执行,如没有遇到跳转指令,则按程序存储器地址顺序执行。当然,程序存储器前面几个位置还有一些玄机,留待中断的单元再详细说明。
图1-18 MCS-51的程序存储器结构
1.3.2 数据存储器
MCS-51的程序存储器与数据存储器是分开的独立区块,所以存取数据存储器时,所使用的地址并不会与程序存储器冲突。相对于程序存储器而言,数据存储器就不那么简单,如图1-19所示。
图1-19 MCS-51的数据存储器结构
除了内部数据存储器外,8x51的数据存储器还可扩展外部数据存储器,这两部分的数据存储器可以并存。不过,存取数据存储器时,所采用的指令并不一样,例如,存取内部数据存储器时,可用MOV指令,但存取外部数据存储器时,则使用MOVX指令。
另外,内部数据存储器中,从0000H到007FH之间的128B为可直接寻址或间接寻址的存储器。在编写C语言程序时,以数据类型来区别直接寻址与间接寻址。在这一区间的数据存储器又可分成三部分(如图1-20所示),说明如下。
1.寄存器组区
0000H到001FH的32个地址为寄存器组(Register Bank)区,说明如下。
(1)0000H到0007H为寄存器组0(即RB0),0008H到000FH为寄存器组1(即RB1),0010H到0017H为寄存器组2(即RB2),0018H到001FH为寄存器组3(即RB3)。
图1-20 内部数据存储器
(2)每组寄存器组都包含R0~R7共8个寄存器,而任一时刻只能使用其中一组寄存 器组。
(3)寄存器组的切换可以用程序状态字寄存器(Program Status Word,PSW)中的RS1与RS0来决定,如表1-4所示。
(4)当CPU复位时,系统的堆栈指针(SP)指向07H地址,所以数据存入堆栈时,将从08H开始,也就是RB1里的R0地址。为避免冲突或不必要的错误,通常会把堆栈指针移到30H以后的地址。
2.可位寻址区
0020H到002FH的16个字节存储器区为可位寻址区。通常存取存储器是以字节为单位,“可位寻址”则是指定存取1个位(bit)。在8051的汇编语言里,可使用布尔运算指令进行位操作,例如,要把20H存储器地址的bit 5设置为1,则可使用下列指令:
SETB 20H.5
另外,从0020H到002FH的16个字节总共128个位(16×8),也可以直接指定为0到127,以刚才的20H存储器地址的bit 5而言,也可将“20H.5”指定为“05”,指令如下:
SETB 05
同理,若要将25H存储器地址的bit 2清除为0,则可使用下列指令:
CLR 25H.2
或
CLR 42
其中,42=58+2。
3.一般数据与堆栈区
0030H到007FH的80个字节地址为一般数据存取及堆栈区。由于CPU复位后,堆栈指针指向07H位置,为了确保数据的安全与程序执行的正确,如果在程序之中使用了PUSH、POP命令,最好能把堆栈指针改至本区,例如,要将堆栈指针移至0030H地址,则在程序开始处即使用如下命令:
MOV SP, #30H
从0080H到00FFH之间的128B为特殊功能寄存器(Special Function Register,SFR)或可直接寻址的存储器,至于“特殊功能寄存器”,稍后再详细说明。
如果是8052/8032,则0080H到00FFH之间的128B除了是特殊功能寄存器或可直接寻址的存储器外,另外也可以使用间接寻址的方式存取与这特殊功能寄存器位置重叠但为独立的存储器。
1.3.3 特殊功能寄存器
在MCS-51里,寄存器只是CPU里特定地址的数据存储器而已。而在0080H到00FFH之间的128B,正是特殊功能寄存器(Special Function Register,SFR)所在位置。特殊功能寄存器就是8x51/52内部的结构,若以汇编语言编写程序时,必须熟练掌握这些寄存器,若以C语言编写程序,就不是那么重要。其位置的声明放置在Keil C所提供的“reg51.h”头文件(详见后面章节)里,只要把它包含到程序里即可,而不必记忆这些位置。以下简单介绍这些寄存器(如表1-5所示),仅供参考。
注:(1)本表C8-CF行部分为8052/8032才有的寄存器,本表第1列的部分为可位寻址的寄存器,较深灰底的部分为89S51/52才有的。
(2)8051/52、89C51/52只有一组数据指针寄存器,所以其中的DP0L应改为DPL,DP0H应改为DPH。
P0、P1、P2、P3P0~P3为MCS-51的4个输入/输出端口,其地址分别为80H、90H、0A0H及0B0H,待第3章再详细介绍。
SPSP为堆栈指针寄存器(Stack Pointer register),其地址为81H。堆栈是一种特殊的数据存储方式,其数据的操作顺序是先进后出(First In Last Out,FILO),当数据以PUSH命令送入堆栈时,SP自动减1;若以POP命令从堆栈取出数据时,SP自动加1。
DPL、DPH89C51只有一组16位的数据指针寄存器(Data Pointer register,DPTR),这组数据指针寄存器是由DPL与DPH两个8位的数据指针寄存器组成,其地址分别为82H、83H。若以DPL为低8位、DPH为高8位,所组成的16位数据指针寄存器将可寻址到64KB的数据地址。89S51有两组16位数据指针寄存器,分别是DP0L、DP0H、DP1L及DP1H,其地址分别为82H、83H、84H、85H。若以汇编语言编写程序时,DPTR是查表法的必备寄存器。不过,使用C语言编写程序时,就不太需要由我们直接控制这个寄存器。
PCONPCON为电源控制寄存器(Power Control register),其地址为87H,其功能是设置CPU的电源方式,待后续7-3-3节再行说明。
TCONTCON为定时器/计数器控制寄存器(Timer/Counter Control register),其地址为88H,其功能是设置定时器/计数器的启动,记录定时/计数溢出及外部中断的类型等(见第6章),待后续关于定时器/计数器部分(第7章),再行说明。
TMODTMOD为定时器/计数器方式控制寄存器(Timer/Counter Mode Control register),其地址为089H,其功能是设置定时/计数的方式,待后续关于定时器/计数器部分(第7章)再行说明。
TL0、TL1、TH0、TH1TL0、TH0为第一组定时器/计数器(Timer0)的计数器,其地址为8AH、8CH,将TH0与TL0组合即可进行16位的定时/计数。TL1、TH1为第二组定时器/计数器(Timer1)的计数器,其地址为8BH、8DH,将TH1与TL1组合即可进行16位的定时/计数,待后续关于定时器/计数器部分(第7章)再行说明。 SCONSCON为串行口控制寄存器(Serial port Control register),其地址为98H,其功能是设置串行口工作方式与标志,待后续关于串行口部分(第8章)再行说明。
SBUFSBUF为串行口缓冲器(Serial BUFfer),其地址为99H,它由使用同一个地址的两个寄存器所构成,其中一个寄存器作为发送数据用的缓冲器,另一个寄存器作为接收数据用的缓冲器。至于如何分辨同一个地址的两个寄存器,视指令而定,若是数据发送的指令,则自动定位到发送数据用的缓冲器;若是接收数据的指令,则自动定位到接收数据用的缓冲器,待后续关于串行口部分(第8章)再行说明。
IEIE为中断使能寄存器(Interrupt Enable register),其地址为0A8H,其功能是启用中断功能,待后续关于中断部分(第6章)再行说明。
IPIP为中断优先等级寄存器(Interrupt Priority register),其地址为0B8H,其功能是设置中断的优先等级,待后续关于中断部分(第6章)再行说明。
T2CONT2CON为Timer2的定时器/计数器控制寄存器,其地址为0C8H,其功能是设置Timer2的启动、记录定时/计数溢出,以及外部中断的类型等,而Timer2只在8052/8032中才有。
RCAP2L、RCAP2HRCAP2L、RCAP2H为捕捉寄存器(Capture register),其地址为0CAH、0CBH。当Timer2在捕捉方式时,若T2EX(P1.1)引脚上的输入信号由高电平跳变为低电平,TL2与TH2的内容将被载入RCAP2L与RCAP2H里,就像是把Timer2的内容“捉进”RCAP寄存器一样。
TL2、TH2TL2、TH2为第三组定时器/计数器(Timer2)的计数器,其地址为0CCH、0CDH,将TH2与TL2组合即可进行16位的定时/计数。
PSWPSW为CPU的程序状态字组寄存器(Program Status Word register),其地址为0D0H,其内容说明如下。
PSW.7:本位为进位标志(CY),进行加法(减法)运算时,若最左边位(MSB,即bit 7)产生进位(借位)时,则本位将自动设置为1,即CY=1;否则CY=0。PSW.6:本位为辅助进位标志(AC),进行加法(减法)运算时,若bit 3产生进位(借位)时,则本位将自动设置为1,即AC=1;否则AC=0。PSW.5:本位为用户标志(F0),可由用户自行设置的位。PSW.4与PSW.3:这两个位为寄存器组选择位(RS1、RS0),其功能如表1-4所示。PSW.2:本位为溢出标志(OV),当进行算术运算时,若发生溢出,则OV=1;否则 OV=0。PSW.1:本位为保留位,没有提供服务。PSW.0:本位为校验标志(P),8051采用偶校验,若ACC里有奇数个1,则P=1;若ACC里有偶数个1,则P=0。ACCACC累加器(Accumulator)又称为A寄存器,其地址为0E0H,这个寄存器提供CPU主要运行的位置,可说是最常用的寄存器。 BB寄存器的地址为0F0H,主要功能是配合A寄存器进行乘法或除法运算,进行乘法运算时,乘数放在B寄存器,而运算结果的高8位放在B寄存器;进行除法运算时,除数放在B寄存器,而运算结果的余数放在B寄存器。若不进行乘/除法运算,B寄存器也可当成一般寄存器使用。
AUXRAUXR寄存器为89S51新增的辅助寄存器(AUXiliary Register),其地址为8EH,其内容说明如下。
WDIDLE:本位设置在待机方式(Idle Mode)下,是否启用看门狗。若本位设置为1,则在Idle方式下将启用看门狗;若本位设置为0,则在Idle方式下将停用看门狗。DISRTO:本位设置是否输出复位信号,若本位设置为1,则Reset引脚(第9脚)只有输入功能;若本位设置为0,则在WDT计数完毕后,Reset引脚输出复位信号(即高电平脉冲)。DISALE:本位设置是否启用ALE信号,若本位设置为1,则只有在执行MOVX指令或MOVC指令时,ALE引脚(第30脚)才会正常工作;若本位设置为0,则固定每6个脉冲就输出1个高电平脉冲,稍后说明。其他位为保留位。当然,这个寄存器只有在89S51里才有作用。
AUXR1AUXR1寄存器为89S51新增的第2个辅助寄存器,其地址为0A2H,其内容说明如下:
DPS:本位的功能是选择数据指针寄存器。若本位设置为1,则使用DP1L及DP1H;若本位设置为0,则使用DP0L及DP0H。其他位为保留位。同样,这个寄存器只有在89S51里才有作用。
WDTRSTWDTRST寄存器为89S51新增的看门狗定时器复位寄存器(WatchdogTimer Reset register),其地址为0A6H。当要启用看门狗定时器WDT时,依序将01EH、0E1H放入WDTRST寄存器,当14位计数器溢出(达到16383,即3FFFH),即由RESET引脚送出一个高电平脉冲以复位系统。此脉冲的宽度为98 times T_text{OSC},其中T_text{OSC} = 1/F_text{OSC} ,以12MHz的时钟脉冲为例,脉冲的宽度为98 times frac{1}{{12 times 10^6 }} cong 8.167mtext{u} s,关于看门狗与节电方式,待后续相关单元,再行说明。
文章
C语言 · .NET · 开发框架 · 安全 · 存储
2017-05-02
《例说51单片机(C语言版)(第3版)》——1-3 认识MCS-51的存储器结构
本节书摘来自异步社区《例说51单片机(C语言版)(第3版)》一书中的第1章,第1-3节,作者 张义和,王敏男,许宏昌,余春长,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1-3 认识MCS-51的存储器结构
除了无ROM型的8031及8032外,MCS-51的存储器包括程序存储器(ROM)与数据存储器(RAM)两部分,一般地这两部分是独立的个体。标准的8x51系列具有4KB程序存储器、128B数据存储器,而标准的8x52系列具有8KB、256B数据存储器,刚好是8x51系列的两倍。不管是8x51、8031、8032或8x52,其外部扩展的程序存储器或数据存储器最多为64KB。
虽然MCS-51的兼容单片机都扩展了其内部程序存储器与数据存储器,例如Atmel半导体公司的TS83C51RB2,其内部有16KB程序存储器、256B数据存储器;TS83C51RC2,其内部有32KB程序存储器、256B数据存储器;TS83C51RD2,其内部有64KB程序存储器、768KB数据存储器。尽管如此,在此仍探讨MCS-51单片机微控制器的标准存储器结构。
1-3-1 程序存储器
顾名思义,程序存储器(ROM)是存放程序的位置,而CPU将自动从程序存储器中读取所要执行的指令码。MCS-51可选择使用内部程序存储器或外部程序存储器(如图1-18所示),说明如下。
说明:
4KB的程序存储器对于初学者而言,已是绰绰有余。坏掉的8x51/8x52,很可能是其中的程序存储器坏掉,可将其\overline {{\text{EA}}}引脚接地,改为外接程序存储器,即可当作8031/8032使用。
当CPU复位后,程序将从程序存储器0000H地址位置开始执行,如没有遇到跳转指令,则按程序存储器地址顺序执行。当然,程序存储器前面几个位置还有一些玄机,留待中断的单元再详细说明。
1-3-2 数据存储器
MCS-51的程序存储器与数据存储器是分开的独立区块,所以存取数据存储器时,所使用的地址并不会与程序存储器冲突。相对于程序存储器而言,数据存储器就不那么简单,如图1-19所示。
除了内部数据存储器外,8x51的数据存储器还可扩展外部数据存储器,这两部分的数据存储器可以并存。不过,存取数据存储器时,所采用的指令并不一样,例如,存取内部数据存储器时,可用MOV指令,但存取外部数据存储器时,则使用MOVX指令。
另外,内部数据存储器中,从0000H到007FH之间的128B为可直接寻址或间接寻址的存储器。在编写C语言程序时,以数据类型来区别直接寻址与间接寻址。在这一区间的数据存储器又可分成三部分(如图1-20所示),说明如下。
1.寄存器组区
0000H到001FH的32个地址为寄存器组(Register Bank)区,说明如下。
(1)0000H到0007H为寄存器组0(即RB0),0008H到000FH为寄存器组1(即RB1),0010H到0017H为寄存器组2(即RB2),0018H到001FH为寄存器组3(即RB3)。
(2)每组寄存器组都包含R0~R7共8个寄存器,而任一时刻只能使用其中一组寄存 器组。
(3)寄存器组的切换可以用程序状态字寄存器(Program Status Word,PSW)中的RS1与RS0来决定,如表1-4所示。
(4)当CPU复位时,系统的堆栈指针(SP)指向07H地址,所以数据存入堆栈时,将从08H开始,也就是RB1里的R0地址。为避免冲突或不必要的错误,通常会把堆栈指针移到30H以后的地址。
2.可位寻址区
0020H到002FH的16个字节存储器区为可位寻址区。通常存取存储器是以字节为单位,“可位寻址”则是指定存取1个位(bit)。在8051的汇编语言里,可使用布尔运算指令进行位操作,例如,要把20H存储器地址的bit 5设置为1,则可使用下列指令:
SETB 20H.5
另外,从0020H到002FH的16个字节总共128个位(16×8),也可以直接指定为0到127,以刚才的20H存储器地址的bit 5而言,也可将“20H.5”指定为“05”,指令如下:
SETB 05
同理,若要将25H存储器地址的bit 2清除为0,则可使用下列指令:
CLR 25H.2
或
CLR 42
其中,42=58+2。
3.一般数据与堆栈区
0030H到007FH的80个字节地址为一般数据存取及堆栈区。由于CPU复位后,堆栈指针指向07H位置,为了确保数据的安全与程序执行的正确,如果在程序之中使用了PUSH、POP命令,最好能把堆栈指针改至本区,例如,要将堆栈指针移至0030H地址,则在程序开始处即使用如下命令:
MOV SP, #30H
从0080H到00FFH之间的128B为特殊功能寄存器(Special Function Register,SFR)或可直接寻址的存储器,至于“特殊功能寄存器”,稍后再详细说明。
如果是8052/8032,则0080H到00FFH之间的128B除了是特殊功能寄存器或可直接寻址的存储器外,另外也可以使用间接寻址的方式存取与这特殊功能寄存器位置重叠但为独立的存储器。
1-3-3 特殊功能寄存器
在MCS-51里,寄存器只是CPU里特定地址的数据存储器而已。而在0080H到00FFH之间的128B,正是特殊功能寄存器(Special Function Register,SFR)所在位置。特殊功能寄存器就是8x51/52内部的结构,若以汇编语言编写程序时,必须熟练掌握这些寄存器,若以C语言编写程序,就不是那么重要。其位置的声明放置在Keil C所提供的“reg51.h”头文件(详见后面章节)里,只要把它包含到程序里即可,而不必记忆这些位置。以下简单介绍这些寄存器(如表1-5所示),仅供参考。
注:(1)本表C8-CF行部分为8052/8032才有的寄存器,本表第1列的部分为可位寻址的寄存器,较深灰底的部分为89S51/52才有的。
(2)8051/52、89C51/52只有一组数据指针寄存器,所以其中的DP0L应改为DPL,DP0H应改为DPH。
P0、P1、P2、P3
P0~P3为MCS-51的4个输入/输出端口,其地址分别为80H、90H、0A0H及0B0H,待第3章再详细介绍。
SP
SP为堆栈指针寄存器(Stack Pointer register),其地址为81H。堆栈是一种特殊的数据存储方式,其数据的操作顺序是先进后出(First In Last Out,FILO),当数据以PUSH命令送入堆栈时,SP自动减1;若以POP命令从堆栈取出数据时,SP自动加1。
DPL、DPH
89C51只有一组16位的数据指针寄存器(Data Pointer register,DPTR),这组数据指针寄存器是由DPL与DPH两个8位的数据指针寄存器组成,其地址分别为82H、83H。若以DPL为低8位、DPH为高8位,所组成的16位数据指针寄存器将可寻址到64KB的数据地址。89S51有两组16位数据指针寄存器,分别是DP0L、DP0H、DP1L及DP1H,其地址分别为82H、83H、84H、85H。若以汇编语言编写程序时,DPTR是查表法的必备寄存器。不过,使用C语言编写程序时,就不太需要由我们直接控制这个寄存器。
PCON
PCON为电源控制寄存器(Power Control register),其地址为87H,其功能是设置CPU的电源方式,待后续7-3-3节再行说明。
TCON
TCON为定时器/计数器控制寄存器(Timer/Counter Control register),其地址为88H,其功能是设置定时器/计数器的启动,记录定时/计数溢出及外部中断的类型等(见第6章),待后续关于定时器/计数器部分(第7章),再行说明。
TMOD
TMOD为定时器/计数器方式控制寄存器(Timer/Counter Mode Control register),其地址为089H,其功能是设置定时/计数的方式,待后续关于定时器/计数器部分(第7章)再行说明。
TL0、TL1、TH0、TH1
TL0、TH0为第一组定时器/计数器(Timer0)的计数器,其地址为8AH、8CH,将TH0与TL0组合即可进行16位的定时/计数。TL1、TH1为第二组定时器/计数器(Timer1)的计数器,其地址为8BH、8DH,将TH1与TL1组合即可进行16位的定时/计数,待后续关于定时器/计数器部分(第7章)再行说明。
SCON
SCON为串行口控制寄存器(Serial port Control register),其地址为98H,其功能是设置串行口工作方式与标志,待后续关于串行口部分(第8章)再行说明。
SBUF
SBUF为串行口缓冲器(Serial BUFfer),其地址为99H,它由使用同一个地址的两个寄存器所构成,其中一个寄存器作为发送数据用的缓冲器,另一个寄存器作为接收数据用的缓冲器。至于如何分辨同一个地址的两个寄存器,视指令而定,若是数据发送的指令,则自动定位到发送数据用的缓冲器;若是接收数据的指令,则自动定位到接收数据用的缓冲器,待后续关于串行口部分(第8章)再行说明。
IE
IE为中断使能寄存器(Interrupt Enable register),其地址为0A8H,其功能是启用中断功能,待后续关于中断部分(第6章)再行说明。圓 IP
IP为中断优先等级寄存器(Interrupt Priority register),其地址为0B8H,其功能是设置中断的优先等级,待后续关于中断部分(第6章)再行说明。
T2CON
T2CON为Timer2的定时器/计数器控制寄存器,其地址为0C8H,其功能是设置Timer2的启动、记录定时/计数溢出,以及外部中断的类型等,而Timer2只在8052/8032中才有。
RCAP2L、RCAP2H
RCAP2L、RCAP2H为捕捉寄存器(Capture register),其地址为0CAH、0CBH。当Timer2在捕捉方式时,若T2EX(P1.1)引脚上的输入信号由高电平跳变为低电平,TL2与TH2的内容将被载入RCAP2L与RCAP2H里,就像是把Timer2的内容“捉进”RCAP寄存器一样。
TL2、TH2
TL2、TH2为第三组定时器/计数器(Timer2)的计数器,其地址为0CCH、0CDH,将TH2与TL2组合即可进行16位的定时/计数。
PSW
PSW为CPU的程序状态字组寄存器(Program Status Word register),其地址为0D0H,其内容说明如下。
PSW.7:本位为进位标志(CY),进行加法(减法)运算时,若最左边位(MSB,即bit 7)产生进位(借位)时,则本位将自动设置为1,即CY=1;否则CY=0。
PSW.6:本位为辅助进位标志(AC),进行加法(减法)运算时,若bit 3产生进位(借位)时,则本位将自动设置为1,即AC=1;否则AC=0。
PSW.5:本位为用户标志(F0),可由用户自行设置的位。
PSW.4与PSW.3:这两个位为寄存器组选择位(RS1、RS0),其功能如表1-4所示。
PSW.2:本位为溢出标志(OV),当进行算术运算时,若发生溢出,则OV=1;否则 OV=0。
PSW.1:本位为保留位,没有提供服务。
PSW.0:本位为校验标志(P),8051采用偶校验,若ACC里有奇数个1,则P=1;若ACC里有偶数个1,则P=0。
ACC
ACC累加器(Accumulator)又称为A寄存器,其地址为0E0H,这个寄存器提供CPU主要运行的位置,可说是最常用的寄存器。
B
B寄存器的地址为0F0H,主要功能是配合A寄存器进行乘法或除法运算,进行乘法运算时,乘数放在B寄存器,而运算结果的高8位放在B寄存器;进行除法运算时,除数放在B寄存器,而运算结果的余数放在B寄存器。若不进行乘/除法运算,B寄存器也可当成一般寄存器使用。
AUXR
AUXR寄存器为89S51新增的辅助寄存器(AUXiliary Register),其地址为8EH,其内容说明如下。
WDIDLE:本位设置在待机方式(Idle Mode)下,是否启用看门狗。若本位设置为1,则在Idle方式下将启用看门狗;若本位设置为0,则在Idle方式下将停用看门狗。
DISRTO:本位设置是否输出复位信号,若本位设置为1,则Reset引脚(第9脚)只有输入功能;若本位设置为0,则在WDT计数完毕后,Reset引脚输出复位信号(即高电平脉冲)。
DISALE:本位设置是否启用ALE信号,若本位设置为1,则只有在执行MOVX指令或MOVC指令时,ALE引脚(第30脚)才会正常工作;若本位设置为0,则固定每6个脉冲就输出1个高电平脉冲,稍后说明。
其他位为保留位。当然,这个寄存器只有在89S51里才有作用。
AUXR1
AUXR1寄存器为89S51新增的第2个辅助寄存器,其地址为0A2H,其内容说明如下:
DPS:本位的功能是选择数据指针寄存器。若本位设置为1,则使用DP1L及DP1H;若本位设置为0,则使用DP0L及DP0H。
其他位为保留位。同样,这个寄存器只有在89S51里才有作用。
WDTRST
文章
C语言 · .NET · 开发框架 · 安全 · 存储
2017-05-02
《例说51单片机(C语言版)(第3版)》——1-3 认识MCS-51的存储器结构
本节书摘来异步社区《例说51单片机(C语言版)(第3版)》一书中的第1章,第1.3节,作者:张义和,王敏男,许宏昌,余春长,更多章节内容可以访问云栖社区“异步社区”公众号查看
1-3 认识MCS-51的存储器结构
例说51单片机(C语言版)(第3版)除了无ROM型的8031及8032外,MCS-51的存储器包括程序存储器(ROM)与数据存储器(RAM)两部分,一般地这两部分是独立的个体。标准的8x51系列具有4KB程序存储器、128B数据存储器,而标准的8x52系列具有8KB、256B数据存储器,刚好是8x51系列的两倍。不管是8x51、8031、8032或8x52,其外部扩展的程序存储器或数据存储器最多为64KB。
虽然MCS-51的兼容单片机都扩展了其内部程序存储器与数据存储器,例如Atmel半导体公司的TS83C51RB2,其内部有16KB程序存储器、256B数据存储器;TS83C51RC2,其内部有32KB程序存储器、256B数据存储器;TS83C51RD2,其内部有64KB程序存储器、768KB数据存储器。尽管如此,在此仍探讨MCS-51单片机微控制器的标准存储器结构。
1-3-1 程序存储器
顾名思义,程序存储器(ROM)是存放程序的位置,而CPU将自动从程序存储器中读取所要执行的指令码。MCS-51可选择使用内部程序存储器或外部程序存储器(如图1-18所示),说明如下。
三角形 若使用8031或8032,由于内部没有程序存储器,一定要使用外部程序存储器,所以其overline {{text{EA}}} 引脚必须接地。
三角形 当overline {{text{EA}}} 引脚接高电平时,CPU将使用内部程序存储器,若程序超过4KB(8x51)或8KB(8x52)时,CPU会自动从外部程序存储器里,读取超过部分的程序代码。
三角形 当overline {{text{EA}}} 引脚接地时,CPU将自外部程序存储器读取所要执行的指令码,而CPU内部的程序存储器形同虚设。
说明:
4KB的程序存储器对于初学者而言,已是绰绰有余。坏掉的8x51/8x52,很可能是其中的程序存储器坏掉,可将其overline {{text{EA}}}引脚接地,改为外接程序存储器,即可当作8031/8032使用。当CPU复位后,程序将从程序存储器0000H地址位置开始执行,如没有遇到跳转指令,则按程序存储器地址顺序执行。当然,程序存储器前面几个位置还有一些玄机,留待中断的单元再详细说明。
1-3-2 数据存储器
MCS-51的程序存储器与数据存储器是分开的独立区块,所以存取数据存储器时,所使用的地址并不会与程序存储器冲突。相对于程序存储器而言,数据存储器就不那么简单,如图1-19所示。
除了内部数据存储器外,8x51的数据存储器还可扩展外部数据存储器,这两部分的数据存储器可以并存。不过,存取数据存储器时,所采用的指令并不一样,例如,存取内部数据存储器时,可用MOV指令,但存取外部数据存储器时,则使用MOVX指令。
另外,内部数据存储器中,从0000H到007FH之间的128B为可直接寻址或间接寻址的存储器。在编写C语言程序时,以数据类型来区别直接寻址与间接寻址。在这一区间的数据存储器又可分成三部分(如图1-20所示),说明如下。
1.寄存器组区
0000H到001FH的32个地址为寄存器组(Register Bank)区,说明如下。
(1)0000H到0007H为寄存器组0(即RB0),0008H到000FH为寄存器组1(即RB1),0010H到0017H为寄存器组2(即RB2),0018H到001FH为寄存器组3(即RB3)。
(2)每组寄存器组都包含R0~R7共8个寄存器,而任一时刻只能使用其中一组寄存 器组。
(3)寄存器组的切换可以用程序状态字寄存器(Program Status Word,PSW)中的RS1与RS0来决定,如表1-4所示。
(4)当CPU复位时,系统的堆栈指针(SP)指向07H地址,所以数据存入堆栈时,将从08H开始,也就是RB1里的R0地址。为避免冲突或不必要的错误,通常会把堆栈指针移到30H以后的地址。
2.可位寻址区
0020H到002FH的16个字节存储器区为可位寻址区。通常存取存储器是以字节为单位,“可位寻址”则是指定存取1个位(bit)。在8051的汇编语言里,可使用布尔运算指令进行位操作,例如,要把20H存储器地址的bit 5设置为1,则可使用下列指令:
SETB 20H.5```
另外,从0020H到002FH的16个字节总共128个位(16×8),也可以直接指定为0到127,以刚才的20H存储器地址的bit 5而言,也可将“20H.5”指定为“05”,指令如下:
SETB 05`同理,若要将25H存储器地址的bit 2清除为0,则可使用下列指令:
CLR 25H.2```
或
CLR 42`其中,42=58+2。
3.一般数据与堆栈区
0030H到007FH的80个字节地址为一般数据存取及堆栈区。由于CPU复位后,堆栈指针指向07H位置,为了确保数据的安全与程序执行的正确,如果在程序之中使用了PUSH、POP命令,最好能把堆栈指针改至本区,例如,要将堆栈指针移至0030H地址,则在程序开始处即使用如下命令:
MOV SP, #30H```
从0080H到00FFH之间的128B为特殊功能寄存器(Special Function Register,SFR)或可直接寻址的存储器,至于“特殊功能寄存器”,稍后再详细说明。
如果是8052/8032,则0080H到00FFH之间的128B除了是特殊功能寄存器或可直接寻址的存储器外,另外也可以使用间接寻址的方式存取与这特殊功能寄存器位置重叠但为独立的存储器。
####1-3-3 特殊功能寄存器
在MCS-51里,寄存器只是CPU里特定地址的数据存储器而已。而在0080H到00FFH之间的128B,正是特殊功能寄存器(Special Function Register,SFR)所在位置。特殊功能寄存器就是8x51/52内部的结构,若以汇编语言编写程序时,必须熟练掌握这些寄存器,若以C语言编写程序,就不是那么重要。其位置的声明放置在Keil C所提供的“reg51.h”头文件(详见后面章节)里,只要把它包含到程序里即可,而不必记忆这些位置。以下简单介绍这些寄存器(如表1-5所示),仅供参考。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/9df418014608fbd4ae2fe0eeda9adc5369e19e36.png" width="" height="">
</div>
注:(1)本表C8-CF行部分为8052/8032才有的寄存器,本表第1列的部分为可位寻址的寄存器,较深灰底的部分为89S51/52才有的。
(2)8051/52、89C51/52只有一组数据指针寄存器,所以其中的DP0L应改为DPL,DP0H应改为DPH。
圓 P0、P1、P2、P3
P0~P3为MCS-51的4个输入/输出端口,其地址分别为80H、90H、0A0H及0B0H,待第3章再详细介绍。
圓SP
SP为堆栈指针寄存器(Stack Pointer register),其地址为81H。堆栈是一种特殊的数据存储方式,其数据的操作顺序是先进后出(First In Last Out,FILO),当数据以PUSH命令送入堆栈时,SP自动减1;若以POP命令从堆栈取出数据时,SP自动加1。
圓 DPL、DPH
89C51只有一组16位的数据指针寄存器(Data Pointer register,DPTR),这组数据指针寄存器是由DPL与DPH两个8位的数据指针寄存器组成,其地址分别为82H、83H。若以DPL为低8位、DPH为高8位,所组成的16位数据指针寄存器将可寻址到64KB的数据地址。89S51有两组16位数据指针寄存器,分别是DP0L、DP0H、DP1L及DP1H,其地址分别为82H、83H、84H、85H。若以汇编语言编写程序时,DPTR是查表法的必备寄存器。不过,使用C语言编写程序时,就不太需要由我们直接控制这个寄存器。
圓 PCON
PCON为电源控制寄存器(Power Control register),其地址为87H,其功能是设置CPU的电源方式,待后续7-3-3节再行说明。
圓TCON
TCON为定时器/计数器控制寄存器(Timer/Counter Control register),其地址为88H,其功能是设置定时器/计数器的启动,记录定时/计数溢出及外部中断的类型等(见第6章),待后续关于定时器/计数器部分(第7章),再行说明。
圓 TMOD
TMOD为定时器/计数器方式控制寄存器(Timer/Counter Mode Control register),其地址为089H,其功能是设置定时/计数的方式,待后续关于定时器/计数器部分(第7章)再行说明。
圓 TL0、TL1、TH0、TH1
TL0、TH0为第一组定时器/计数器(Timer0)的计数器,其地址为8AH、8CH,将TH0与TL0组合即可进行16位的定时/计数。TL1、TH1为第二组定时器/计数器(Timer1)的计数器,其地址为8BH、8DH,将TH1与TL1组合即可进行16位的定时/计数,待后续关于定时器/计数器部分(第7章)再行说明。
圓 SCON
SCON为串行口控制寄存器(Serial port Control register),其地址为98H,其功能是设置串行口工作方式与标志,待后续关于串行口部分(第8章)再行说明。
圓 SBUF
SBUF为串行口缓冲器(Serial BUFfer),其地址为99H,它由使用同一个地址的两个寄存器所构成,其中一个寄存器作为发送数据用的缓冲器,另一个寄存器作为接收数据用的缓冲器。至于如何分辨同一个地址的两个寄存器,视指令而定,若是数据发送的指令,则自动定位到发送数据用的缓冲器;若是接收数据的指令,则自动定位到接收数据用的缓冲器,待后续关于串行口部分(第8章)再行说明。
圓 IE
IE为中断使能寄存器(Interrupt Enable register),其地址为0A8H,其功能是启用中断功能,待后续关于中断部分(第6章)再行说明。
圓 IP
IP为中断优先等级寄存器(Interrupt Priority register),其地址为0B8H,其功能是设置中断的优先等级,待后续关于中断部分(第6章)再行说明。
圓 T2CON
T2CON为Timer2的定时器/计数器控制寄存器,其地址为0C8H,其功能是设置Timer2的启动、记录定时/计数溢出,以及外部中断的类型等,而Timer2只在8052/8032中才有。
圓 RCAP2L、RCAP2H
RCAP2L、RCAP2H为捕捉寄存器(Capture register),其地址为0CAH、0CBH。当Timer2在捕捉方式时,若T2EX(P1.1)引脚上的输入信号由高电平跳变为低电平,TL2与TH2的内容将被载入RCAP2L与RCAP2H里,就像是把Timer2的内容“捉进”RCAP寄存器一样。
圓 TL2、TH2
TL2、TH2为第三组定时器/计数器(Timer2)的计数器,其地址为0CCH、0CDH,将TH2与TL2组合即可进行16位的定时/计数。
圓 PSW
PSW为CPU的程序状态字组寄存器(Program Status Word register),其地址为0D0H,其内容说明如下。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/25e3c8f6f7ade2825cc500fa13ade337376dbf3c.png" width="" height="">
</div>
PSW.7:本位为进位标志(CY),进行加法(减法)运算时,若最左边位(MSB,即bit 7)产生进位(借位)时,则本位将自动设置为1,即CY=1;否则CY=0。
PSW.6:本位为辅助进位标志(AC),进行加法(减法)运算时,若bit 3产生进位(借位)时,则本位将自动设置为1,即AC=1;否则AC=0。
PSW.5:本位为用户标志(F0),可由用户自行设置的位。
PSW.4与PSW.3:这两个位为寄存器组选择位(RS1、RS0),其功能如表1-4所示。
PSW.2:本位为溢出标志(OV),当进行算术运算时,若发生溢出,则OV=1;否则 OV=0。
PSW.1:本位为保留位,没有提供服务。
PSW.0:本位为校验标志(P),8051采用偶校验,若ACC里有奇数个1,则P=1;若ACC里有偶数个1,则P=0。
圓 ACC
ACC累加器(Accumulator)又称为A寄存器,其地址为0E0H,这个寄存器提供CPU主要运行的位置,可说是最常用的寄存器。
圓 B
B寄存器的地址为0F0H,主要功能是配合A寄存器进行乘法或除法运算,进行乘法运算时,乘数放在B寄存器,而运算结果的高8位放在B寄存器;进行除法运算时,除数放在B寄存器,而运算结果的余数放在B寄存器。若不进行乘/除法运算,B寄存器也可当成一般寄存器使用。
圓 AUXR
AUXR寄存器为89S51新增的辅助寄存器(AUXiliary Register),其地址为8EH,其内容说明如下。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/dfe6a980baf1aa7c19a65e8c81d30c6e52bf7dcc.png" width="" height="">
</div>
WDIDLE:本位设置在待机方式(Idle Mode)下,是否启用看门狗。若本位设置为1,则在Idle方式下将启用看门狗;若本位设置为0,则在Idle方式下将停用看门狗。
DISRTO:本位设置是否输出复位信号,若本位设置为1,则Reset引脚(第9脚)只有输入功能;若本位设置为0,则在WDT计数完毕后,Reset引脚输出复位信号(即高电平脉冲)。
DISALE:本位设置是否启用ALE信号,若本位设置为1,则只有在执行MOVX指令或MOVC指令时,ALE引脚(第30脚)才会正常工作;若本位设置为0,则固定每6个脉冲就输出1个高电平脉冲,稍后说明。
其他位为保留位。当然,这个寄存器只有在89S51里才有作用。
圓 AUXR1
AUXR1寄存器为89S51新增的第2个辅助寄存器,其地址为0A2H,其内容说明如下:
<div style="text-align: center"><img src="https://yqfile.alicdn.com/16956fb531f9dfac8d70c93782cf74b15a5d5134.png" width="" height="">
</div>
DPS:本位的功能是选择数据指针寄存器。若本位设置为1,则使用DP1L及DP1H;若本位设置为0,则使用DP0L及DP0H。
其他位为保留位。同样,这个寄存器只有在89S51里才有作用。
圓 WDTRST
文章
C语言 · .NET · 开发框架 · 安全 · 存储
2017-05-02
《例说8051:单片机程序设计案例教程》——第 2 章 输出口的应用 2-1 认识MCS-51的存储器结构
本节书摘来自异步社区《例说8051:单片机程序设计案例教程》一书中的第2章,第2-1节,作者 张义和,陈敌北,更多章节内容可以访问云栖社区“异步社区”公众号查看。
第 2 章 输出口的应用
本章内容丰富,主要包括3部分:
2-1 认识MCS-51的存储器结构
除了无ROM型的8031及8032外,MCS-51的存储器包括程序存储器(ROM)与数据存储器(RAM)两部分,一般这两部分是独立的个体。标准的8x51系列具有4KB程序存储器、128B数据存储器,而标准的8x52系列具有8KB、256B数据存储器,刚好是8x51系列的两倍。不管是8x51、8031、8032还是8x52,其外部扩展的程序存储器或数据存储器最多为64KB,如表2-1所示。
近年来许多半导体厂商所推出的MCS-51兼容单片机都增大其内部程序存储器与数据存储器,例如Atmel半导体公司的TS83C51RB2,其内部有16KB程序存储器、256B数据存储器;TS83C51RC2,其内部有32KB程序存储器、256B数据存储器;TS83C51RD2,其内部有64KB程序存储器、768B数据存储器。
2-1-1 程序存储器
顾名思义,程序存储器(ROM)是存放程序的存储器,而CPU将自动从程序存储器读取所要执行的指令。MCS-51的程序存储器结构如图2-1所示,可选择使用内部程序存储器或外部程序存储器,说明如下:
若使用8031或8032,由于内部没有程序存储器,一定要使用外部程序存储器,所以其引脚必须接地。
当引脚接高电平时,CPU将使用内部程序存储器,若程序超过4KB(8x51)或8KB(8x52),则CPU会自动从外部程序存储器里,读取超过部分的程序代码。
当引脚接地时,CPU将从外部程序存储器读取所要执行的指令,而CPU内部的程序存储器形同虚设。
注意:4KB的程序存储器对于初学者而言,已是绰绰有余。另外,坏掉的8x51/8x52,很可能是其中的程序存储器坏掉,这时可将其引脚接地,改为外接程序存储器,即可当作8031/8032使用。
当CPU复位后,程序将从程序存储器0000H位置开始执行,如果没有遇到跳转指令,则按程序存储器顺序执行。当然,程序存储器前面几个位置还有一些特殊功能,留到第5章介绍中断时,再详细说明。
2-1-2 数据存储器
MCS-51的程序存储器与数据存储器是分开的独立区域,所以访问数据存储器时,所使用的地址并不会与程序存储器冲突。相对于程序存储器,数据存储器就比较不简单,如图2-2所示。
如图2-2所示,8051的数据存储器中,除内部数据存储器外,还可扩展外部数据存储器,这两部分的数据存储器可以并存。不过,访问数据存储器时,所采用的指令并不一样,例如访问内部数据存储器时,可用MOV指令,但访问外部数据存储器时,则使用MOVX指令。
另外,内部数据存储器也有些复杂,如图2-3所示,说明如下:
从0000H到007FH之间的128B为可直接寻址或间接寻址的存储器,至于“直接寻址”与“间接寻址”,将在2-5节再详细说明。在这一区域里的数据存储器又可分成三部分,说明如下。
● 寄存器组区
0000H到001FH的32个地址为寄存器组(Register Bank)区:0000H到0007H为寄存器组0(即RB0),0008H到000FH为寄存器组1(即RB1),0010H到0017H为寄存器组2(即RB2),0018H到001FH为寄存器组3(即RB3)。每组寄存器组都包含R0~R7共8个寄存器,而在任何一个时刻,只能使用其中一组寄存器组。
对于寄存器组的切换可以由程序状态字寄存器(Program Status Word,PSW)中的RS1与RS0来决定,如表2-2所示。
当CPU复位时,系统的堆栈指针(SP)指向07H地址,所以数据存入堆栈时,将从08H开始,也就是RB1里的R0地址。为避免冲突或不必要的错误,通常会把堆栈指针移到30H以后的地址。
● 可位寻址区
0020H到002FH共16个字节的存储器区为可位寻址区,通常访问存储器是以一个字节(B)为单位,所谓“可位寻址”是指可以指定访问一个位(bit),在8051里,只要使用布尔运算指令,即可进行位操作,例如,要把20H存储器地址的bit 5设置为1,则可使用下列指令:
SETB 20H.5
另外,从0020H到002FH的16个字节总共128个位(168),也可以直接指定为0到127,以刚才的20H存储器地址的bit 5来说,可指定为5,即
SETB 05
同理,若要将25H存储器地址的bit 2清除为0,则可使用下列指令:
CLR 25H.2
或(5'8+2=42)
CLR 42
● 一般数据与堆栈区
0030H到007FH的80个字节地址为一般数据访问及堆栈区。由于CPU复位后,堆栈指针指向07H位置,为了确保数据的安全与程序执行的正确,如果在程序之中使用了PUSH、POP命令,最好能把堆栈指针改至本区,例如,要将堆栈指针移至0030H地址,则在程序开始处即使用如下命令:
MOV SP, #30H
从0080H到00FFH之间的128B为特殊功能寄存器(Special Function Register,SFR)或可直接寻址的存储器。特殊功能寄存器稍后再详细说明。
如果是8052/8032,则0080H到00FFH之间的128B,除了是特殊功能寄存器或可直接寻址的存储器外,另外也可以采用间接寻址的方式访问与这些特殊功能寄存器位置重叠但独立的存储器。
2-1-3 特殊功能寄存器
在MCS-51里,寄存器只是CPU里特定地址的数据存储器而已。而在0080H到00FFH之间的128B正是特殊功能寄存器所在的位置,什么是特殊功能寄存器呢?首先看看这128个地址,如图2-4所示。
图2-4 特殊功能寄存器
注意:上图中斜体字部分为8052/8032才有的寄存器,最左列的部分为可位寻址的寄存器,灰底的部分为89S51/52才有的。另外,8051/52、89C51/52只有一个数据指针寄存器,所以其中的DP0L应改为DPL,DP0H应改为DPH。
P0、P1、P2、P3
P0~P3为MCS-51的4个输入/输出口,其地址分别为80H、90H、0A0H及0B0H,稍后再详细介绍这4个输入/输出口。对于89S51而言,在执行烧录程序(In-System Programmable,ISP)时,P1.5引脚即为MOSI引脚,P1.6引脚即为MISO引脚,P1.7引脚即为SCK引脚。
SP
SP为堆栈指针寄存器(Stack Pointer register),其地址为81H。堆栈是一种特殊的数据存储方式,其数据的操作顺序是先进后出(First In Last Out,FILO),当数据以PUSH指令存入堆栈时,SP自动加1;以POP指令从堆栈取出数据时,SP自动减1。
DPL、DPH
89C51只有一个16位的数据指针寄存器(Data Pointer register,DPTR),而这个数据指针寄存器是由DPL与DPH两个8位的数据指针寄存器组成,其地址分别为82H、83H。若以DPL为低8位、DPH为高8位,所组成的16位数据指针寄存器将可寻址到64KB的数据地址。89S51有两个16位数据指针寄存器,分别是DP0L、DP0H、DP1L及DP1H,其地址分别为82H、83H、84H、85H。
PCON
PCON为电源控制寄存器(Power Control register),其地址为87H,其功能是设置CPU的电源方式,等讲到电源部分章节,再作说明。
TCON
TCON为定时器/计数器控制寄存器(Timer/Counter Control register),其地址为88H,其功能是设置定时器/计数器的启动、记录定时器/计数器溢出,以及设置外部中断的触发方式等,在第6章讲述定时器/计数器部分时再详细说明。
TMOD
TMOD为定时器/计数器方式控制寄存器(Timer/Counter Mode Control register),其地址为089H,其功能是设置定时器/计数器的方式,在第6章讲述定时器/计数器部分时再详细说明。
TL0、TL1、TH0、TH1
TL0、TH0为第一个定时器/计数器(Timer 0)的计数寄存器,其地址为8AH、8CH,将TH0与TL0组合即可实现16位的定时/计数功能。TL1、TH1为第二个定时器/计数器(Timer 1)的计数寄存器,其地址为8BH、8DH,将TH1与TL1组合即可实现16位的定时/计数功能,在第6章讲述定时器/计数器部分时再详细说明。
SCON
SCON为串行口控制寄存器(Serial Port Control register),其地址为98H,其功能是设置串行口工作方式与标志,在第7章关于串行口部分再详细说明。
SBUF
SBUF为串行口缓冲器,其地址为99H,这是由使用同一个地址的两个寄存器构成的,其中一个寄存器作为发送数据用的缓冲器,另一个寄存器作为接收数据用的缓冲器。至于如何分辨同一个地址的两个寄存器,则视指令而定,若是发送数据的指令,则自动定位到发送数据用的缓冲器;若是接收数据的指令,则自动定位到接收数据用的缓冲器,在第7章讲述串行口部分再进行详细说明。
IE
IE为中断使能寄存器(Interrupt Enable register),其地址为0A8H,其功能是启用中断功能,在第5章讲述中断部分再进行详细说明。
IP
IP为中断优先级寄存器(Interrupt Priority register),其地址为0B8H,其功能是设置中断的优先级,在第5章关于中断部分再进行详细说明。
T2CON
T2CON为Timer 2的定时器/计数器控制寄存器,其地址为0C8H,其功能是设置Timer 2的启动、记录定时/计数溢出,以及外部中断的触发方式等,而Timer 2只有在8052/8032中才有。
RCAP2L、RCAP2H
RCAP2L、RCAP2H为捕获寄存器(Capture register),其地址为0CAH、0CBH。当Timer 2在捕获方式时,若T2EX(P1.1)引脚上的输入信号由高电平转为低电平,TL2与TH2的内容将被载入到RCAP2L与RCAP2H里,就像是捕获Timer 2的内容到RCAP寄存器一样,而Timer 2只有在8052/8032中才有。
TL2、TH2
TL2、TH2为第三个定时器/计数器(Timer 2)的计数寄存器,其地址为0CCH、0CDH,将TH2与TL2组合即可执行16位的定时/计数,而Timer 2只有在8052/8032中才有。
PSW
PSW为CPU的程序状态字寄存器(Program Status Word register),其地址为0D0H,其内容说明如下:
● PSW.7:该位为进位位(CY),进行加法(减法)运算时,若最左边位(MSB,即bit 7)产生进位(借位)时,则此位将自动设置为1,即CY=1;否则CY=0。
● PSW.6:该位为辅助进位位(AC),进行加法(减法)运算时,若bit 3产生进位(借位)时,则此位将自动设置为1,即AC=1;否则AC=0。
● PSW.5:该位为用户标志位(F0),可由用户自行设置的位。
● PSW.4与PSW.3:这两个位为寄存器组选择位(RS1、RS0),其功能如表2-3所示。
● PSW.2:该位为溢位标志位(OV),当进行算术运算时,若发生溢位,则OV=1;否则OV=0。
● PSW.1:该位为保留位,没有提供服务。
● PSW.0:该位为奇偶位(P),8051采用偶校验,若ACC里有奇数个1,则P=1;若ACC里有偶数个1,则P=0。
ACC
ACC累加器(Accumulator)又称为A寄存器,其地址为0E0H,这个寄存器作为CPU主要运行的寄存器,可说是最常用的寄存器。
B
B寄存器的地址为0F0H,主要功能是配合A寄存器进行乘法或除法运算,进行乘法运算时,乘数放在B寄存器,而运算结果的高八位放在B寄存器;进行除法运算时,除数放在B寄存器,而运算的结果,即余数,放在B寄存器。若不进行乘/除法运算,B寄存器也可当成一般寄存器使用。
AUXR
AUXR寄存器为89S51新增的辅助寄存器(Auxiliary register),其地址为8EH,其内容说明如下:
● WDIDLE:该位设置在待机方式(Idle Mode)下,用于设置是否启用看门狗。若此位设置为1,则在Idle方式下将启用看门狗;若此位设置为0,则在Idle方式下将停用看门狗。
● DISRTO:该位用于设置是否输出复位信号。若此位设置为1,则Reset引脚(第9脚)只有输入功能;若此位设置为0,则在WDT计数完毕后,Reset引脚输出复位信号(即高电平脉冲)。
● DISALE:该位用于设置是否启用ALE信号。若此位设置为1,则只有在执行MOVX指令或MOVC指令时, ALE引脚(第30脚)才会正常工作;若此位设置为0,则固定每6个脉冲就输出1个高电平脉冲,详见第3章。
其他位为保留位。当然,这个寄存器只有在89S51里才有作用。
AUXR1
AUXR1寄存器为89S51新增的第二个辅助寄存器,其地址为0A2H,其内容说明如下:
● DPS:该位的功能是选择数据指针寄存器。若此位设置为1,则使用DP1L及DP1H。若此位设置为0,则使用DP0L及DP0H。
其他位为保留位。同样地,这个寄存器只有在89S51里才有作用。
WDTRST
WDTRST寄存器为89S51新增的看门狗定时器复位寄存器(Watchdog Timer Reset register),其地址为0A6H。当我们要启用WDT时,则按顺序将01EH、0EH写入WDTRST寄存器,当14位计数器溢位(达到16383,即3FFFH),即由RESET引脚送出一个高电平脉冲以达到复位状态。此脉冲的宽度为98TOSC,其中TOSC=1/FOSC,以12MHz的时钟脉冲为例,脉冲的宽度为,关于看门狗与节电方式,详见第4章。
《例说8051:单片机程序设计案例教程》——2-1 认识MCS-51的存储器结构
本节书摘来异步社区《例说8051:单片机程序设计案例教程》一书中的第2章,第2.1节,作者:张义和,陈敌北,更多章节内容可以访问云栖社区“异步社区”公众号查看
2-1 认识MCS-51的存储器结构
例说8051:单片机程序设计案例教程除了无ROM型的8031及8032外,MCS-51的存储器包括程序存储器(ROM)与数据存储器(RAM)两部分,一般这两部分是独立的个体。标准的8x51系列具有4KB程序存储器、128B数据存储器,而标准的8x52系列具有8KB、256B数据存储器,刚好是8x51系列的两倍。不管是8x51、8031、8032还是8x52,其外部扩展的程序存储器或数据存储器最多为64KB,如表2-1所示。
近年来许多半导体厂商所推出的MCS-51兼容单片机都增大其内部程序存储器与数据存储器,例如Atmel半导体公司的TS83C51RB2,其内部有16KB程序存储器、256B数据存储器;TS83C51RC2,其内部有32KB程序存储器、256B数据存储器;TS83C51RD2,其内部有64KB程序存储器、768B数据存储器。
2-1-1 程序存储器
顾名思义,程序存储器(ROM)是存放程序的存储器,而CPU将自动从程序存储器读取所要执行的指令。MCS-51的程序存储器结构如图2-1所示,可选择使用内部程序存储器或外部程序存储器,说明如下:
三角形 若使用8031或8032,由于内部没有程序存储器,一定要使用外部程序存储器,所以其引脚必须接地。
三角形 当引脚接高电平时,CPU将使用内部程序存储器,若程序超过4KB(8x51)或8KB(8x52),则CPU会自动从外部程序存储器里,读取超过部分的程序代码。
当引脚接地时,CPU将从外部程序存储器读取所要执行的指令,而CPU内部的程序存储器形同虚设。
注意:4KB的程序存储器对于初学者而言,已是绰绰有余。另外,坏掉的8x51/8x52,很可能是其中的程序存储器坏掉,这时可将其引脚接地,改为外接程序存储器,即可当作8031/8032使用。当CPU复位后,程序将从程序存储器0000H位置开始执行,如果没有遇到跳转指令,则按程序存储器顺序执行。当然,程序存储器前面几个位置还有一些特殊功能,留到第5章介绍中断时,再详细说明。
2-1-2 数据存储器
MCS-51的程序存储器与数据存储器是分开的独立区域,所以访问数据存储器时,所使用的地址并不会与程序存储器冲突。相对于程序存储器,数据存储器就比较不简单,如图2-2所示。
如图2-2所示,8051的数据存储器中,除内部数据存储器外,还可扩展外部数据存储器,这两部分的数据存储器可以并存。不过,访问数据存储器时,所采用的指令并不一样,例如访问内部数据存储器时,可用MOV指令,但访问外部数据存储器时,则使用MOVX指令。
另外,内部数据存储器也有些复杂,如图2-3所示,说明如下:
三角形 从0000H到007FH之间的128B为可直接寻址或间接寻址的存储器,至于“直接寻址”与“间接寻址”,将在2-5节再详细说明。在这一区域里的数据存储器又可分成三部分,说明如下。
● 寄存器组区
0000H到001FH的32个地址为寄存器组(Register Bank)区:0000H到0007H为寄存器组0(即RB0),0008H到000FH为寄存器组1(即RB1),0010H到0017H为寄存器组2(即RB2),0018H到001FH为寄存器组3(即RB3)。每组寄存器组都包含R0~R7共8个寄存器,而在任何一个时刻,只能使用其中一组寄存器组。
对于寄存器组的切换可以由程序状态字寄存器(Program Status Word,PSW)中的RS1与RS0来决定,如表2-2所示。
当CPU复位时,系统的堆栈指针(SP)指向07H地址,所以数据存入堆栈时,将从08H开始,也就是RB1里的R0地址。为避免冲突或不必要的错误,通常会把堆栈指针移到30H以后的地址。
● 可位寻址区
0020H到002FH共16个字节的存储器区为可位寻址区,通常访问存储器是以一个字节(B)为单位,所谓“可位寻址”是指可以指定访问一个位(bit),在8051里,只要使用布尔运算指令,即可进行位操作,例如,要把20H存储器地址的bit 5设置为1,则可使用下列指令:
SETB 20H.5```
另外,从0020H到002FH的16个字节总共128个位(168),也可以直接指定为0到127,以刚才的20H存储器地址的bit 5来说,可指定为5,即
SETB 05`同理,若要将25H存储器地址的bit 2清除为0,则可使用下列指令:
CLR 25H.2```
或(5'8+2=42)
CLR 42`● 一般数据与堆栈区
0030H到007FH的80个字节地址为一般数据访问及堆栈区。由于CPU复位后,堆栈指针指向07H位置,为了确保数据的安全与程序执行的正确,如果在程序之中使用了PUSH、POP命令,最好能把堆栈指针改至本区,例如,要将堆栈指针移至0030H地址,则在程序开始处即使用如下命令:
MOV SP, #30H```
三角形 从0080H到00FFH之间的128B为特殊功能寄存器(Special Function Register,SFR)或可直接寻址的存储器。特殊功能寄存器稍后再详细说明。
三角形 如果是8052/8032,则0080H到00FFH之间的128B,除了是特殊功能寄存器或可直接寻址的存储器外,另外也可以采用间接寻址的方式访问与这些特殊功能寄存器位置重叠但独立的存储器。
####2-1-3 特殊功能寄存器
在MCS-51里,寄存器只是CPU里特定地址的数据存储器而已。而在0080H到00FFH之间的128B正是特殊功能寄存器所在的位置,什么是特殊功能寄存器呢?首先看看这128个地址,如图2-4所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/f7c1cfdc4c91c1814a7a012d6cef12283b572bb7.png" width="" height="">
</div>
图2-4 特殊功能寄存器
注意:上图中斜体字部分为8052/8032才有的寄存器,最左列的部分为可位寻址的寄存器,灰底的部分为89S51/52才有的。另外,8051/52、89C51/52只有一个数据指针寄存器,所以其中的DP0L应改为DPL,DP0H应改为DPH。
圓 P0、P1、P2、P3
P0~P3为MCS-51的4个输入/输出口,其地址分别为80H、90H、0A0H及0B0H,稍后再详细介绍这4个输入/输出口。对于89S51而言,在执行烧录程序(In-System Programmable,ISP)时,P1.5引脚即为MOSI引脚,P1.6引脚即为MISO引脚,P1.7引脚即为SCK引脚。
圓 SP
SP为堆栈指针寄存器(Stack Pointer register),其地址为81H。堆栈是一种特殊的数据存储方式,其数据的操作顺序是先进后出(First In Last Out,FILO),当数据以PUSH指令存入堆栈时,SP自动加1;以POP指令从堆栈取出数据时,SP自动减1。
圓 DPL、DPH
89C51只有一个16位的数据指针寄存器(Data Pointer register,DPTR),而这个数据指针寄存器是由DPL与DPH两个8位的数据指针寄存器组成,其地址分别为82H、83H。若以DPL为低8位、DPH为高8位,所组成的16位数据指针寄存器将可寻址到64KB的数据地址。89S51有两个16位数据指针寄存器,分别是DP0L、DP0H、DP1L及DP1H,其地址分别为82H、83H、84H、85H。
圓 PCON
PCON为电源控制寄存器(Power Control register),其地址为87H,其功能是设置CPU的电源方式,等讲到电源部分章节,再作说明。
圓 TCON
TCON为定时器/计数器控制寄存器(Timer/Counter Control register),其地址为88H,其功能是设置定时器/计数器的启动、记录定时器/计数器溢出,以及设置外部中断的触发方式等,在第6章讲述定时器/计数器部分时再详细说明。
圓 TMOD
TMOD为定时器/计数器方式控制寄存器(Timer/Counter Mode Control register),其地址为089H,其功能是设置定时器/计数器的方式,在第6章讲述定时器/计数器部分时再详细说明。
圓 TL0、TL1、TH0、TH1
TL0、TH0为第一个定时器/计数器(Timer 0)的计数寄存器,其地址为8AH、8CH,将TH0与TL0组合即可实现16位的定时/计数功能。TL1、TH1为第二个定时器/计数器(Timer 1)的计数寄存器,其地址为8BH、8DH,将TH1与TL1组合即可实现16位的定时/计数功能,在第6章讲述定时器/计数器部分时再详细说明。
圓 SCON
SCON为串行口控制寄存器(Serial Port Control register),其地址为98H,其功能是设置串行口工作方式与标志,在第7章关于串行口部分再详细说明。
圓 SBUF
SBUF为串行口缓冲器,其地址为99H,这是由使用同一个地址的两个寄存器构成的,其中一个寄存器作为发送数据用的缓冲器,另一个寄存器作为接收数据用的缓冲器。至于如何分辨同一个地址的两个寄存器,则视指令而定,若是发送数据的指令,则自动定位到发送数据用的缓冲器;若是接收数据的指令,则自动定位到接收数据用的缓冲器,在第7章讲述串行口部分再进行详细说明。
圓 IE
IE为中断使能寄存器(Interrupt Enable register),其地址为0A8H,其功能是启用中断功能,在第5章讲述中断部分再进行详细说明。
圓 IP
IP为中断优先级寄存器(Interrupt Priority register),其地址为0B8H,其功能是设置中断的优先级,在第5章关于中断部分再进行详细说明。
圓 T2CON
T2CON为Timer 2的定时器/计数器控制寄存器,其地址为0C8H,其功能是设置Timer 2的启动、记录定时/计数溢出,以及外部中断的触发方式等,而Timer 2只有在8052/8032中才有。
圓 RCAP2L、RCAP2H
RCAP2L、RCAP2H为捕获寄存器(Capture register),其地址为0CAH、0CBH。当Timer 2在捕获方式时,若T2EX(P1.1)引脚上的输入信号由高电平转为低电平,TL2与TH2的内容将被载入到RCAP2L与RCAP2H里,就像是捕获Timer 2的内容到RCAP寄存器一样,而Timer 2只有在8052/8032中才有。
圓 TL2、TH2
TL2、TH2为第三个定时器/计数器(Timer 2)的计数寄存器,其地址为0CCH、0CDH,将TH2与TL2组合即可执行16位的定时/计数,而Timer 2只有在8052/8032中才有。
圓 PSW
PSW为CPU的程序状态字寄存器(Program Status Word register),其地址为0D0H,其内容说明如下:
<div style="text-align: center"><img src="https://yqfile.alicdn.com/e75035e15f838938931efd98863d807369623be7.png" width="" height="">
</div>
● PSW.7:该位为进位位(CY),进行加法(减法)运算时,若最左边位(MSB,即bit 7)产生进位(借位)时,则此位将自动设置为1,即CY=1;否则CY=0。
● PSW.6:该位为辅助进位位(AC),进行加法(减法)运算时,若bit 3产生进位(借位)时,则此位将自动设置为1,即AC=1;否则AC=0。
● PSW.5:该位为用户标志位(F0),可由用户自行设置的位。
● PSW.4与PSW.3:这两个位为寄存器组选择位(RS1、RS0),其功能如表2-3所示。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/15989a9701703df009333b384c3248305680bcb0.png" width="" height="">
</div>
● PSW.2:该位为溢位标志位(OV),当进行算术运算时,若发生溢位,则OV=1;否则OV=0。
● PSW.1:该位为保留位,没有提供服务。
● PSW.0:该位为奇偶位(P),8051采用偶校验,若ACC里有奇数个1,则P=1;若ACC里有偶数个1,则P=0。
圓 ACC
ACC累加器(Accumulator)又称为A寄存器,其地址为0E0H,这个寄存器作为CPU主要运行的寄存器,可说是最常用的寄存器。
圓 B
B寄存器的地址为0F0H,主要功能是配合A寄存器进行乘法或除法运算,进行乘法运算时,乘数放在B寄存器,而运算结果的高八位放在B寄存器;进行除法运算时,除数放在B寄存器,而运算的结果,即余数,放在B寄存器。若不进行乘/除法运算,B寄存器也可当成一般寄存器使用。
圓 AUXR
AUXR寄存器为89S51新增的辅助寄存器(Auxiliary register),其地址为8EH,其内容说明如下:
<div style="text-align: center"><img src="https://yqfile.alicdn.com/560496d03990fa63056fd575af85c07afda2c786.png" width="" height="">
</div>
● WDIDLE:该位设置在待机方式(Idle Mode)下,用于设置是否启用看门狗。若此位设置为1,则在Idle方式下将启用看门狗;若此位设置为0,则在Idle方式下将停用看门狗。
● DISRTO:该位用于设置是否输出复位信号。若此位设置为1,则Reset引脚(第9脚)只有输入功能;若此位设置为0,则在WDT计数完毕后,Reset引脚输出复位信号(即高电平脉冲)。
● DISALE:该位用于设置是否启用ALE信号。若此位设置为1,则只有在执行MOVX指令或MOVC指令时, ALE引脚(第30脚)才会正常工作;若此位设置为0,则固定每6个脉冲就输出1个高电平脉冲,详见第3章。
其他位为保留位。当然,这个寄存器只有在89S51里才有作用。
圓 AUXR1
AUXR1寄存器为89S51新增的第二个辅助寄存器,其地址为0A2H,其内容说明如下:
<div style="text-align: center"><img src="https://yqfile.alicdn.com/b4806190c478827adaf5d5247ea749f1e03bb5c1.png" width="" height="">
</div>
● DPS:该位的功能是选择数据指针寄存器。若此位设置为1,则使用DP1L及DP1H。若此位设置为0,则使用DP0L及DP0H。
其他位为保留位。同样地,这个寄存器只有在89S51里才有作用。
圓 WDTRST
《新编计算机科学概论》一2.3 冯?诺依曼结构和哈佛结构
2.3 冯?诺依曼结构和哈佛结构
阿兰?图灵在1937年首次提出了一个通用计算设备的设想。他设想所有的计算都可能在一种特殊的机器上执行,这就是现在所说的图灵机。如果我们把计算机定义成一个数据处理器,计算机就可以被看作是一个接收输入数据、处理数据、产生输出数据的黑盒。尽管这个模型能够体现现代计算机的功能,但是它的定义还是太狭窄,因为按照这种定义,便携式计算器也可以认为是计算机。基于通用图灵机建造的计算机都是在存储器中存储数据的。在1944 ~ 1945年期间,冯?诺依曼指出,鉴于程序和数据在逻辑上是相同的,程序也能存储在计算机的存储器中。冯?诺依曼结构又称作普林斯顿体系结构(Princetion Architecture),这是由于冯?诺依曼当时在普林斯顿大学任职。该结构自提出以来,主导了电子计算机半个多世纪的发展,是计算机发展史上最重要的体系结构,是通用计算机使用的最主要结构。
2.3.1 冯?诺依曼理论
冯?诺依曼理论的思想是:应该把程序也存储在存储器里,让计算机自己负责从存储器里提取指令,执行指令,并循环式地执行这两个动作,如图2?5所示。这样计算机在执行程序的过程中,就可以完全摆脱外界的影响,以自己可能的速度(电子的速度)自动地运行。按照这种原理构造出来的计算机就是“存储程序控制计算机”,也被称作“冯?诺依曼计算机”,简称冯?诺依曼机。
从20世纪初,物理学和电子学科学家们就在争论制造可以进行数值计算的机器应该采用什么样的结构。人们被十进制这个人类习惯的计数方法所困扰,著名的ENIAC就是用的十进制,那时以研制模拟计算机的呼声更为响亮和有力。20世纪40年代,冯?诺依曼大胆地提出:抛弃十进制,采用二进制作为数字计算机的数制基础。同时,他还提出预先编制计算程序,然后由计算机来按照事前制定的计算顺序来执行数值计算工作的思想,奠定了冯?诺依曼结构的理论基础,这就是著名的“存储程序控制原理”。1946年6月,冯?诺依曼在“关于电子计算装置逻辑结构初探”的报告中正式提出了以二进制、程序存储和程序控制为核心的一系列思想,对ENIAC的缺陷进行了有效的改进,从而奠定了冯?诺依曼计算机的体系结构基础。计算机经过了几十年的发展,其体系结构也发生了许多改进,但冯?诺依曼提出的数字计算机的原则仍然没有突破。冯?诺依曼理论的要点:指令像数据那样存放在存储器中,并可以像数据那样进行处理。指令格式使用二进制机器码表示。使用程序存储控制方式工作。这3条内容合称冯?诺依曼原理。EDVAC是最早采用冯?诺依曼体系结构的计算机。大半个世纪过去了,直到今天商品化的计算机还基本遵循着冯?诺依曼提出的原理。
2.3.2 冯?诺依曼体系结构
从冯?诺依曼原理的角度看,一台完整的计算机系统必须具有如下功能:运算、自我控制、存储、输入/输出(I/O)和用户界面。其中,运算、自我控制、存储、输入/输出功能由相应全称命名的功能模块实现,各模块之间通过连接线路传输信息,我们一般将其统称为计算机硬件系统;用户界面主要由软件实现,我们一般将其统称为计算机软件系统。因此,我们可以这样理解,现代计算机是由遵循冯?诺依曼原理的硬件和软件系统所构建的运算处理系统。冯?诺依曼体系结构主要具有以下特点:指令与数据均使用二进制代码形式表现,电子线路采用二进制。存储器中的指令与数据形式一致,机器对它们同等对待,不加区分。指令在存储器中按执行顺序存储,并使用一个指令计数器来控制指令执行的方向,实现顺序执行或转移,即算法是顺序型的。存储器的结构是按地址访问的顺序线性编址的一维结构。计算机由5大部分组成:运算器、控制器、存储器、输入设备、输出设备。指令由操作码和地址码两部分组成,操作码确定操作的类型,地址码指明操作数据存储的地址。一个字长的各位同时进行处理,即在运算器中是并行的字处理。运算器的基础是加法器。二进制在工程上实现容易,因为具有两个稳定状态的电子器件众多,所以用二进制表示数据具有抗干扰能力强、可靠性高的优点,这是数字计算机最关键的因素。另一方面,数制基数越小,表示数的符号就越少,相应的运算规律也就愈简单。例如十进制乘法有“九九表”的55条规则,如果要采用电路实现十进制乘法,将非常复杂。而二进制的乘法只有3条规则,即:0×0=0、0×l=0(或者1×0=0)、1×l=1。用电路实现二进制乘法规则,只需一个两输入端的与门就可实现。二进制的“1”和“0”还正好与逻辑推理中的真和假对应,可以非常方便地实现逻辑运算和逻辑判断。冯?诺依曼计算机的结构如图2?6所示。整个结构以运算器为中心,数据流动必须经过运算器,并由控制器进行控制。其5大部件相互关系及工作过程如下:
1)通过输入设备输入原始数据、计算程序以及给控制器的控制命令等,由控制器控制,将有关部分存入存储器(存储程序和数据)。2)在控制器控制下从存储器读取一条指令到控制器,经译码分析将指令转换为控制信号控制运算器动作,需要的话,还要从存储器读取数据到运算器中。3)运算器在控制器控制下完成规定操作,并将结果送回存储器或送到输出设备输出。4)从存储器读取下一条指令,重复第2步以后的步骤,直到程序中包含的全部指令都执行完成或收到停止的指令为止。运算器是用二进制进行算术和逻辑运算的部件。它由算术逻辑运算部件(ALU)和若干通用寄存器组成。ALU由组合逻辑电路构成,完成算术和逻辑运算。其中,算术运算指加减乘除和求补码等,它以加法运算为核心,减法通过补码变减为加,乘除通过一系列的加法和移位来完成。逻辑运算完成“与”、“或”、“非”和“异或”等基本逻辑运算。寄存器用来存放参加运算的数据以及保存运算结果。运算器除了完成运算之外,还可以传送数据,因此,运算器是计算机的关键部件之一,它的功能和运算速度对计算机来说至关重要。 控制器是计算机的指挥中心,它的作用是从存储器中取出指令,然后分析指令,发出由该指令规定的一系列操作命令,完成指令的功能。控制器是计算机的关键部件,它的功能直接关系到计算机性能,是计算机最复杂、最关键的部件。输入/输出设备是计算机与外界联系的设备,因此也称为外设。计算机通过外设获得各种外界信息,并且通过外设输出运算处理结果。因此,计算机能够在各行各业大显身手,完成人难以实现的运算工作,控制机器生产完成人力难以实现的产品数量和质量。常见的主要外设有键盘、鼠标器、扫描仪、显示器、打印机、绘图仪等。前三者是典型的输入设备,而后三者是典型的输出设备。这些外设是人机联系最密切的设备。
2.3.3 冯?诺依曼结构的演变
冯?诺依曼结构是一种宏观的自动计算结构,现代通用电子计算机在设计时基本沿用了冯?诺依曼体系结构的原理。传统的冯?诺依曼计算机系统是串行执行的。其主要缺点有以下几点:1)由于处理器与存储器之间的特殊关系,处理器要频繁访问存储器,而处理器的速度要高出存储器几个数量级,存在处理器与存储器之间的瓶颈;同时,由于其执行指令是串行的,由此造成指令串行执行效率低,不能充分发挥处理器功效。2)在使用计算机语言的时候,人们常常使用高级语言,而高级语言与机器语言之间的差别是较大的,要用不同的编译和解释程序将高级语言的源程序翻译成机器可以识别的机器语言,从而带来较大的工作量。3)由于冯?诺依曼计算机采用的是按地址访问的顺序存储空间,对于复杂的数据结构,必须经过地址映像存放才能解决问题,因此也带来了不便。对于传统的冯?诺依曼计算机系统存在的缺点,计算机系统结构的设计者在极力寻求解决问题的办法,通常有两种方法可以使用。一种是改良的方法,即在现有的机型上进行不断改进,使系统更合理、更有效;另一种是革命的方法,那就是彻底推翻冯?诺依曼计算机系统的结构,重新设计更完整、更合理的系统。显然,革命的方法不论从哪个方面讲代价都是很大的,而且设计周期也会很长,况且设计出来的新结构也不一定就是完美的。因此,系统结构的设计者选择了改良的方法对现有的计算机体系结构进行改进,在实践中不断探索,以便设计出更好的系统。随着电子技术的发展和实际使用的需要,现代计算机在结构设计上较冯?诺依曼结构有了进一步的演变,特别是在微观结构方面,基于LSI/VLSI技术的发展而有了更加明显的变化。与冯?诺依曼结构相比,现代计算机结构主要在以下方面有所演变:1)将运算器与控制器集中于一块芯片,称为微处理器,并常用它作为中央处理器件(CPU),有时甚至将部分存储器和输入输出接口都集中到微处理器上,称作单片机。2)采用并行控制、流水线等方法,开发并行性。采用并行控制技术和流水线技术,提高系统作业的吞吐率。引入流水线技术,将传统的串行执行方式转变为并行方式,充分利用处理器内部的功能部件,采用精简指令系统,单周期执行一条或多条指令以提高程序执行效率。3)采用多体交叉存储器,增加存储带宽。采用多体交叉存储器,可以在一个存储器访问周期中同时对多个存储单元进行访问,可以进行多字的一次性存取,从而增加存储带宽。4)以存储器为核心,使I/O设备和处理器可并行工作。传统的冯?诺依曼系统以运算器为核心,存储器、输入/输出设备都直接对应运算器,从而使得各种I/O设备无法与处理器并行工作。采用以存储器为核心的结构,可以提高I/O设备和处理器并行工作的能力,从而改进系统的性能。5)现代机器采用总线(bus)技术。总线是连接计算机有关部件的一组信号线,是计算机中用来传送信息代码的公共通道,其作用是将计算机各个部件连接起来,并实现各部件之间正确的数据传输。冯?诺依曼机的五大部分之间都可以用总线连接。总线包括单总线、双总线和多总线结构。采用总线结构主要有以下优点:简化了系统结构,便于系统设计制造。大大减少了连线数目,便于布线,减小体积,提高系统的可靠性。便于接口设计,所有与总线连接的设备均采用类似的接口。便于系统的扩充、更新与灵活配置,易于实现系统的模块化。便于设备的软件设计,所有需要控制接口的软件就是对不同的接口地址进行操作。便于故障诊断和维修,同时也降低了成本。在后面的各章节中我们将分别介绍这些概念和技术。
2.3.4 哈佛结构
哈佛结构(Harvard Architecture,缩写为HARC)是一种将程序指令存储和数据存储分开的存储器结构,这一点与冯?诺依曼结构有本质的区别,由哈佛大学的学者提出。中央处理器首先到程序指令存储器中读取程序指令内容,解码后得到数据地址,再到相应的数据存储器中读取数据,并进行下一步的操作(通常是执行)。程序指令存储和数据存储分开,可以使指令和数据有不同的数据宽度,如Microchip公司的PIC16芯片的程序指令是14位宽度,而数据是8位宽度。哈佛结构的微处理器通常具有较高的执行效率。其程序指令和数据指令分开组织和存储,执行时可以预先读取下一条指令。哈佛结构采用程序和数据空间独立的体系结构,目的是为了减轻程序运行时的访存瓶颈。例如在一条指令同时取两个操作数的运算中,在流水线处理时同时还有一个取指令操作,如果程序和数据通过一条总线访问,取指令和取数据必会产生冲突,而这对大运算量循环的执行效率是很不利的。哈佛结构能基本上解决取指和取数的冲突问题。目前使用哈佛结构的中央处理器和微控制器有很多,除了上面提到的Microchip公司的PIC系列芯片,还有Motorola公司的MC68系列、Zilog公司的Z8系列、ATMEL公司的AVR系列和ARM公司的ARM9、ARM10和ARM11,51单片机也属于哈佛结构。
[体感游戏] 1、MPU6050数据采集传输与可视化
最近在研究体感游戏,到目前为止实现了基于51单片机的MPU6050数据采集、利用蓝牙模块将数据传输到上位机,并利用C#自制串口数据高速采集软件,并且将数据通过自制的折线图绘制模块可视化地展示出来等功能。本文将主要对实现这意见单系统中遇到的问题做一个小结——其中包括:
1、基于51的MPU6050模块通信简介(入门级)
2、陀螺仪数据采集与传输及帧格式介绍(小技巧)
3、基于C#的串口接收函数(C#基本知识)
4、多线程数据池解决高速串口实时性问题(难点)
5、折线图可视化模块(程序员基本功)
关键词:MPU6050 蓝牙 C#串口 多线程 高速串口 折线图绘制
1、基于51的MPU6050模块通信简介(入门级)
因为是入门级,就先最简单的介绍如何利用51从MPU6050中读取数据吧(对于想知道卡尔曼滤波、俯角仰角、距离测量、摔倒检测、记步等算法的可能要在接下来介绍)。既然要和MPU6050通信,那么必不可少的是阅读芯片手册,如果您觉得亲自去看又长又多而且都是英文的手册很费时,不仿看看我找的简要版:
MPU-60X0是全球首例9轴运动处理器。它集成了3轴MEMS陀螺仪,3轴MEMS加速计,以及1个可扩展的数字运动处理器DMP(Digital Motion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。扩展之后就可以通过其I2C或SPI接口输出一个9轴的信号。MPU-60X0也可以通过其I2C接口连接非惯性的数字传感器,比如压力传感器。
MPU-60X0对陀螺仪和加速计分别用了三个16位的ADC,将其测量的模拟量转化为可输出的数字量。为了精确跟踪快速和慢速运动,传感器的测量范围是可控的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速计可测范围为±2,±4,±8,±16g(重力加速度)。
注:下图是采用串口助手将MPU6050采集的数据显示在上位机上,其中前三列输出为三维的加速度(这里的加速度包括地球本身的重力加速度),后三列为三维的角速度。
但是这里的输出值并不是真正的加速度和角速度的值,上面说过,MPU是一个16位AD量程可程控的设备,这里设置的加速度传感器的测量量程为正负2g(这里的g为重力加速度),陀螺仪的量程为正负2000°/s。所以要用下面的公式进行转化:
好了,有了上面的基础知识之后咱们就能尝试用51的I2C总线从MPU6050读取实时的3轴加速度和3轴角速度了。由于51本身不带有I2C总线通信协议,所以我们要自己实现一个I2C通信协议,下面是我从网上找的并稍加修改的一个I2C总线通信的代码:
1 #include <REG52.H>
2 #include <INTRINS.H>
3
4 typedef unsigned char uchar;
5 typedef unsigned short ushort;
6 typedef unsigned int uint;
7
8 //-----------------------------------------
9 // 定义MPU6050内部地址
10 //-----------------------------------------
11 #define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
12 #define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
13 #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
14 #define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
15 #define ACCEL_XOUT_H 0x3B
16 #define ACCEL_XOUT_L 0x3C
17 #define ACCEL_YOUT_H 0x3D
18 #define ACCEL_YOUT_L 0x3E
19 #define ACCEL_ZOUT_H 0x3F
20 #define ACCEL_ZOUT_L 0x40
21 #define TEMP_OUT_H 0x41
22 #define TEMP_OUT_L 0x42
23 #define GYRO_XOUT_H 0x43
24 #define GYRO_XOUT_L 0x44
25 #define GYRO_YOUT_H 0x45
26 #define GYRO_YOUT_L 0x46
27 #define GYRO_ZOUT_H 0x47
28 #define GYRO_ZOUT_L 0x48
29 #define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
30 #define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
31 #define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
32
33 //-----------------------------------------
34 // I2C总线通信函数
35 //-----------------------------------------
36 void I2C_Start(); //I2C起始信号
37 void I2C_Stop(); //I2C停止信号
38 void I2C_SendACK(bit ack); //I2C发送应答信号[入口参数:ack (0:ACK 1:NAK)]
39 bit I2C_RecvACK(); //I2C接收应答信号
40 void I2C_SendByte(uchar dat); //向I2C总线发送一个字节数据
41 uchar I2C_RecvByte(); //从I2C总线接收一个字节数据
42 void Single_WriteI2C(uchar REG_Address,uchar REG_data);//向I2C设备写入一个字节数据
43 uchar Single_ReadI2C(uchar REG_Address); //从I2C设备读取一个字节数据
44
45 //-----------------------------------------
46 // 通过I2C和MPU6050通信的函数
47 //-----------------------------------------
48 void InitMPU6050(); //初始化MPU6050
49 int GetData(uchar REG_Address); //合成数据
如果你没搞过硬件又从未听说过I2C,那么想想socket的握手再看看上面36~43行的有关ACK、Send、Write的函数大概能明白I2C的功能。当我们实现I2C的通信函数之后就可以与带有I2C通信接口的芯片进行通信,那么怎样通信呢?其实很简单——你可以把每个芯片比做为一个巨大的储物柜,储物柜里每个抽屉里存着相应的东西,你想让佣人帮你去拿个东西,只要告诉佣人对应的抽屉号就行了。这里I2C总线相当于这个佣人,每个抽屉相当于芯片中的寄存器,抽屉号相当于寄存器地址。当你想设置芯片的某些属性时是向对应的寄存器内写数据,当想从芯片内获取相关数据时,就要通过I2C向对应的地址写数据然后接收芯片返回的数据。这里的8~31行为MPU-6050芯片内几个常用的寄存器地址,前四个常用来作为设置芯片工作属性,15~28共14个寄存器地址用来获取传感器的3轴加速度、3轴角速度和温度的数据(这里每一种信息都包括H和L两位,是由于8位表示不完该数据,于是分高低两部分)
这样我们便不难理解InitMPU6050()和GetData(uchar REG_Address)函数:初始化函数是向相应的地址写初始化配置数据(关于0x00\0x07等意思请参看MPU6050寄存器版说明书),而GetData则是传入想获得数据项的低地址,然后连续读取当前地址数据和下一地址数据合成为想要的项目数据(上面讲了数据分高低部分)。
1 //-----------------------------------------
2 //初始化MPU6050
3 //-----------------------------------------
4 void InitMPU6050()
5 {
6 Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态
7 Single_WriteI2C(SMPLRT_DIV, 0x07);
8 Single_WriteI2C(CONFIG, 0x06);
9 Single_WriteI2C(GYRO_CONFIG, 0x18);
10 Single_WriteI2C(ACCEL_CONFIG, 0x01);
11 }
12 //-----------------------------------------
13 //合成数据
14 //-----------------------------------------
15 int GetData(uchar REG_Address)
16 {
17 uchar H,L;
18 H=Single_ReadI2C(REG_Address);
19 L=Single_ReadI2C(REG_Address+1);
20 return (H<<8)+L; //合成数据
21 }
2、陀螺仪数据采集与传输及帧格式介绍(小技巧)
上面我们已经知道单片机如何利用I2C设置MPU6050的工作属性,以及从MPU6050获得3轴加速度和3轴角速度的数据。那么接下来将介绍单片机是如何将数据通过蓝牙发送给上位机的。如下图左半部分,下位机部分包括一个MPU6050、一个单片机、一个电源模块,以及一个蓝牙模块。对于蓝牙模块我不想做过多的讲解(我记得我已经写了不下于3次关于手机、PC等和下位机通信的教程了:(如果是想用安卓手机和蓝牙模块通信来实现遥控功能的话,可以参考:http://www.cnblogs.com/zjutlitao/p/4231635.html;想用笔记本和蓝牙模块通信来实现遥控功能的话可以参考:http://www.cnblogs.com/zjutlitao/p/3886826.html)
其实,利用串口蓝牙模块单片机要做的工作和对串口进行的操作一样,对串口写数据则送至蓝牙模块将数据发出,当外部有数据传送过来时,单片机可以用相应的中断捕获该事件,然后接收消息。因此主函数中初始化串口和MPU6050之后就进入循环数据发送状态,在循环中GetData是上面介绍的获得3轴加速度、3轴角速度或温度的值的函数,SendData则是将int类型的值转换为字符串然后一位一位的发送出去,而最开始和最后分别发送一个#和$作为该帧的开始和结束标志位,具体格式如下:
#
1
2
3
5
4
-
2
1
3
3
2
-
2
1
1
2
5
$
注:符号位要么为'-',要么为空。
1 //-----------------------------------------
2 //主程序
3 //-----------------------------------------
4 void main()
5 {
6 delay(500); //上电延时
7 init_uart();
8 InitMPU6050(); //初始化MPU6050
9 delay(150);
10 while(1)
11 {
12 SeriPushSend('#');//
13 SendData(GetData(0x3B)); //X轴加速度
14 SendData(GetData(0x3D)); //Y轴加速度
15 SendData(GetData(0x3F)); //Z轴加速度
16 SeriPushSend('$'); //结束
17 delay(20);
18 }
19 }
3、基于C#的串口接收函数(C#基本知识)
上面讲到下位机通过串口蓝牙将数据发送给上位机,那么上位机如何接收蓝牙信号呢?其实以我的笔记本为例,因为笔记本内置蓝牙模块,所以无需在上位机上独立安装一个USB-蓝牙模块。而上位机操作蓝牙模块和操作串口几乎一模一样。如下面的C#程序,当点击连接按钮时实例化SerialPort,设置端口号、读超时、然后实例化一个串口数据接收事件句柄(这里PortDataReceived作为数据接收的回调函数)。
1 //Create a serial port for Connection
2 SerialPort Connection = new SerialPort();
3 private void btn_link_Click(object sender, EventArgs e)
4 {
5 if (!Connection.IsOpen)
6 {
7 //Start
8 //Status = "正在连接...";
9 Connection = new SerialPort();
10 btn_link.Enabled = false;
11 Connection.PortName = PortList.SelectedItem.ToString();
12 Connection.Open();
13 Connection.ReadTimeout = 10000;
14 Connection.DataReceived += new SerialDataReceivedEventHandler(PortDataReceived);
15 //Status = "连接成功";
16 timer1.Start();
17 }
18 }
在PortDataReceived中,只要简单调用Connection.Read(data, 0, length);就能从串口缓冲区读取数据到data中。
1 private void PortDataReceived(object o, SerialDataReceivedEventArgs e)
2 {
3 byte[] data = new byte[length];
4 int num=Connection.Read(data, 0, length);
5 datepool.push_back(data,num);//实际接收的不一定是length,之前一直错
6 Connection.DiscardInBuffer();
7 Connection.DiscardOutBuffer();
8 }
注:本来是每次读取1byte放入数据池,结果出现程序运行速度越来越慢,本以为是上面的数据池设计的有问题,结果把数据池里的线程注释掉改为ask函数来每次需要数据时才获得,但是问题并不在于此;于是想到可能是绘制折线图的函数有问题,但是重查了一遍发现问题不在于此;于是仔细测量每个过程耗时,发现每个模块耗时正常,最后发现是由于串口缓冲区数据积累造成程序变慢,(因为下位机每20ms发送一次20byte的数据给上位机,上位机若一次不接收完所有数据,将会造成每次都有剩余而逐渐变慢),于是直接改成每次接收20byte,问题得到解决。
4、多线程数据池解决高速串口实时性问题(难点)
由于下位机10ms发送一次20byte的数据,上位机一方面要做好接收工作,保证数据不拥挤在串口接收缓冲区;另一方面也要实时获取当前从串口读到的最新数据。如果采用传统多线程+锁的机制是可以的,但是当多线程中加入锁势必会影响程序执行效率,通过综合分析该问题最终抽象出一个特殊的数据模型——自动更新的环形栈:
这样,当采用多线程时,用一个类似于栈的环状栈结构体(实时从串口读数据放入数据池,数据池用p_write标记最新数据存储位置,当外部程序想得到最新数据时,调用ask程序,ask程序从当前p_write向前取40个数据(因为有效数据长度为20,一次取40保证至少有一个有效数据),然后从这40个数据中找出有效信息,赋值给X,Y,Z;然后外部程序可以直接用对象访问X,Y,Z),通过适当调节环的容量达到自我覆盖的效果,同时根据p_write指针可以实时取得最新数据。
1 /// <summary>
2 /// 询问当前值
3 /// </summary>
4 /// <returns>如果解析到则返回真</returns>
5 public bool ask()
6 {
7 i = 0;//立刻将相应的40个字符复制出来
8 p_read_from = p_write - 40;
9 while (i < 40)
10 {
11 str[i] = pool[(p_read_from + pool_size) % pool_size];
12 i++;
13 p_read_from++;
14 }
15 i = 39;
16 while (i > 18 && str[i] != '$') i--;
17 if (i == 18) return false;
18 i--;
19 data_Z = 0;
20 for (int j = 4; j > -1; j--)
21 {
22 data_Z *= 10;
23 data_Z += (str[i - j] - '0');
24 }
25 if (str[i - 5] == '-') data_Z = -data_Z;
26 i -= 6;
27
28 data_Y = 0;
29 for (int j = 4; j > -1; j--)
30 {
31 data_Y *= 10;
32 data_Y += (str[i - j] - '0');
33 }
34 if (str[i - 5] == '-') data_Y = -data_Y;
35 i -= 6;
36
37 data_X = 0;
38 for (int j = 4; j > -1; j--)
39 {
40 data_X *= 10;
41 data_X += (str[i - j] - '0');
42 }
43 if (str[i - 5] == '-') data_X = -data_X;
44
45 X = data_X;
46 Y = data_Y;
47 Z = data_Z;
48 return true;
49 }
50
51 /// <summary>
52 /// 将数据输入数据池
53 /// </summary>
54 /// <param name="date">数据</param>
55 /// <param name="length">长度</param>
56 internal void push_back(byte[] date, int length)
57 {
58 for (int i = 0; i < length; i++)
59 {
60 pool[p_write++] = date[i];
61 if (p_write == pool_size) p_write = 0;
62 }
63 }
5、折线图可视化模块(程序员基本功)
通过上面几步我们已经可以将下位机的陀螺仪3轴的加速度收集过来了,但是如果先将数据收集好,然后再用matlab绘制,我们很难知道哪个动作对应哪个数据,不利于我们观察效果(虽然matlab上自带串口接口,但是LZ就是任性!有一张好看的脸,还是想着靠实力赢得地位,哈哈哈~)。
如本节小标题括号内所示,在C#里写一个绘制折线图的程序应该属于我们的基本功(我可不是调用相应的绘图接口哦!),其大致思想就是用一个List存储num个数据,当list中的数据少于num个时则不断添加,当list内的数据大于num个时,则从尾部进来一个的同时从头部删除一个(这样才能实现perfect的效果)。
注:其实中间还出现了一个逻辑错误性小插曲:原初写好之后,本以为能够实现高效数据采集显示,但是仔细观察发现还是有很大延时,但是旁边的数据显示却非常实时。这是为什么呢?查找了一会最终发现问题出在折线图绘制上——本来采用固定的模式(一张图能存放多少数据点就用vector<int>P/Q/R在初始化的时候存放这么多点,然后每次有一个新的数据过来时就会将新数据加到vector后面,同时删除最前面的一个数据,这样做是为了方便初始vector里没有数据绘制折线图错误的问题),可是问题就出在这!咋一看这种思路很好,初始化vector中放num个点,每次新的来到将最前面一个数据冲掉,这样这个vector始终保持着num个点,且最新的在最后面,整个折线图能反应实时情况。但是由于我为了“安全”起见,在vector初始化时多Add几个数据,这样导致vector中的数据量N>折线图一次能呈现的数据量num,所以最新的数据总会在之后出现!当时没有想到是这个原因,就直接改了下DateLineChar函数,实现根据vector大小自动绘制的算法(这样就不用预先在vector中装入一定量的值了)
6、预告与小结(预知后事如何,请听下回分解)
上面我只是简单收集了MPU6050的3轴加速度数值,当MPU6050位置固定好之后,我们就能根据数据推测其具体的姿态。例如:
绿色的z轴方向的加速度先高后低,红色y轴方向加速度先低后高,蓝色x轴方向加速度和y轴类似,但是比y轴幅度变化小,而后半周期数值正负正好相反。那么MPU6050运动过程大致为:在y轴方向上做往返运动,同时在x轴和z轴方向有稍微的偏转。(水平静止放置时z轴为重力加速度,x,y为0)
绿色的z轴变化不大,红色的y和蓝色的x同步类正弦变化。呵呵,这个运动状态分析起来就不太容易了~不过没关系,接下来我们要进一步获取并计算MPU6050的倾角,甚至是利用卡尔曼滤波计算MPU6050的运动距离,最终达到perfect的运动跟踪效果~
链接
51MPU6050采集代码:http://pan.baidu.com/s/1c0yE7Ws
4月2号总工程:http://pan.baidu.com/s/1hqzSt7Y (我用)
4月7号总工程:http://pan.baidu.com/s/1pJwq6qZ (我用)
github:https://github.com/beautifulzzzz/C4plus/tree/master/体感游戏
预习用1:[芯片][MPU6050] MPU60X0的DMP相关链接
预习用2:[stm32] MPU6050 HMC5883 Kalman 融合算法移植
本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/4396653.html,如需转载请自行联系原作者
文章
数据采集 · 传感器 · 数据可视化 · C# · 芯片 · 算法 · 程序员 · 存储 · Android开发 · 安全
2017-05-17