发新话题
打印

[分享] 【社区大讲堂】之万人学习51单片机!

本主题由 soso 于 2009-5-8 10:47 移动

【社区大讲堂】之万人学习51单片机!


请点击图片放大,查看详情

获奖网友名单公布:(学习光盘近期赠送,请您注意查收。谢谢大家的参与!让我们共同进步,谢谢!)


在此劳烦大家将各位的具体通讯地址:如电话,邮编,姓名,地址(因为是快递所以请不要写邮箱)发到wangtingting@eeworld.com.cn中,谢谢大家!



springvirus  在这里感谢 springvirus 长期,持之以恒的关注,而且给了我们许多非常好的建议,值得我们借鉴与思考,springvirus总说自己是
菜鸟,但我想也是eeworld的super 菜鸟~ 希望,通过大家在一起一系列的学习,由菜鸟变成大虾哦~

xhrabbit  姗姗,年纪虽小,可是学识渊博,有自己的思想,在技术方面也有自己的认识与见解,小朋友非常刻苦也非常可爱,大家看看姗姗的帖子,会立刻喜欢上这个小姑娘的!姗姗,Fighting~~

wang7588038 我们已经推出了关于AVR方面的系列讲座,链接如下,知道你非常希望得到这方面的知识,我们论坛就马上动手准备啦,
【社区大讲堂】之让我们学学AVR! (m16;led流水灯;外部中断;定时器;串口中断)http://bbs.eeworld.com.cn/thread-66668-1-1.html   多多关注啊!

pflysky pflysky小朋友期待关于ARM的课程,我们也是在筹划之中了,计日可待。

njxiii 什么问题继续问,我会随时回答你的问题的,打破砂锅问到底的精神值得肯定!

sungodmg
ARM讲座马上展开,看来大家都等不及了,希望我们越办越好吧!






老练讲座的PDF文档:




51零起点学习板及物料清单.rar


51零起点学习原理图及源程序.rar


老练讲座继续更新:

9.22  “单片机复位电路分析专题”
http://bbs.eeworld.com.cn/thread-66492-1-1.html

9.23  51上面也能跑操作系统了啊
http://bbs.eeworld.com.cn/viewthread.php?tid=66531

热心朋友ZQK推荐的单片机教程伟纳 平凡老师的教程)



成都理工大单片机课件
http://bbs.eeworld.com.cn/viewthread.php?tid=66883&pid=117889&page=1&extra=#pid117889






【社区大讲堂】之万人学习51单片机! 老练全部教程整理:






第一部分 IO知识培训(09.12)

  单片机的IO口控制是单片机初学者最为关心的问题,如何快速学会使用IO是初学者最为困难的地方。

  众多的教科书上面介绍了很多IO的原理,这些长篇大论让很多初学者看起来难以理解,同时也会止步于单片机门外。我们现在所要学习的使用IO就是很简单的使用就可以了,IO无非就是4种状态,输出为高、输出为低、输入为高、输入为低。

  我们只要把握这四个方面就可以了,先看看我们的单片机接口,单片机共有32个io。分别为P0、P1、P2、P3口,P0口如果当作IO来使用时,必须要使用上拉电阻,因为51单片机内部没有上拉这一功能。


  在这个学习板上面,大家可以看到40个io对应的位置,并且在数码管下面就增加了上拉电阻,使得io可以实现。
现在我们以P1口接的led指示灯来说明输出高低电平的功能,当我们使用汇编程序:

ORG 0000H
LJMP MAIN
ORG 0100H
MAIN:  MOV P1,#00H
        JMP  MAIN
        END

  这样,P1口的led就会变亮,因为P1口赋值#00h以后,就是将led的所有io都设置为低电平了。

  如果将MOV P1,#00H改成MOV P1,#0FFH,那么io就设置成了高电平,led就会被熄灭。当然C51中也比较简单。

#include <reg51.h>
Main()
{
P1=#00H;
While(1)
{;}
}

  这个同样的道理,如果P1=#0FFH,就是熄灭led。

  当我们对键盘的程序如下图解释:



  这个就是我们板子上面的键盘的接线图,当我们单独使用键盘的时候,我们要将P3.4置低电平,此时,我们也是根据前面led设置io的方式进行设置。

  如下程序:

    ORG  0000H
  JMP   MAIN
ORG  0100H
MAIN: CLR   P3.4
        MOV  P1,#0FFH
JNB   P2.7, MAIN
  MOV P1,#00H
        JMP    MAIN
        END
C51中:
       #include <reg51.h>
       Main()
       {
        P3^4=0;
While(1)
        {
          P1=0x0FF;
while(P2^7==0)
          {
P1=0x00;
}
}
}

  从这2个程序都可以看出io为低电平和高电平的效果。
附件:
第二讲
单片机外部中断使用 (09.16)


  很多人都知道51单片机中中断的重要性,但是书中的长篇累牍让我们一下子理解有点很不适应。所以我们尽可能简化51单片机的中断,目前我们是要学会单片机中断的使用就可以了,而不是从原理基本说起。

  在51单片机中有2个外部中断,这两个外部中断口处于低电平的时候开始触发中断信号,使得程序可以进入中断处理中断部分的程序。

  我们还是老规矩,先看看硬件电路:

 



  在图上的单片机原理图中,S0,S1分别接的是单片机的外部中断1和外部中断2,我们特意将两个发光二极管放在上面进行指示,以便我们可以看到中断运行的情况,此时我们可以设置中断程序的要求:

1、 指示灯平时没有中断按下的时候不进行工作,保持原有状态;

2、 当外部中断0响应的时候,我们就点亮D0,让外部中断0响应的条件就是按下S0按键。

  下面就是汇编程序:

      ORG    0000H
      LJMP   MAIN        ;主程序
      ORG    0003H
      LJMP    W_INT0      ;进入中断子程序
  ORG    0100H
MAIN:MOV SP, #50H         ;设置堆栈指针
     SETB    EA          ;CPU所有中断开(IE最高位MSB)
     SETB    EX0         ;INT0中断开
     CLR     IT0          ;INT0低电平触发(为1则为下降沿触发)
      MOV    P1,     #0FFH
      JMP     $
W_INT0:CLR   P1.0
        RETI
      END

如下是c51的程序:
//---------------------------------------------------
#include <reg51.h>
//---------------------------------------------------
//重定义I/0引脚名称
sbit led1=P1^0;
//---------------------------------------------------
//固定函数声明
void int_0();       //外部中断0
//---------------------------------------------------
void main(){
      P1=0X0FF;
      EA=1;               // CPU所有中断开(IE最高位MSB)
      EX0=1;              // INT0中断开
      IT0=0;              // INT0低电平触发(为1则为下降沿触发)
      while(1){
     {
     ;
     }
}
//-------------------------------------------------------------------
//INT0中断  由P3.2引脚产生
void int_0() interrupt 0 using 0
{
   led0=0;
}

      在上面的程序中,我们可以看出外部中断使用的方法,现在我们学会了使用中断0,那么我们现在开始学使用外部中断1吧。

  首先我们来设置程序的要求:

1、 外部中断0点亮D0,关闭D1

2、 外部中断1点亮D1,关闭D0

      我们先看看汇编语言的程序:
;---------------------------------------------------------------------
        LED0      EQU   P1.0
        LED1      EQU   P1.1
        ORG   0000H
        LJMP   main
        ORG   0003H
        LJMP   W_INT0      
        ORG   00013H
        LJMP    W_INT1
        ORG        0100H
main:
        SETB    EA          ;CPU所有中断开(IE最高位MSB)
        SETB    EX0         ;INT0中断开
        CLR     IT0         ;INT0低电平触发(为1则为下降沿触发)
        SETB    EX1         ;INT1中断开
        CLR     IT1         ;INT1低电平触发(为1则为下降沿触发)
        MOV    P1,  #0FFH
       JMP     $
W_INT0:
        SETB    LED1            ;关闭LED1
        CLR     LED0     ;点亮LED0
        RETI
W_INT0:
        CLR     LED1            ;点亮LED1
        SETB    LED0     ;关闭LED0
        RETI
END

    这是c51的程序:

//---------------------------------------------------
#include <reg51.h>
//---------------------------------------------------
//重定义I/0引脚名称
sbit LED0=P1^0;
sbit LED1=P1^1;
//---------------------------------------------------
//固定函数声明
void int_0();       //外部中断0
void int_1();       //外部中断1
//---------------------------------------------------
void main(){
     EA=1;               // CPU所有中断开(IE最高位MSB)
     EX0=1;              // INT0中断开
     IT0=0;              // INT0低电平触发(为1则为下降沿触发)
     EX1=1;              // INT1中断开
     IT1=0;              // INT1低电平触发(为1则为下降沿触发)
    while(1){
      {
        ;
}
}
//-------------------------------------------------------------------
void initial(){
     EA=1;               // CPU所有中断开(IE最高位MSB)
     EX0=1;              // INT0中断开
     IT0=0;              // INT0低电平触发(为1则为下降沿触发)
     EX1=1;              // INT1中断开
     IT1=0;              // INT1低电平触发(为1则为下降沿触发)
     return;
}
//-------------------------------------------------------------------
//INT0中断  由P3.2引脚产生
void int_0() interrupt 0 using 0
{
    Led1=1;             //关闭D1
    LED0=0;            //点亮D0
}
//-------------------------------------------------------------------
//INT1中断  由P3.3引脚产生
void int_1() interrupt 2 using 1
{
    LED0=1;          //关闭D0
    LED1=0;          //点亮D1
}

  这样,通过程序和实践,我们就可以很轻易学会了外部中断的使用方法。

有什么问题,欢迎大家随时提问啊!
附件:

TOP

第三部分 定时器中断培训(09.17)

  关于定时器中断,我们简单介绍一下原理就可以了,因为具体介绍已经有很多资料都已经提供了。


  80C
51单片机内部设有两个16位的可编程定时器/计数器。可编程的意思是指其功能(如工作方式、定时时间、量程、启动方式等)均可由指令来确定和改变。在定时器/计数器中除了有两个16位的计数器之外,还有两个特殊功能寄存器(控制寄存器和方式寄存器)。我们可以看出,16位的定时/计数器分别由两个8位专用寄存器组成,即:T0由TH0和TL0构成;T1由TH1和TL1 构成。其访问地址依次为8AH-8DH。每个寄存器均可单独访问。这些寄存器是用于存放定时或计数初值的。此外,其内部还有一个8位的定时器方式寄存器 TMOD和一个8位的定时控制寄存器TCON。这些寄存器之间是通过内部总线和控制逻辑电路连接起来的。TMOD主要是用于选定定时器的工作方式; TCON主要是用于控制定时器的启动停止,此外TCON还可以保存T0、T1的溢出和中断标志。当定时器工作在计数方式时,外部事件通过引脚T0 (P3.4)和T1(P3.5)输入。定时计数器的原理:


当定时器/计数器为定时工作方式时,计数器的加1信号由振荡器的12分频信号产生,即每过一个机器周期,计数器加1,直至计满溢出为止。显然,定时器的定时时间与系统的振荡频率有关。因一个机器周期等于12个振荡周期,所以计数频率fcount=1/12osc。如果晶振为12MHz,则计数周期为:

T=1/(12×106)Hz×1/12=1μs


  这是最短的定时周期。若要延长定时时间,则需要改变定时器的初值,并要适当选择定时器的长度(如8位、13位、16位等)。
 


当定时器/计数器为计数工作方式时,通过引脚T0和T1对外部信号计数,外部脉冲的下降沿将触发计数。计数器在每个机器周期的S5P2期间采样引脚输入电 平。若一个机器周期采样值为1,下一个机器周期采样值为0,则计数器加1。此后的机器周期S3P1期间,新的计数值装入计数器。所以检测一个由1至0的跳 变需要两个机器周期,故外部事年的最高计数频率为振荡频率的1/24。例如,如果选用12MHz晶振,则最高计数频率为0.5MHz。虽然对外部输入信号 的占空比无特殊要求,但为了确保某给定电平在变化前至少被采样一次,外部计数脉冲的高电平与低电平保持时间均需在一个机器周期以上。


当CPU用软件给定时器设置了某种工作方式之后,定时器就会按设定的工作方式独立运行,不再占用CPU的操作时间,除非定时器计满溢出,才可能中断CPU 当前操作。CPU也可以重新设置定时器工作方式,以改变定时器的操作。由此可见,定时器是单片机中效率高而且工作灵活的部件。

  综上所述,我们已知定时器/计数器是一种可编程部件,所以在定时器/计数器开始工作之前,CPU必须将一些命令(称为控制字)写入定时/计数器。将控制字写入定时/计数器的过程叫定时器/计数器初始化。在初始化过程中,要将工作方式控制字写入方式寄存器,工作状态字(或相关位)写入控制寄存器,赋定时/计 数初值。下面我们就提出的控制字的格式及各位的主要功能与大家详细的讲解。


控制寄存器 定时器/计数器T0和T1有2个控制寄存器-TMOD和TCON,它们分别用来设置各个定时器/计数器的工作方式,选择定时或计数功能,控制启动运行,以及作为运行状态的标志等。其中,TCON寄存器中另有4位用于中断系统。


  TMOD定时器/计数器方式寄存器

  定时器方式控制寄存器TMOD在特殊功能寄存器中,字节地址为89H,无位地址。TMOD的格式如下图所示。





由图可见,TMOD的高4位用于T1,低4使用于T0,4种符号的含义如下:


GATE:门控制位。GATE和软件控制位TR、外部引脚信号INT的状态,共同控制定时器/计数器的打开或关闭。


C/T:定时器/计数器选择位。C/T=1,为计数器方式;C/T=0,为定时器方式。


M1M0:工作方式选择位,定时器/计数器的4种工作方式由M1M0设定。


M1M0


工作方式


功能描述


00


工作方式0


13位计数器


01


工作方式1


16位计数器


10


工作方式2


自动再装入8位计数器


11


工作方式3


定时器0:分成两个8位计数器;
定时器1:停止计数




  定时器/计数器方式控制寄存器TMOD不能进行位寻址,只能用字节传送指令设置定时器工作方式,低半字节定义为定时器0,高半字节定义为定时器1。复位时,TMOD所有位均为0。


  例:设定定时器1为定时工作方式,要求软件启动定时器1按方式2工作。定时器0为计数方式,要求由软件启动定时器0,按方式1工作。

  我们怎么来实现这个要求呢?
  


大家先看上面TMOD寄存器各位的分布图
 


第一个问题:控制定时器1工作在定时方式或计数方式是哪个位?通过前面的学习,我们已知道,C/T位(D6)是定时或计数功能选择位,当C/T=0时定时/计数器就为定时工作方式。所以要使定时/计数器1工作在定时器方式就必需使D6为0。


第二个问题:设定定时器1按方式2工作。上表中可以看出,要使定时/计数器1工作在方式2,M0(D4) M1(D5)的值必须是1 0。


第三个问题:设定定时器0为计数方式。与第一个问题一样,定时/计数器0的工作方式选择位也是C/T(D2),当C/T=1时,就工作在计数器方式。


第四个问题:由软件启动定时器0,前面已讲过,当门控位GATE=0时,定时/计数器的启停就由软件控制。


第五个问题:设定定时/计数器工作在方式1,使定时/计数器0工作在方式1,M0(D0) M1(D1)的值必须是0 1。


从上面的分析我们可以知道,只要将TMOD的各位,按规定的要求设置好后,定时器/计灵敏器就会按我们预定的要求工作。我们分析的这个例子最后各位的情况如下:


    D7 D6 D5 D4 D3 D2 D1 D0

    0  0  1   0 0  1  0  1



二进制数00100101=十六进制数25H。所以执行MOV TMOD,#25H这条指令就可以实现上述要求。


  TCON定时器/计数器控制寄存器

  TCON在特殊功能寄存器中,字节地址为88H,位地址(由低位到高位)为88H一8FH,由于有位地址,十分便于进行位操作。


TCON的作用是控制定时器的启、停,标志定时器溢出和中断情况。


TCON的格式如下图所示。其中,TFl,TRl,TF0和TR0位用于定时器/计数器;IEl,ITl,IE0和IT0位用于中断系统。





各位定义如下:


TF1:定时器1溢出标志位。当字时器1计满溢出时,由硬件使TF1置“1”,并且申请中断。进入中断服务程序后,由硬件自动清“0”,在查询方式下用软件清“0”。


TR1:定时器1运行控制位。由软件清“0”关闭定时器1。当GATE=1,且INT1为高电平时,TR1置“1”启动定时器1;当GATE=0,TR1置“1”启动定时器1。


TF0:定时器0溢出标志。其功能及操作情况同TF1。


TR0:定时器0运行控制位。其功能及操作情况同TR1。


IE1:外部中断1请求标志。


IT1:外部中断1触发方式选择位。


IE0:外部中断0请求标志。


IT0:外部中断0触发方式选择位。


TCON中低4位与中断有关,我们将在下节课讲中断时再给予讲解。由于TCON是可以位寻址的,因而如果只清溢出或启动定时器工作,可以用位操作命令。例如:执行“CLR TF0”后则清定时器0的溢出;执行“SETB TR1”后可启动定时器1开始工作(当然前面还要设置方式定)。


  定时器/计数器的初始化


由于定时器/计数器的功能是由软件编程确定的,所以一般在使用定时/计数器前都要对其进行初始化,使其按设定的功能工作。初始货的步骤一般如下:


1、确定工作方式(即对TMOD赋值);


2、预置定时或计数的初值(可直接将初值写入TH0、TL0或TH1、TL1);


3、根据需要开放定时器/计数器的中断(直接对IE位赋值);


4、启动定时器/计数器(若已规定用软件启动,则可把TR0或TR1置“1”;若已规定由外中断引脚电平启动,则需给外引脚步加启动电平。当实现了启动要求后,定时器即按规定的工作方式和初值开始计数或定时)。


下面介绍一下确定时时/计数器初值的具体方法。

  因为在不同工作方式下计数器位数不同,因而最大计数值也不同。


现假设最大计数值为M,那么各方式下的


最大值M值如下:


方式0:M=213=8 192


方式1:M=216=65 536


方式2:M=28=256


方式3:定时器0分成两个8位计数器,所以两个M均为256。


因为定时器/计数器是作“加1”计数,并在计数满溢出时产生中断,因此初值X可以这样计算:


X=M-计数值


下面举例说明初值的确定方法。


例1、选择T1方式0用于定时,在P1.1输出周期为1ms方波,晶振fosc=6MHz。


解:根据题意,只要使P1.1每隔500us取反一次即可得到1ms的方波,因而T1的定时时间为500us,因定时时间不长,取方式0即可。则M1 M0=0;因是定时器方式,所以C/T=0;在此用软件启动T1,所以GATE=0。T0不用,方式字可任意设置,只要不使其进入方式3即可,一般取0, 故TMOD=00H。系统复位后TMOD为0,可不对TMOD重新清0。


下面计算500us定时T1初始值:


机器周期T=12/fosc=12/(6×106)Hz=2μs


设初值为X,则:


(1013-X)×2×10-6s=500×10-6s


X=7942D=1111100000110B=1F06H


因为在作13位计数器用时,TL1的高3位未用,应填写0,TH1占用高8位,所以X的实际填写应为:


X=111100000000110B=F806H


结果:TH1=F8H,TL1=06H


源程序如下:


ORG 2000H


MOV TL1,#06H ;给TL1置初值


MOV TH1,#0F8H ;给TH1置初值


SETB TR1 ;启动T1


LP1:JBC TF1,LP2 ;查询计数溢出否?


AJMP LP1


LP2:MOV TL1,#06H ;重新设置计数初值


MOV TH1,#0F8H


CPL P1.1 ;输出取反


AJMP LP1 ;重复循环


  看了上面的介绍,我们现在应该会计算定时器的初值和设置相关的参数了,下面我们先看看硬件如何设计,在protel99se中,我们可以设计成如下led电路图:




  我们在这里还是要设计自己的要求:


  使用定时器
0,定时1s,工作在方式1,使用16位计数方式,让D01s后取反闪烁。

  现在我们计算一下:在定时器中最大的延时只能做到
65ms,无法做到1s,那么我们只能通过寄存器进行计算,定时器为50ms,累加到1s,然后对D0进行取反操作。

  看如下汇编程序:


LED0   BIT    P1.0  ;灯的输出信号
LED1   BIT    P1.1
MSS    EQU   30H   ;20=1s
MS1    EQU   31H
;**************************************************
;主程序
;**************************************************

     ORG    0000H
     NOP
     NOP
     LJMP   MAIN
     ORG    00BH   ;计数器
     NOP
     NOP
     LJMP   SCAN   \
     ORG    01BH   ;计数器
     NOP
     NOP
     LJMP   SCAN1
     ORG    0100H


MAIN:   LCALL    DEL_1S
             MOV      P1,          #0FFH
             MOV      P3,          #0FFH
             MOV      SP,          #60H             ;设置堆栈
             MOV      MSS,       #00H
             MOV      TH0,        #3CH            ;50ms
             MOV      TL0,        #0B0H
             MOV      TH1,        #3CH            ;50ms
             MOV      TL1,        #0B0H
             MOV      TMOD,    #11H
             SETB      EA                              ;打开定时中断
             SETB      TR0
             SETB      ET0
             SETB      TR1
             SETB      ET1
             MOV      P1,       #0FFH
             AJMP     $
             NOP


;***********************************************
;定时子程序
;***********************************************

SCAN:     MOV       TH0,     #3CH
               MOV       TL0,     #0B0H
               PUSH      ACC
               INC         MSS
               MOV       A,          MSS
               CJNE       A,         #20,          SCA_E
               MOV       MSS,     #00
               CPL         LED0                            ;1S到了对LED0取反
               AJMP       SCA_E
SCA_E:    POP         ACC
               RETI
SCAN1:    MOV        TH1,      #3CH
                MOV        TL1,     #0B0H
                PUSH       ACC
                INC          MS1
                MOV        A,        MS1
                CJNE        A,       #20,         SCA_E1
                MOV         MS1,  #00
               CPL           LED1                               ;1S到了对LED0取反
               AJMP        SCA_E
SCA_E1:   POP          ACC
                RETI

                END


如下为c51的程序:
//---------------------------------------------------
#include <reg52.h>
//---------------------------------------------------
//重定义I/0引脚名称
sbit LED0=P1^0;
sbit LED1=P1^1;
//---------------------------------------------------
//全局变量及位标志定义
unsigned char MSS;
unsigned char MS1;
//---------------------------------------------------
//固定函数声明
void timer_0();
//
定时器中断0
void timer_1();
//
定时器中断1
//---------------------------------------------------
void main(){

                  TH0= 0x3C      ;50ms

                  TL0=0x0B0;
                  TH1= 0x3C      ;50ms
                  TL1=0x0B0;
                  EA=1;                            ;打开定时中断
                  TR0=1;
                  ET0=1;
                  TR1=1;
                  ET1=1;
                 TMOD=0X11;
      while(1){
     {
      ;
      }
}

//-------------------------------------------------------------------
//定时器0中断
void timer_0() interrupt 1 using 2
{

       TH0= 0x3C     ;50ms

       TL0=0x0B0;
       MSS=MSS+1;
       If(MSS>=20)
      {
       LED0=!LED0;
       }
       }
//-------------------------------------------------------------------
//定时器1中断
void timer_1() interrupt 3 using 3
{
     TH1= 0x3C        ;50ms
     TL1=0x0B0;
     MS1=MS1+1;
     If(MS1>=20)
    {
     LED1=!LED1;
     }

}

  以上就是定时器的用法,有问题欢迎交流
附件:
第四部分 串口中断培训(09.18)

MCS-51单片机的串行口具有两条独立的数据线——发送端TXD和接收端RXD,它允许数据同时往两个相反的方向传输。一般通信时发送数据由TXD端输出,接收数据由RXD端输入。MCS-51单片机的串行口既可以用于网络通信,亦可实现串行异步通信,还可以用作同步移位寄存器。如果在串行口的输入输出引脚上加上电平转换器,就可方便地构成标准的RS-232接口。MCS-51单片机的串行接口是一个全双工通信接口,它有两个物理上独立的接收、发送缓冲器SBUF,可以同时发送和接收数据。但是发送缓冲器只能写入,不能读出;接收缓冲器只能读出,不能写入。两个缓冲器共用一个地址(99H)。



数据通信的基本概念



常用于数据通信的传输方式有单工、半双工、全双工和多工方式。



  •   单工方式:数据仅按一个固定方向传送。因而这种传输方式的用途有限,常用于串行口的打印数据传输与简单系统间的数据采集。
  •   半双工方式:数据可实现双向传送,但不能同时进行,实际的应用采用某种协议实现收/发开关转换。
  •   全双工方式:允许双方同时进行数据双向传送,但一般全双工传输方式的线路和设备较复杂。
  •   多工方式:以上三种传输方式都是用同一线路传输一种频率信号,为了充分地利用线路资源,可通过使用多路复用器或多路集线器,采用频分、时分或码分复用技术,即可实现在同一线路上资源共享功能。


根据同步方式,串行数据通信有两种形式,如图5-5所示。



  •   异步通信。在这种通信方式中,接收器和发送器有各自的时钟,它们的工作是非同步的。异步通信用一帧来表示一个字符,其内容是一个起始位,紧接着是若干个数据位。
  •   同步通信。同步通信格式中,发送器和接收器由同一个时钟源控制,在异步通信中,每传输一帧字符都必须加上起始位和停止位,占用了传输时间,若要求传送数据 量较大,速度就会慢得多。同步传输方式去掉了这些起始位和停止位,只在传输数据块时先送出一个同步头(字符)标志即可。
  •   同步传输方式比异步传输方式速度快,这是它的优势。但同步传输方式也有其缺点,即它必须要用一个时钟来协调收发器的工作,所以它的设备也较复杂。


MCS-51的串行口控制寄存器



在完成串行口初始化后,发送数据时,采用MOV SBUF,A指令,将要发送的数据写入SBUF,则CPU自动启动和完成串行数据的输出;接收数据时,采用MOV A,SBUF指令,CPU就自动将接收到的数据从SBUF中读出。



控制MCS-51单片机串行接口的控制寄存器有两个——特殊功能寄存器SCON和PCON,用以设置串行端口的工作方式、接收/发送的运行状态、接收/发送数据的特征、数据传输率的大小,以及作为运行的中断标志等,其格式如下:



① 串行口控制寄存器SCON。SCON的字节地址是98H,位地址(由低位到高位)分别是98H一9FH。SCON的格式如下:








SM0、SMl:串行口工作方式控制位。


00——方式0;01——方式1;


10——方式2;11——方式3。


SM2:仅用于方式2和方式3的多机通信控制位。


发送机SM2=1(要求程控设置)。




当为方式2或方式3时:




接收机 SM2=1时,若RB8=1,可引起串行接收中断;若RB8=0,不引起串行接收中断。SM2=0时,若RB8=1,可引起串行接收中断;若RB8=0,亦可引起串行接收中断。



  • REN串行接收允许位:0——禁止接收;1——允许接收。
  • TB8:在方式2、3中,TB8是发送机要发送的第9位数据。
  • RB8:在方式2、3中,RB8是接收机接收到的第9位数据,该数据正好来自发送机的TB8。
  • TI:发送中断标志位。发送前必须用软件清零,发送过程中TI保持零电平,发送完一帧数据后,由硬件自动置1。如要再发送,必须用软件再清零。
  • RI:接收中断标志位。接收前,必须用软件清零,接收过程中RI保持零电平,接收完一帧数据后,由片内硬件自动置1。如要再接收,必须用软件再清零。

② 电源控制寄存器PCON。PCON的字节地址为87H,无位地址,其格式如下:






PCON是为在CMOS结构的MCS-51单片机上实现电源控制而附加的,对于HMOS结构的MCS-51系列单片机,除了第7位外,其余都是虚设的。与串行通信有关的也就是第7位,称作SMOD,它的用处是使数据传输率加倍。



SMOD:数据传输率加倍位。在计算串行方式1,2,3的数据传输率时;0表示不加倍;1表示加倍。



其余有效位说明如下。



GF1、GF2:通用标志位。


PD:掉电控制位,0表示正常方式,1表示掉电方式。


IDL:空闲控制位,0表示正常方式,1表示空闲方式。



除了以上两个控制寄存器外,中断允许寄存器IE中的ES位也用来作为串行I/O中断允许位。当ES=1,允许串行I/O中断;当ES=0,禁止串行I/O中断。中断优先级寄存器IP的PS位则用作串行I/O中断优先级控制位。当PS=1,设定为高优先级;当PS =0,设定为低优先级。




工作方式



MCS-51 单片机可以通过软件设置串行口控制寄存器SCON中SM0(SCON.7)和SMl(SCON.6)来指定串行口的4种工作方式。串行口操作模式选择如表5-2所示。



表5-2 串行口操作模式选择表



SM0 SM1


模 式


功 能


波 特 率


0 0


0


同步移位寄存器


fOSC/12


0 1


1


8位UART


可变(T1溢出率)


1 0


0


9位UART


fOSC/64或fOSC/32


1 1


1


9位UART


可变(T1溢出率)




其中,fosc是振荡器的频率,UART为通用异步接收和发送器的英文缩写。下面对这4种工作模式作进一步介绍。



1.方式0



当设定SM1、SM0为00时,串行口工作于方式0,它又叫同步移位寄存器输出方式。在方式0下,数据从 RXD(P3.0)端串行输出或输入,同步信号从TXD(P3.1)端输出,发送或接收的数据为8位,低位在前,高位在后,没有起始位和停止位。数据传输 率固定为振荡器的频率1/12,也就是每一机器周期传送一位数据。方式0可以外接移位寄存器,将串行口扩展为并行口,也可以外接同步输入/输出设备。



执行任何一条以SBUF为目的的寄存器指令,就开始发送。



2.方式1



当设定SM1、SM0为01时,串行口工作于方式1。方式1为数据传输率可变的8位异步通信方式,由TXD发送,RXD接收,一帧数据为10位,1位起始位(低电平),8位数据位(低位在前)和1位停止位(高电平)。数据传输率取决于定时器1或2的溢出速率 (1/溢出周期)和数据传输率是否加倍的选择位SMOD。




对于有定时器/计数器2的单片机,当T2CON寄存器中RCLK和TCLK置位时,用定时器2作为接收和发送的数据传输率发生器,而RCLK=TCLK=0时,用定时器1作为接收和发送的数据传输率发生器。两者还可以交叉使用,即发送和接收采用不同的数据传输率。



类似于模式0,发送过程是由执行任何一条以SBUF为目的的寄存器指令引起的。



3.方式2



当设定SM0、SM1二位为10时,串行口工作于方式2,此时串行口被定义为9位异步通信接口。采用这种方式可接收或发送 11 位数据,以 11 位为一帧,比方式 1 增加了一个数据位,其余相同。第 9 个数据即 D8 位用作奇偶校验或地址/数据选择,可以通过软件来控制它,再加特殊功能寄存器 SCON 中的 SM2 位的配合,可使 MCS-51 单片机串行口适用于多机通信。发送时,第9位数据为TB8,接收时,第9位数据送入RB8。方式 2 的数据传输率固定,只有两种选择,为振荡率的 1/64 或 1/32 ,可由 PCON 的最高位选择。



4.方式3



当设定SM0、SM1二位为11时,串行口工作于方式3。方式3与方式2类似,唯一的区别是方式3的数据传输率是可变的。而帧格式与方式2一样为11位一帧。所以方式3也适合于多机通信。



数据传输率的确定



串行口每秒钟发送(或接收)的位数就是数据传输率。



对方式0来说,数据传输率已固定成fosc/12,随着外部晶振的频率不同,数据传输率亦不相同。常用的fosc有12MHz和6MHz,所以数据传输率相应为1000×103和500×103bit/s。在此方式下,数据将自动地按固定的数据传输率发送/接收,完全不用设置。



对方式2而言,数据传输率的计算式为2SMOD·fosc/64。当SMOD=0时,数据传输率为fm/64;当SMOD=1时,数据传输率为fosc/32。在此方式下,程控设置SMOD位的状态后,数据传输率就确定了,不需要再作其他设置。



对方式1和方式3来说,数据传输率和定时器1的溢出率有关,定时器1的溢出率为:



定时器1的溢出率=定时器1的溢出次数/秒


方式1和方式3的数据传输率计算式为:


2SMOD/32×T1溢出率




根据SMOD状态位的不同,数据传输率有Tl/32溢出率和T1/16溢出率两种。由于T1溢出率的设置是方便的,因而数据传输率的选择将十分灵活。




前已叙及,定时器Tl有4种工作方式,为了得到其溢出率,而又不必进入中断服务程序,往往使T1设置在工作方式2的运行状态,也就是8位自动加入时间常数的方式。



表5-3所示常用数据传输率的设置方法。



表5-3 常用数据传输率设置方法



数据传输率/Hz


fOSC/MHz


SMOD


定时器1


C/T


方 式


重新装入值


方式0最大:1M


方式2最大:375k


方式1、3:62.5k


19.2k


9.6k


4.8k


2.4k


1.2k


110


12


12


12


11.0592


11.0592


11.0592


11.0592


11.0592


12


X


1


1


1


0


0


0


0


0


X


X


0


0


0


0


0


0


0


X


X


2


2


2


2


2


2


1


X


X


FFH


FDH


FDH


FAH


F4H


E8H


0FEEH





串行通信实例



我们先看看实例图形,我们先设计一个串口通讯的电路,一端和单片机连接,另一端和电脑连接。如下的protel所示:






此图中,使用的晶振是11.0592mhz的晶振,和电脑进行通讯,电脑上面



下面是汇编程序





      LED BIT  P1.0


      ORG   0000H


      LJMP  MAIN


      ORG   0023H


      LJMP  TRX


      ORG   0100H


MAIN: MOV   SP,    $50H


      MOV   TMOD,  #21H


      MOV   SCON,  #80H


      MOV   TH1,   #0fdh  ;9600bit


      MOV   TL1,   #0fdh


      SETB  ES


      SETB  REN


      SETB  EA


      MOV   SBUF,  #30H    ;发送数据第一位
      JNB   TI,    $


      CLR   TI


      MOV   SBUF,  #31H    ;发送数据第一位
      JNB   TI,    $


      CLR   TI


      MOV   SBUF,   #32H   ;发送数据第一位
      JNB   TI,     $


      CLR   TI


      JMP   $


TRX:   PUSH  PSW            ;串行中断


      PUSH  ACC


      PUSH  DPL


      PUSH  DPH


      JNB   RI,ENDRX


      clr   tb8


      CLR   RI


      MOV   A,SBUF


      CJNE A,#30H,ENDRX


      SETB LED0


ENDRX: POP  DPH


       POP  DPL


       POP  ACC


       POP  PSW


       RETI


END




这是一个单片机C51串口接收(中断)和发送例程,可以用来测试51单片机的中断接收和查询发送。



#include <reg51.h>


#include <string.h>


main()


{



       init_serial();      //串行口初始化


       while (1)



         {



             send_char(0X30);    //向串口发送字符串


             send_char(0X31);    //向串口发送字符串


             send_char(0X32);    //向串口发送字符串


         while(1)


        {;}



      }


}


/* 串行口初始化 */


void init_serial( void )


{



       SCON = 0x50;     //串行工作方式1, 8位异步通信方式



       TMOD |= 0x20;   //定时器1, 方式 2, 8位自动重装



       PCON |= 0x80;   //SMOD=1,表示数据传输率加倍



       TH1 = 0xFD;    //数据传输率:9600 fosc=11.0592MHz



       IE |= 0x90;   //允许串行中断



       TR1 = 1;     //启动定时器1


}


/* 向串口发送一个字符 */


void send_char( unsigned char x)


{


    SBUF=x;



    while (TI== 0 );



    TI= 0;


}


/* 串口接收中断函数 */


void serial () interrupt 4 using 3


{



     if (RI)



     {



      unsigned char x;



      RI = 0;



      x=SBUF;     //接收字符



      if ( x= 0X30 )



      {



       LED0=0;



       }



     }


}



第五部分 单片机抗干扰(09.19)
本章很多是收集整理的,我也经常参考这些,虽然不是经典名言,但是至少给了我们一个依据。
一、下面的一些系统要特别注意抗电磁干扰:
1
、微控制器时钟频率特别高,总线周期特别快的系统。
2
、系统含有大功率,大电流驱动电路,如产生火花的继电器,大电流开关等。
3
、含微弱模拟信号电路以及高精度A/D变换电路的系统。
二、为增加系统的抗电磁干扰能力采取如下措施:
1
、选用频率低的微控制器:
选用外时钟频率低的微控制器可以有效降低噪声和提高系统的抗干扰能力。同样频率的方波和正弦波,方波中的高频成份比正弦波多得多。虽然方波的高频成份的波的幅度,比基波小,但频率越高越容易发射出成为噪声源,微控制器产生的最有影响的高频噪声大约是时钟频率的3倍。
2
、减小信号传输中的畸变
微控制器主要采用高速CMOS技术制造。信号输入端静态输入电流在1mA左右,输入电容10PF左右,输入阻抗相当高,高速CMOS电路的输出端都有相当的带载能力,即相当大的输出值,将一个门的输出端通过一段很长线引到输入阻抗相当高的输入端,反射问题就很严重,它会引起信号畸变,增加系统噪声。当TpdTr时,就成了一个传输线问题,必须考虑信号反射,阻抗匹配等问题。
信号在印制板上的延迟时间与引线的特性阻抗有关,即与印制线路板材料的介电常数有关。
可以粗略地认为,信号在印制板引线的传输速度,约为光速的1/31/2之间。微控制器构成的系统中常用逻辑电话元件的Tr(标准延迟时间)为318ns之间。
在印制线路板上,信号通过一个7W的电阻和一段25cm长的引线,线上延迟时间大致在4~20ns之间。也就是说,信号在印刷线路上的引线越短越好,最长不宜超过25cm。而且过孔数目也应尽量少,最好不多于2个。
当信号的上升时间快于信号延迟时间,就要按照快电子学处理。此时要考虑传输线的阻抗匹配,对于一块印刷线路板上的集成块之间的信号传输,要避免出现TdTrd的情况,印刷线路板越大系统的速度就越不能太快。
用以下结论归纳印刷线路板设计的一个规则:
信号在印刷板上传输,其延迟时间不应大于所用器件的标称延迟时间。
3
、减小信号线间的交叉干扰:
A
点一个上升时间为Tr的阶跃信号通过引线AB传向B端。信号在AB线上的延迟时间是Td。在D点,由于A点信号的向前传输,到达B点后的信号反射和AB线的延迟,Td时间以后会感应出一个宽度为Tr的页脉冲信号。在C点,由于AB上信号的传输与反射,会感应出一个宽度为信号在AB线上的延迟时间的两倍,即2Td的正脉冲信号。这就是信号间的交叉干扰。干扰信号的强度与C点信号的di/at有关,与线间距离有关。当两信号线不是很长时,AB上看到的实际是两个脉冲的迭加。
CMOS
工艺制造的微控制由输入阻抗高,噪声高,噪声容限也很高,数字电路是迭加100~200mv噪声并不影响其工作。若图中AB线是一模拟信号,这种干扰就变为不能容忍。如印刷线路板为四层板,其中有一层是大面积的地,或双面板,信号线的反面是大面积的地时,这种信号间的交叉干扰就会变小。原因是,大面积的地减小了信号线的特性阻抗,信号
D端的反射大为减小。特性阻抗与信号线到地间的介质的介电常数的平方成反比,与介质厚度的自然对数成正比。若AB线为一模拟信号,要避免数字电路信号线CDAB的干扰,AB线下方要有大面积的地,AB线到CD线的距离要大于AB线与地距离的2~3倍。可用局部屏蔽地,在有引结的一面引线左右两侧布以地线。
4
、减小来自电源的噪声
电源在向系统提供能源的同时,也将其噪声加到所供电的电源上。电路中微控制器的复位线,中断线,以及其它一些控制线最容易受外界噪声的干扰。电网上的强干扰通过电源进入电路,即使电池供电的系统,电池本身也有高频噪声。模拟电路中的模拟信号更经受不住来自电源的干扰。
5
、注意印刷线板与元器件的高频特性
在高频情况下,印刷线路板上的引线,过孔,电阻、电容、接插件的分布电感与电容等不可忽略。电容的分布电感不可忽略,电感的分布电容不可忽略。电阻产生对高频信号的反射,引线的分布电容会起作用,当长度大于噪声频率相应波长的1/20时,就产生天线效应,噪声通过引线向外发射。
印刷线路板的过孔大约引起0.6pf的电容。
一个集成电路本身的封装材料引入2~6pf电容。
一个线路板上的接插件,有520nH的分布电感。一个双列直扦的24引脚集成电路扦座,引入4~18nH的分布电感。
这些小的分布参数对于这行较低频率下的微控制器系统中是可以忽略不计的;而对于高速系统必须予以特别注意。
6
、元件布置要合理分区
元件在印刷线路板上排列的位置要充分考虑抗电磁干扰问题,原则之一是各部件之间的引线要尽量短。在布局上,要把模拟信号部分,高速数字电路部分,噪声源部分(如继电器,大电流开关等)这三部分合理地分开,使相互间的信号耦合为最小。
7
、处理好接地线
印刷电路板上,电源线和地线最重要。克服电磁干扰,最主要的手段就是接地。
对于双面板,地线布置特别讲究,通过采用单点接地法,电源和地是从电源的两端接到印刷线路板上来的,电源一个接点,地一个接点。印刷线路板上,要有多个返回地线,这些都会聚到回电源的那个接点上,就是所谓单点接地。所谓模拟地、数字地、大功率器件地开分,是指布线分开,而最后都汇集到这个接地点上来。与印刷线路板以外的信号相连时,通常采用屏蔽电缆。对于高频和数字信号,屏蔽电缆两端都接地。低频模拟信号用的屏蔽电缆,一端接地为好。
对噪声和干扰非常敏感的电路或高频噪声特别严重的电路应该用金属罩屏蔽起来。
8
、用好去耦电容。
好的高频去耦电容可以去除高到1GHZ的高频成份。陶瓷片电容或多层陶瓷电容的高频特性较好。设计印刷线路板时,每个集成电路的电源,地之间都要加一个去耦电容。去耦电容有两个作用:一方面是本集成电路的蓄能电容,提供和吸收该集成电路开门关门瞬间的充放电能;另一方面旁路掉该器件的高频噪声。数字电路中典型的去耦电容为0.1uf的去耦电容有5nH分布电感,它的并行共振频率大约在7MHz左右,也就是说对于10MHz 以下的噪声有较好的去耦作用,对40MHz以上的噪声几乎不起作用。
1uf
10uf电容,并行共振频率在20MHz以上,去除高频率噪声的效果要好一些。在电源进入印刷板的地方和一个1uf10uf的去高频电容往往是有利的,即使是用电池供电的系统也需要这种电容。
10片左右的集成电路要加一片充放电电容,或称为蓄放电容,电容大小可选10uf。最好不用电解电容,电解电容是两层溥膜卷起来的,这种卷起来的结构在高频时表现为电感,最好使用胆电容或聚碳酸酝电容。
去耦电容值的选取并不严格,可按C=1/f计算;即10MHz0.1uf,对微控制器构成的系统,取0.1~0.01uf之间都可以。
三、降低噪声与电磁干扰的一些经验。
1.
能用低速芯片就不用高速的,高速芯片用在关键地方。
2.
可用串一个电阻的办法,降低控制电路上下沿跳变速率。
3.
尽量为继电器等提供某种形式的阻尼。
4.
使用满足系统要求的最低频率时钟。
5.
时钟产生器尽量靠近到用该时钟的器件。石英晶体振荡器外壳要接地。
6.
用地线将时钟区圈起来,时钟线尽量短。
7. I/O
驱动电路尽量靠近印刷板边,让其尽快离开印刷板。对进入印制板的信号要加滤波。
8.
高噪声区来的信号也要加滤波,同时用串终端电阻的办法,减小信号反射。
9. MCD
无用端要接高,或接地,或定义成输出端,集成电路上该接电源地的端都要接,不要悬空。
10.
闲置不用的门电路输入端不要悬空,闲置不用的运放正输入端接地,负输入端接输出端。
11.
印制板尽量使用45折线而不用90折线布线以减小高频信号对外的发射与耦合。
12.
印制板按频率和电流开关特性分区,噪声元件与非噪声元件要距离再远一些。
13.
单面板和双面板用单点接电源和单点接地、电源线、地线尽量粗,经济是能承受的话用多层板以减小电源,地的容生电感。
14.
时钟、总线、片选信号要远离I/O线和接插件。
15.
模拟电压输入线、参考电压端要尽量远离数字电路信号线,特别是时钟。
16.
A/D类器件,数字部分与模拟部分宁可统一下也不要交叉。
17.
时钟线垂直于I/O线比平行I/O线干扰小,时钟元件引脚远离I/O电缆。
18.
元件引脚尽量短,去耦电容引脚尽量短。
19.
关键的线要尽量粗,并在两边加上保护地。高速线要短要直。
20.
对噪声敏感的线不要与大电流,高速开关线平行。
21.
石英晶体下面以及对噪声敏感的器件下面不要走线。
22.
弱信号电路,低频电路周围不要形成电流环路。
23.
任何信号都不要形成环路,如不可避免,让环路区尽量小。
24.
每个集成电路一个去耦电容。每个电解电容边上都要加一个小的高频旁路电容
25.
用大容量的钽电容或聚酷电容而不用电解电容作电路充放电储能电容。使用管状电容时,外壳要接地
附件:

keil c语法参考

很多人对keilc不是很了解 我收集了一些资料,提供如下:
Keil C51 vs 标准C
深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一。因为大多数扩展功能都是直接针对8051系列CPU硬件的。大致有以下8类:
l 8051存储类型及存储区域
2 存储模式
3 存储器类型声明
4 变量类型声明
5 位变量与位寻址
6 特殊功能寄存器(SFR)
7 C51指针
8 函数属性
具体说明如下(8031为缺省CPU)。
1. 第一节 Keil C51扩展关键字
C51 V4.0版本有以下扩展关键字(共19个):
_at_ idata sfr16 alien interrupt small
bdata large _task_ Code bit pdata
using reentrant xdata compact sbit data sfr
2. 第二节 内存区域(Memory Areas):
1. 1. Pragram Area:
由Code说明可有多达64kBytes的程序存储器
2. 2. Internal Data Memory:
内部数据存储器可用以下关键字说明:
data:直接寻址区,为内部RAM的低128字节 00H~7FH
idata:间接寻址区,包括整个内部RAM区 00H~FFH
bdata:可位寻址区,  20H~2FH
3. 3. External Data Memory
外部RAM视使用情况可由以下关键字标识:
xdata:可指定多达64KB的外部直接寻址区,地址范围0000H~0FFFFH
pdata:能访问1页(25bBytes)的外部RAM,主要用于紧凑模式(Compact Model)。
4. 4. Speciac Function Register Memory
8051提供128Bytes的SFR寻址区,这区域可位寻址、字节寻址或字寻址,用以控制定时器、计数器、串口、I/O及其它部件,可由以下几种关键字说明:
sfr:字节寻址 比如 sfr P0=0x80;为PO口地址为80H,“=”后H~FFH之间的常数。
sfr16:字寻址,如sfr16 T2=0xcc;指定Timer2口地址T2L=0xcc T2H=0xCD
sbit:位寻址,如sbit EA="0xAF";指定第0xAF位为EA,即中断允许
还可以有如下定义方法:
sbit 0V=PSW^2;(定义0V为PSW的第2位)
sbit 0V=0XDO^2;(同上)
或bit 0V-=0xD2(同上)。

存储类型与存储区关系

     data > 可寻址片内ram

     bdata > 可位寻址的片内ram

     idata > 可寻址片内ram,允许访问全部内部ram

     pdata > 分页寻址片外ram (MOVX @R0) (256 BYTE/页)

     xdata > 可寻址片外ram (64k 地址范围)

     code > 程序存储区 (64k 地址范围),对应MOVC @DPTR

     二、指针类型和存储区的关系对变量进行声明时可以指定变量的存储类型如:

     uchar data x和data uchar x相等价都是在内ram区分配一个字节的变量。同样对于指针变量的声明,因涉及到指针变量本身的存储位置和指针所指向的存储区位置不同而进行相应的存储区类型关键字的使用如:uchar xdata * data pstr是指在内ram区分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata区("*"前xdata关键字的作用),可能初学C51时有点不好懂也不好记。没关系,我们马上就可以看到对应“*”前后不同的关键字的使用在编译时出现什么情况。......

     uchar xdata tmp[10]; //在外ram区开辟10个字节的内存空间,地址是外ram的0x0000-0x0009

     ......第1种情况:uchar data * data pstr;

     pstr="tmp";首先要提醒大家这样的代码是有bug的, 他不能通过这种方式正确的访问到tmp空间。 为什么?我们把编译后看到下面的汇编代码:MOV 0x08,#tmp(0x00) ;0x08是指针pstr的存储地址看到了吗!本来访问外ram需要2 byte来寻址64k空间,但因为使用data关键字(在"*"号前的那个),所以按KeilC编译环境来说就把他编译成指向内ram的指针变量了,这也是初学C51的朋友们不理解各个存储类型的关键字定义而造成的bug。特别是当工程中的默认的存储区类为large时,又把tmp[10] 声明为uchar tmp[10] 时,这样的bug是很隐秘的不容易被发现。第2种情况:uchar xdata * data pstr;

     pstr = tmp;这种情况是没问题的,这样的使用方法是指在内ram分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata区("*"前xdata关键字的作用)。编译后的汇编代码如下。MOV 0x08,#tmp(0x00) ;0x08和0x09是在内ram区分配的pstr指针变量地址空间

     MOV 0x09,#tmp(0x00)这种情况应该是在这里所有介绍各种情况中效率最高的访问外ram的方法了,请大家记住他。第3种情况:uchar xdata * xdata pstr;

     pstr="tmp";这中情况也是对的,但效率不如第2种情况。编译后的汇编代码如下。MOV DPTR, #0x000A ;0x000A,0x000B是在外ram区分配的pstr指针变量地址空间

     MOV A, #tmp(0x00)

     MOV @DPTR, A

     INC DPTR

     MOV A, #tmp(0x00)

     MOVX @DPTR, A这种方式一般用在内ram资源相对紧张而且对效率要求不高的项目中。第4种情况:uchar data * xdata pstr;

     pstr="tmp";如果详细看了第1种情况的读者发现这种写法和第1种很相似,是的,同第1 种情况一样这样也是有bug的,但是这次是把pstr分配到了外ram区了。编译后的汇编代码如下。MOV DPTR, #0x000A ;0x000A是在外ram区分配的pstr指针变量的地址空间

     MOV A, #tmp(0x00)

     MOVX @DPTR, A第5种情况:uchar * data pstr;

     pstr="tmp";大家注意到"*"前的关键字声明没有了,是的这样会发生什么事呢?下面这么写呢!对了用齐豫的一首老歌名来说就是 “请跟我来”,请跟我来看看编译后的汇编代码,有人问这不是在讲C51吗? 为什么还要给我们看汇编代码。C51要想用好就要尽可能提升C51

     编译后的效率,看看编译后的汇编会帮助大家尽快成为生产高效C51代码的高手的。还是看代码吧!MOV 0x08, #0X01 ;0x08-0x0A是在内ram区分配的pstr指针变量的地址空间

     MOV 0x09, #tmp(0x00)

     MOV 0x0A, #tmp(0x00)注意:这是新介绍给大家的,大家会疑问为什么在前面的几种情况的pstr指针变量都用2 byte空间而到这里就用3 byte空间了呢?这是KeilC的一个系统内部处理,在KeilC中一个指针变量最多占用 3 byte空间,对于没有声明指针指向存储空间类型的指针,系统编译代码时都强制加载一个字节的指针类型分辩值。具体的对应关系可以参考KeilC的help中C51 User's Guide。第6种情况:uchar * pstr;

     pstr="tmp";这是最直接最简单的指针变量声明,但他的效率也最低。还是那句话,大家一起说好吗!编译后的汇编代码如下。MOV DPTR, #0x000A ;0x000A-0x000C是在外ram区分配的pstr指针变量地址空间

     MOV A, #0x01

     MOV @DPTR, A

     INC DPTR

     MOV DPTR, #0x000A

     MOV A, #tmp(0x00)

     MOV @DPTR, A

     INC DPTR

     MOV A, #tmp(0x00)

     MOVX @DPTR, A这种情况很类似第5种和第3种情况的组合,既把pstr分配在外ram空间了又增加了指针类型的分辨值。 小结一下:大家看到了以上的6种情况,其中效率最高的是第2种情况,既可以正确访问ram区又节约了代码,效率最差的是第 6种,但不是说大家只使用第2种方式就可以了,还要因情况而定,一般说来应用51系列的系统架构的内部ram资源都很紧张,最好大家在定义函数内部或程序段内部的局部变量使用内ram,而尽量不要把全局变量声明为内ram区中。所以对于全局指针变量我建议使用第 3 种情况,而对于局部的指针变量使用第2种

本征库函数(intrinsic routines)和非本征证库函数
C51强大功能及其高效率的重要体现之一在于其丰富的可直接调用的库函数,多使用库函数使程序代码简单,结构清晰,易于调试和维护,下面介绍C51的库函数系统。
C51提供的本征函数是指编译时直接将固定的代码插入当前行,而不是用ACALL和LCALL语句来实现,这样就大大提供了函数访问的效率,而非本征函数则必须由ACALL及LCALL调用。
C51的本征库函数只有9个,数目虽少,但都非常有用,列如下:
_crol_,_cror_:将char型变量循环向左(右)移动指定位数后返回
_iror_,_irol_:将int型变量循环向左(右)移动指定位数后返回
_lrol_,_lror_:将long型变量循环向左(右)移动指定位数后返回
_nop_: 相当于插入NOP
_testbit_: 相当于JBC bitvar测试该位变量并跳转同时清除。
_chkfloat_: 测试并返回源点数状态。
使用时,必须包含#inclucle <intrins.h>一行。
如不说明,下面谈到的库函数均指非本征库函数。
第二节 几类重要库函数
1. 专用寄存器include文件
例如8031、8051均为REG51.h其中包括了所有8051的SFR及其位定义,一般系统都必须包括本文件。
2. 绝对地址include文件absacc.h
该文件中实际只定义了几个宏,以确定各存储空间的绝对地址。
3. 动态内存分配函数,位于stdlib.h中

4. 缓冲区处理函数位于“string.h”中
其中包括拷贝比较移动等函数如:
memccpy memchr memcmp memcpy memmove memset
这样很方便地对缓冲区进行处理。
5. 输入输出流函数,位于“stdio.h”中
流函数通8051的串口或用户定义的I/O口读写数据,缺省为8051串口,如要修改,比如改为LCD显示,可修改lib目录中的getkey.c及putchar.c源文件,然后在库中替换它们即可。


Keil C51库函数原型列表
1. CTYPE.H
bit isalnum(char c);
bit isalpha(char c);
bit iscntrl(char c);
bit isdigit(char c);
bit isgraph(char c);
bit islower(char c);
bit isprint(char c);
bit ispunct(char c);
bit isspace(char c);
bit isupper(char c);
bit isxdigit(char c);
bit toascii(char c);
bit toint(char c);
char tolower(char c);
char __tolower(char c);
char toupper(char c);
char __toupper(char c);
2. INTRINS.H
unsigned char _crol_(unsigned char c,unsigned char b);
unsigned char _cror_(unsigned char c,unsigned char b);
unsigned char _chkfloat_(float ual);
unsigned int _irol_(unsigned int i,unsigned char b);
unsigned int _iror_(unsigned int i,unsigned char b);
unsigned long _irol_(unsigned long l,unsigned char b);
unsigned long _iror_(unsigned long L,unsigned char b);
void _nop_(void);
bit _testbit_(bit b);
3. STDIO.H
char getchar(void);
char _getkey(void);
char *gets(char * string,int len);
int printf(const char * fmtstr[,argument]…);
char putchar(char c);
int puts (const char * string);
int scanf(const char * fmtstr.[,argument]…);
int sprintf(char * buffer,const char *fmtstr[;argument]);
int sscanf(char *buffer,const char * fmtstr[,argument]);
char ungetchar(char c);
void vprintf (const char *fmtstr,char * argptr);
void vsprintf(char *buffer,const char * fmtstr,char * argptr);
4. STDLIB.H
float atof(void * string);
int atoi(void * string);
long atol(void * string);
void * calloc(unsigned int num,unsigned int len);
void free(void xdata *p);
void init_mempool(void *data *p,unsigned int size);
void *malloc (unsigned int size);
int rand(void);
void *realloc (void xdata *p,unsigned int size);
void srand (int seed);
5. STRING.H
void *memccpy (void *dest,void *src,char c,int len);
void *memchr (void *buf,char c,int len);
char memcmp(void *buf1,void *buf2,int len);
void *memcopy (void *dest,void *SRC,int len);
void *memmove (void *dest,void *src,int len);
void *memset (void *buf,char c,int len);
char *strcat (char *dest,char *src);
char *strchr (const char *string,char c);
char strcmp (char *string1,char *string2);
char *strcpy (char *dest,char *src);
int strcspn(char *src,char * set);
int strlen (char *src);
char *strncat (char 8dest,char *src,int len);
char strncmp(char *string1,char *string2,int len);
char strncpy (char *dest,char *src,int len);
char *strpbrk (char *string,char *set);
int strpos (const char *string,char c);
char *strrchr (const char *string,char c);
char *strrpbrk (char *string,char *set);
int strrpos (const char *string,char c);
int strspn(char *string,char *set);



#############################################################################
#############################################################################




老练最新的“单片机复位电路分析专题”

http://bbs.eeworld.com.cn/thread-66492-1-1.html




想学习一下
引用:
原帖由 317179403 于 2008-9-18 09:26 发表
想学习一下
有什么问题,317179403 可以随时发问哦,专家会定期给与解答。
期待这样的活动已经很久了,感谢eeworld
要是结合proteus软件讲就好了
我会一直关注的
发新话题