精华内容
下载资源
问答
  • 单片机C语言指针意义浅析—Keil-C51

    千次阅读 2019-05-15 16:28:28
    单片机C语言指针意义浅析—Keil-C51 2017年06月24日 16:33:00lucky_草木皆兵阅读数:482 通常认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上,甚至认为指针是C语言的灵魂。这里说通常,...

    单片机C语言指针意义浅析—Keil-C51

    2017年06月24日 16:33:00 lucky_草木皆兵 阅读数:482

    通常认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上,甚至认为指针是C语言的灵魂。这里说通常,是广义上的,因为随着编程语言的发展,指针也饱受争议,并不是所有人都承认指针的“强大”和“优点”。在单片机领域,指针同样有着应用,本章节针对Keil C-51环境下的指针意义做简要分析。



    1     指针与变量
    指针是一个变量,它与其他变量一样,都是RAM中的一个区域,且都可以被赋值,如程序①所示。
    #include "REG52.H"        
    unsigned int j;
    unsigned char *p;
    void main()
    {
             while(1)
             {
                       j=0xabcd;
                       p=0xaa;
             }
    }
    在Debug Session模式下,将鼠标指针移到到变量“j”“p”位置,可以显示变量的物理地址,如图1-1、1-2所示。 






    图中箭头所指处即为变量在RAM中的“首地址”,为什么是“首地址”呢?变量根据类型可分为8位(单字节)、16位(双字节),程序中变量“j”是无符号整型,所占物理空间应为2字节,而在8位单片机中,RAM的一个存储单元大小是8位,即1字节,因此需2个存储单元才满足变量“j”长度。所以实际上变量“j”的物理地址为“08H”“09H”。同理,“p(D:0x0A)”即变量“p”的首地址为“0AH”。
    下面通过单步执行程序来观察RAM内的数据变化,打开两个Memory Windows窗口,在Keil软件下方显示为Memory1和Memory2,在两个窗口中,分别做如图2-1、2-2所示的设置。






    两个Address填写的内容分别是:D:0x08、D:0x0A,即变量“j”和变量“p”的首地址,输入后回车,便可监视RAM中该地址下的数据。设置好后,准备调试。

    在Debug Session模式中,箭头所指处即为即将执行的语句,单击“Step”功能按钮(或按F11键),让程序运行,如图3所示。

    第一次单击“Step”按钮后,Memory1窗口内数据如图4所示。

    由调试结果可知,08H数据由00H变为ABH,09H数据由00H变为CDH,出现这种变化是因为执行了语句j=0xabcd;08H为变量“j”高八位,存储“AB”,09H为变量“j”低八位,存储“CD”。
    第二次单击“Step”按钮,执行语句:p=0xaa;此时需观察Memory2窗口内数据,如图5所示。

    由调试结果可知,0CH处值由00变为“AAH”,程序相吻合。这里需要注意,在Keil C-51编译环境下,指针变量,不管长度是单字节或是双字节,指针变量所占字节数为3字节。故此处“AAH”不是存储在0AH而存储在0CH(0A+2)地址中。
    综上所述,指针实际上是变量,都是映射到RAM中的一段存储空间,区别是,指针占用3字节,而其他变量可根据需要设定其所占RAM是1字节(char)、2字节(int)、4字节(long)。

    2       指针作用
    指针的作用是什么呢?先来看下面的程序:
    程序②
    #include "REG52.H"         
    unsigned chartab1[8]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
    unsigned char codetab2[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80}; 
    unsigned char N1,N2;
    void main()
    {
                N1=tab1[0];
                N2=tab2[0];
    }
    显然,程序执行的结果是N1=0x01,N2=0x10。这里都是讲数组内的数据赋值给变量,但存在区别,tab1数组使用的是单片机RAM空间,而tab2数组使用的是单片机程序存储区(ROM)空间。尽管使用C语言为变量赋值时语句相同,但编译结果并不相同,此程序编译后的结果如图6所示。
     

    由编译结果可知,N1=tab1[0]语句实际上是直接寻址,而N2=tab2[0]是寄存器变址寻址。不管是何种寻址方式,都是将一个物理地址内的数据取出来使用:tab1数组中,tab[0]对应的RAM地址是0x0A,tab[1]对应的RAM地址是0x0B……以此类推;tab2数组中,tab[0]对应的ROM地址是0x00A5,tab[1]对应的ROM地址是0x00A6……以此类推。不管这些数组或变量所在的RAM或ROM地址如何,用户最终需要的是数组或变量的数据,而指针,就是通过变量或数组的物理地址访问数据,也就是说,通过指针,同样可以访问数组或变量数据。现将程序②做出调整,得到程序③如下:
    #include "REG52.H"         
    unsigned chartab1[8]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
    unsigned char code tab2[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};
    unsigned char N1,N2;
    unsigned char  *p;
    void main()
    {        
             unsignedchar i;
             p=tab1;
             for(i=0;i<8;i++,p++)
             N1=*p;
             
             p=tab2;
             for(i=0;i<8;i++,p++) 
             N2=*p;
    }
    程序执行结果:tab1数组内的8个数值依次被赋值给N1;tab2数组内的8个数值依次被赋值给N2;
    程序③执行Debug Session功能后,打Watch Windows窗口,在Watch1窗口下添加需要监视的变量,此处为“p”和“N1”,如图7所示。

    Value为当前变量数值,程序为运行前,p值为0x00,单击Step按键功能后,执行p=tab1;p值变为0x0A,如图8所示。

    0x0A是什么值呢?将鼠标移至tab1数组位置,可显示出数组所在的物理地址,0x0A就是数组tab1的首地址,如图9所示。

    p=tab1就是将tab1数组的首地址赋值给变量p,执行p++即地址值加1;*p则是此物理地址内的具体数据,因此for循环中,N1=*p是依次将tab1数组中的数据赋值给变量N1。由此可见,指针是作为一个变量,指向某一个地址。
    那么指针到底是如何将某个地址内的数据“拿”出来的?下面通过N1=*p语句做演示说明,N1=*p编译后的汇编代码如图10所示。

    C:0x00A0至C:0x00A9的汇编代码即是C程序中的N1=*p。程序先将变量p的值赋值给R3、R2、R1三个通用寄存器,程序为:
    MOV   R3,p(0x12)
    MOV   R2,0x13
    MOV   R1,0x14
    然后调用了一个子函数:LCALL  C?CLDPTR(C:00E4),而C程序中,未定义或使用任何子函数,那么这个子函数是哪里来的?作用是什么?根据标号C:00E4可找到该子函数,程序代码如下:

    C:0x00E4   BB0106   CJNE     R3,#0x01,C:00ED
    C:0x00E7   8982     MOV      DPL(0x82),R1
    C:0x00E9   8A83     MOV      DPH(0x83),R2
    C:0x00EB   E0       MOVX     A,@DPTR
    C:0x00EC   22       RET      
    C:0x00ED   5002     JNC      C:00F1
    C:0x00EF   E7       MOV      A,@R1
    C:0x00F0   22       RET      
    C:0x00F1   BBFE02   CJNE     R3,#0xFE,C:00F6
    C:0x00F4   E3       MOVX     A,@R1
    C:0x00F5   22       RET      
    C:0x00F6    8982    MOV      DPL(0x82),R1
    C:0x00F8   8A83     MOV      DPH(0x83),R2
    C:0x00FA   E4       CLR      A
    C:0x00FB   93       MOVC     A,@A+DPTR
    C:0x00FC   22       RET      

    此程序功能是:先用R3寄存器的值与0x01比较,当R3的值大于0x01时,再和0xFE做比较,比较的结果有如下情况:
    (1)R3的值等于0x01时,执行如下程序:
    C:0x00E7   8982     MOV      DPL(0x82),R1
    C:0x00E9   8A83     MOV      DPH(0x83),R2
    C:0x00EB   E0       MOVX     A,@DPTR
    C:0x00EC   22       RET      
    程序功能:读取扩展RAM内的数据并赋值给A,寻址范围0~65535。当数组用xdata定义时,会跳转到此处。
    (2)R3的值小于0x01即等于0x00时,执行如下程序:
    C:0x00EF   E7       MOV      A,@R1
    C:0x00F0   22       RET  
    程序功能:读取单片机内部256字节RAM内的数据并赋值给A,寻址范围0~255。当数组用data或idata定义时,会跳转到此处。如执行N1=*p语句时,即跳转到自处,读取内部RAM地址内的数据。    
    (3)R3的值不等于0x00或0x01时,通过JNC指令跳转到C:0x00F1处,开始与0xFE做比较。R3的值等于0xFE时,执行如下程序:
    C:0x00F4   E3       MOVX     A,@R1
    C:0x00F5   22       RET  
    程序功能:读取单片机片外RAM内的数据并赋值给A,寻址范围0~255。当数组用pdata定义时,会跳转到此处。通常8051单片机不使用pdata定义变量或数组。
    (4)R3的值不等于0xFE时,即R3的值等于0xFF时,跳转到C:0x00F6处执行如下程序:
    C:0x00F6   8982     MOV      DPL(0x82),R1
    C:0x00F8   8A83     MOV      DPH(0x83),R2
    C:0x00FA   E4       CLR      A
    C:0x00FB   93       MOVC     A,@A+DPTR
    C:0x00FC   22       RET
    程序功能:读取单片机内部ROM内的数据并赋值给A,寻址范围0~65535。当数组用code定义时,如程序③中,tab2数组用code定义,执行p=tab2后,R3的值被赋值为0xFF,再执行N2=*p语句时,即跳转到自处,读取内部ROM地址内的数据。  
    由此可见,子函数“C?CLDPTR”的作用是,根据数据所在存储空间,用不同的寻址方式读取某地址下的数据。R3用于确定寻址方式,R3的值与对应的寻址方式对应关系为:
    1、R3值等于0x00时,片内RAM间接寻址;此时数据用dataidata定义。
    2、R3值等于0x01时,片外RAM(扩展RAM)间接寻址;此时数据用xdata定义。
    3、R3值等于0xFE时,片外RAM(扩展RAM)低246字节间接寻址;此时数据用pdata定义
    4、R3值等于0xFF时,从存储存储器(ROM)进行变址寻址;此时数据用code定义。

    3、指针结构
    R3、R2、R1的值是RAM中0x12、0x13、0x14地址内的值,即变量p映射的RAM地址。而而8位单片机中,不管是何种寻址方式,最大寻址范围是2字节长度(0~65535),为什么指针*p却占用了3字节RAM空间呢?下面通过程序④说明。
    程序④:
    #include "REG52.H"         
    unsigned char tab1[8];
    unsigned char idata tab2[8];
    unsigned char xdata tab3[8];   
    unsigned char pdata tab4[8];
    unsigned char codetab5[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80}; 
    unsigned char  *p;
    void main()
    {        
             p=tab1;
             p=tab2;
             p=tab3;
             p=tab4;
             p=tab5;
    }
    在Debug Session模式下可知,程序中数组与变量所映射的物理地址为及物理存储区分别为:
    tab1 :        0x08~0x0F                        单片机内部RAM
    tab2:     0x03~0x1A                       单片机内部RAM(idata)
    tab3:     0x08~0x0F                        单片机扩展RAM(xdata)
    tab4:     0x00~0x08                        单片机扩展RAM低256字节(pdata)
    tab5:     0x0003D~0x0044            单片机程序存储区(code)
    p:            0x10~0x12                        单片机内部RAM
    注:扩展RAM可以在物理上可以分为片内或片外,如STC15系列增强型单片机的扩展RAM与单片机是封装在一起的,即片内扩展RAM;传统8051单片机没有片内扩展RAM,需连接外部RAM芯片,此为片外扩展RAM。
    在Memory Windows窗口下,监视变量p映射的RAM地址:0x10~0x12的数值变化,如图11所示。

    通过“Step”功能按钮执行住函数中的5调语句,可观察到0x10~0x12寄存器的数据变化:

    执行p=tab1后,0x10、0x11、0x12:0x00、0x00、0x08
    执行p=tab2后,0x10、0x11、0x12:0x00、0x00、0x13
    执行p=tab3后,0x10、0x11、0x12:0x01、0x00、0x08
    执行p=tab4后,0x10、0x11、0x12:0xFE、0x00、0x00
    执行p=tab5后,0x10、0x11、0x12:0xFF、0x00、0x3D
    由此可知,0x10的赋值取决于p指向的物理存储区,0x11、0x12的值是数据存储区的地址。指针所映射的首地址,会根据指向的物理存储区被编译器赋不同的值:0x00,0x01,0xFE,0xFF。这与程序③得到的结论一致,程序③中,寄存器R3、R2、R1对应值实际上就是指针所映射的3字寄存器数值。
    结合程序③编译分析,当需要引用某物理地址内数据时,会调用“C?CLDPTR”函数,函数功能就是根据这些赋值确定使用何种寻址方式引用数据。而这一过程包括“C?CLDPTR”函数都是编译器自动完成的。
    在汇编语言中,R1寄存器可以用于间接寻址,如:MOV  A,@R1。不能写为MOV A,@12H。因此在程序③中,将变量p对应的3字节数据赋值给R3、R2、R1。
    综上所述,Keil C-51编译环境下,指针是一个占3字节的特殊变量,编译器编译程序时,自动生成判断寻址方式的子函数,并根据根据目标数据所在的物理存储区不同,为指针首字节赋值,根据赋值的不同,进行不同方式的寻址;指针的后2字节,用于存放引用的地址。

    调试训练:
    下面的程序编译器会怎样编译?与程序③有何不同?请根据程序③和程序④的分析方式分析程序⑤的执行结果。
    程序⑤
    #include "REG52.H"         
    unsigned char tab1[8];
    unsigned char codetab2[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; 
    unsigned char  *p;
    void main()
    {        
             unsignedchar i;
             p=tab1;
             for(i=0;i<8;i++,p++)
             *p=i;
             
             p=tab2;
             for(i=0;i<8;i++,p++) 
             *p=i;
    }

    思考:下列语句中:

    p=tab2;

    for(i=0;i<8;i++,p++)

    *p=i;

    执行完for循环后,tab2数组内的值会改变吗?为什么?

    4、指针意义

    在汇编编程中,由于单片机数据存放的物理存储区不同,导致有不同的寻址方式,用户进行必须根据这一规律设计程序。而在C语言中,不管目标数据所在的物理存储区如何,指针都可指向该地址,并自动编译寻址方式。

    但指针并不是万能的,如程序⑤中:

    p=tab2;

    for(i=0;i<8;i++,p++)

    *p=i;

    这些语句编译时并不会报错,但却不能实现功能,因为tab2数组是定义在程序存储器(ROM)的常量数组,ROM内的数据更改是不能通过这种方式实现的。因此,当用户不明确单片机的物理存储区特性时,使用指针会容易出错。先将程序⑤中的主函数语句做如下修改,得到程序⑥:

    #include"REG52.H"         

    unsignedchar tab1[8];

    unsignedchar code tab2[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

    unsignedchar  *p;

    voidmain()

    {        

             unsigned char i;

             for(i=0;i<8;i++,p++)

             tab1[ i]=i;

             

             for(i=0;i<8;i++,p++)

             tab2[ i]=i;

    }

    单独看第一个for循环,可实现与程序⑤一样的效果,即tab1数组内被赋值为:0,1,2,3,4,5,6,7。

    第二个for循环从语句上可认为是与程序⑤功能相同,但实际上,不管是程序⑤还是程序⑥,都不能实现对tab2数组的赋值。但在程序⑥中,编译器会提示错误,如图12所示。

    因此,指针的使用不当,不仅会带来程序运行结果的不正确,同时也难以发现这些错误。

    对比程序⑤和程序⑥中的两段程序:

    p=tab1;                                                      for(i=0;i<8;i++,p++)                        

    for(i=0;i<8;i++,p++)                                   tab1[ i]=i;

    *p=i;


    它们执行的结果是一样的,那么哪种更好呢?对于初学者来说,显然是后者,因为后者更易于理解程序含义,而前者必须要理解指针在此处的作用;那么对于有经验的程序员呢?也是后者,因为程序执行效率上,后者也要大于前者,因为程序⑤在编译过程中,编译器始终会生成一个子函数用于确定寻址方式,再赋值;程序⑥则是直接确定了寻址方式执并行进行赋值。尽管执行效率的降低在接受范围内,但对于一个简单、明了的功能来说,用简单的方式实现要比复杂方式合理。
    设计者在程序中使用指针的目的往往是让程序具有可移植性,但8051单片机的功能是有限的,它实现的功能相对固化,如时间显示、数据采集等等,这些功能确定后,几乎不会做出更改,基于此特点,8051单片机的代码代码量都不长。因此即便是不同构架的单片机程序互相移植,代码的修改并不复杂,移植过程中,也几乎都是针对不同构架单片机的I/O工作方式不同、指令周期不同做常规修改;或是关键字的修改。因此合理的设计单片机程序,尽可能的提高程序的效率、稳定性、可阅读性才是程序设计的核心主旨。指针在8051单片机中固然可以使用,但并不能说明指针的使用就一定是高效、准确、易于他人理解。

    展开全文
  • 单片机原理应用第二版课后 习题答案 第1章单片机概述参考答案 1.1答微...计算机系统单片机是集成在一个芯片上的用于 测控目的的单片微计算机 嵌入式处理器一般意义上讲是指嵌入系统的单 片机DSP嵌入式微处理器目前多
  • 单片机

    2016-10-08 22:52:00
    单片机   一.绘图,画板   2G,3G,4G均指信道载波的频率。   手工PCB和工业PCB的区别 1.多了一层铺油。 2.多了一层丝印。   一面有,一面没有,叫作单层版。 电阻:R开头 电容:C开头   绘图...

    单片机

     

    一.绘图,画板

     

    2G,3G4G均指信道载波的频率。

     

    手工PCB和工业PCB的区别

    1.多了一层铺油。

    2.多了一层丝印。

     

    一面有,一面没有,叫作单层版。

    电阻:R开头

    电容:C开头

     

    绘图软件

    1.民用:protelprotel99seprotel DXPAltium

    2.军用,公司用:Cadende spd MentorGraphics

     

    过孔(via):连接上下两层的走线

    焊盘(pad):连接上下两层的走线,同时,焊接元器件

     

    通孔:多层板连接上下表面接线的孔。

    盲孔:多层板连接中间的孔。

     

    pcb层介绍

    Solder层为露铜层,即铺绿油的层

    Paste层为钢网层,工厂加工时需要,自己做PCB不需要。 是工厂批量加工时使用的。

    Keep-Out Layer 分割层,用来给板子规定形状,或者打洞。

    Top Layer /  Bottom Layer 走线层。

    Top Layer /  Bottom overlay 丝印层。

     

    符号:在原理图中使用,标识元器件。只是一种符号,并无实际的电气连接。

    封装:元器件的实际尺寸,包括外形尺寸,高度,引脚尺寸,引脚间距,有实际的电气连接

     

    符号->原理图

    封装->PCB

     

     

    1mil = 1/1000inch = 0.00254cm = 0.0254mm

    1inch = 1000mil =2.54cm = 25.4mm

    100mil = 2.54mm

    1mm大约为40mil

     

    所有的电源都是成对出现的,有VCC必有VDD

     

    蛇形走线。线要求等长,尽量保证信号到达目的地的时间一样。

     

    ①新建工程②新建一个原理图工程③新建一个PCB

     

    工程文件名后缀:prjPCB

    原理图工程:SohDoc->保存到自己创建的目录

    pcb后缀名:PcbDoc->还是保存到自己创建的目录

     

    网格:叫格点 右击->option->Document Option

    封装:footprint

     

    都要放在一个目录下。

     

    原理图中所有符号必须是唯一的  Tab键出菜单  空格旋转90

     

    CAPD电解电容

    按住crtl键是保持连接的,右键按住不放拖拽

     

    开漏口的作用是用来匹配不同电压的外设

     

    X是镜像(左右)

    Y是镜像(上下)

     

    RFID

    晶振于单片机相当于心脏于人类

     

    104 10*104次方 = 10^5PF = 100NF

     

    焊盘:内径30mil   外径60mil

     

    两层板过孔一般是内径12mil 外径24mil

     

    ctrl+m是量距离

    英文状态下按q ,单位就转化了

     

     

    每个器件都有一个中心点

    中心点作用之一:可以看两个器件之间的距离

     

    Edit ->Set Reference->设置中心点

     

    ①走线最短原则 ②晶振尽量靠近芯片摆放 ③滤波电路,滤谁就放到谁的旁边 ④尽量紧凑

     

    Toggle Visible 关格点

     

    Design - Board shape -reDefing Board shape 重新定义板子类型

     

    电源线 线宽 30mil

    普通信号线 15mil

     

    检查线距,线宽。以及器件之间的间距

     

    Design - rules 设置规则

     

    间距 15mil clearance

              shortCircuit 短路

    Routing走线

               width 15mil  Min  Max设置为一样的

               width下新建一个规则 , Vcc Net Vcc 改为30mil

               Routing Via Style 12mil 24mil

     

     

    Auto Route ->Auto ->Run 自动布线

     

    覆铜:pcb的覆铜一般都是连在地线上,增大底线面积,有利于地线阻抗降低,使电源和传输信号稳定,在高频的信号线附近覆铜,可大大减少电磁辐射干扰,总的来说增强了pcb的电磁兼容性,,提高板子的扛干扰能力。

    覆铜的意义在于,减少地线阻抗,提高扛干扰能力。

     

     

     

    展开全文
  • 51单片机的延时定时器

    万次阅读 2017-02-25 14:35:03
    经常看些东西,有时在书上,有时在网上,还有的是突然醒悟,也该...2.软件延时:软件延时有时候不能够做到非常精确地延时,主要靠循环体或是一些无意义的指令来完成。 单片机都有一个属于自己的晶振频率:11.0592Mhz

           经常看些东西,有时在书上,有时在网上,还有的是突然醒悟,也该做一些总结,最近想总结一下单片机的定时以及延时问题。

    单片机主要是两种延时方式:

    1.硬件延时:要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;

    2.软件延时:软件延时有时候不能够做到非常精确地延时,主要靠循环体或是一些无意义的指令来完成。

    单片机都有一个属于自己的晶振频率:11.0592Mhz(主要是为了设置波特率的方便),12Mhz6Mhz等(后面的例子全都用12M晶振)。对于12Mhz的晶振频率,一个机器周期为1us,对于51单片机的库函数就有nop()这个函数(调用时需要#include<intrins.h>),实现延时一个机器周期。那么就有了简单的软件延时程序。可以有delay5us,delay10us等程序,只需要在程序里用nop()就可以了,但是要注意调用该函数需要有一个调用指令(2us),结束后也有个结束指令(2us),而且在函数里调用该函数,只有最内层的函数有结束指令。

    例如:

    void Delay5us( ) {

    _NOP_( );

    }

    void Delay10us()

    {

    _NOP_( );

    _NOP_( );

    _NOP_( );

    _NOP_( );

    _NOP_( );

    _NOP_( );

    }

    分别是两个不同的延时函数。


    再就是我们会经常使用的for循环延时程序了,我现在也是在学单片机,在郭天祥老师的程序里经常会有

    void delay(unsigned int i)

    {

    while(i--);

    }

    在这个程序里,如果没有中断完全可以用仿真模拟的方法并自己调整,直到自己想要的延时时间,因为在后面中断,串口,模拟时序的时候并没有那么精确的延时,都是一个比较大的时间段,但是学了就尽量弄得精确一些。(如果是大神完全可以抠汇编,用示波器,当然我现在都不会。),因为这是c语言编程,不是汇编,汇编的一条指令(也就是机器指令)机器周期是一定的,也就是说可以很精确,但是c不行,需要取决于很多东西(如编译器,cpu等等)。

    继续说这个程序,他就是用不断循环的做一些无意义的事,达到延时的目的。因为你不能准确的知道一条c指令确是多少时间(或者说会有误差),在上面的程序里,当i=1时,大约延时10us。下面再给出几个延时函数,仅供参考。

    200ms延时子程序

    程序:

    void delay200ms(void)

    {

    unsigned char i,j,k;

    for(i=5;i>0;i--)

    for(j=132;j>0;j--)

    for(k=150;k>0;k--);

    }

    10ms延时子程序

    程序:

    void delay10ms(void)

    {

    unsigned char i,j,k;

    for(i=5;i>0;i--)

    for(j=4;j>0;j--)

    for(k=248;k>0;k--);

    }

    关于函数延时以及一些基本的程序,我也只了解这些,再来说说定时器实现精确延时。

    定时器有4种模式,一般较为常用的为方式1,因为一旦溢出就会申请中断,因此一次溢出共需要65536us,约等于63.5ms,若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。

    在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。

    在这里,用定时器中断服务程序中,需要给定时器重装初值,完成定时器中断服务程序就回到主程序,但是要注意,若是没有关闭中断,在运行中断服务程序(进入中断服务程序需要时间)而且没有到给它重新赋值的语句前,定时器也在计数中,只有在重新赋初值后的瞬间,又开始从新的值处开始计时,因此,这是一个误差,解决误差的办法就是赋值初值的时候加上它当前的值。TH0=TH0+初值,TL0=TL0+初值。

    另外,中断服务程序不要过长,或者有一个或多个延时程序(不是说不能,是不建议),否则中断服务程序还没结束就又进入中断,会造成崩溃。

    下面给出一个程序,实现数码管每隔1s循环显示0-F,实现准确延时,当然不是绝对的准确。(我就直接把我学习单片机开发板上面的程序拷过来了)。这个程序重新赋值的时候没有向我上面说的那样,中断服务程序也略显赘余,可以把if放在主函数while中。

    /**************************************************************************************
    *		              定时器1实验												  *
    实现现象:下载程序后数码管最后一位间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。																				  
    ***************************************************************************************/
    
    #include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器
    
    typedef unsigned int u16;	  //对数据类型进行声明定义
    typedef unsigned char u8;
    
    sbit LSA=P2^2;
    sbit LSB=P2^3;
    sbit LSC=P2^4;
    
    u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
    					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
    u8 n=0;
    /*******************************************************************************
    * 函 数 名         : Timer1Init
    * 函数功能		   : 定时器1初始化
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    void Timer1Init()
    {
    	TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。
    
    	TH1=0XFC;	//给定时器赋初值,定时1ms
    	TL1=0X18;	
    	ET1=1;//打开定时器1中断允许
    	EA=1;//打开总中断
    	TR1=1;//打开定时器			
    }
    
    /*******************************************************************************
    * 函 数 名       : main
    * 函数功能		 : 主函数
    * 输    入       : 无
    * 输    出    	 : 无
    *******************************************************************************/
    void main()
    {	
    	LSA=0;
    	LSB=0;
    	LSC=0;
    	Timer1Init();  //定时器1初始化
    	while(1);		
    }
    
    /*******************************************************************************
    * 函 数 名         : void Timer1() interrupt 3
    * 函数功能		   : 定时器0中断函数
    * 输    入         : 无
    * 输    出         : 无
    *******************************************************************************/
    void Timer1() interrupt 3
    {
    	static u16 i;
    	TH1=0XFC;	//给定时器赋初值,定时1ms
    	TL1=0X18;
    	i++;
    	if(i==1000)
    	{
    		i=0;
    		P0=smgduan[n++];
    		if(n==16)n=0;	
    	}	
    }
    

    注:这里数码管用的是138译码器。


    这就是我的一些理解,我现在也在学习中,想着花点时间总结会有更好的脉络。之后会再来完善。有错欢迎指出。





    展开全文
  • 2答CPU存储器I/O口总线 3答C 4答B 5答微处理器微处理机和CPU它们都是中央处理器的不同称谓微处理器芯片本身不是计算机而微计算机单片机它们都是一个完整的计算机系统单片机是集成在一个芯片上的用于测控目的的单片微...
  • 学而不思则惘思而不学则殆 第 1 章 单片机概述参考答案 1.1 ...计算机而微计算机单片机它们都是一个完整的计算机系统单片机是集成在一个芯片上的 用于测控目的的单片微计算机 嵌入式处理器一般意义上讲是指嵌入系统的单
  • 此文档仅供收集于网络如有侵权请联系网站删除 第 ...身不是计算机而微计算机单片机它们都是一个完整的计算机系统单片机是集成在一 个芯片上的用于测控目的的单片微计算机 嵌入式处理器一般意义上讲是指嵌入系统的单片机
  • 2答CPU存储器I/O口总线 3答C 4答B 5答微处理器微处理机和CPU它们都是中央处理器的不同称谓微处理器芯片本身不是计算机而微计算机单片机它们都是一个完整的计算机系统单片机是集成在一个芯片上的用于测控目的的单片微...
  • 单片机原理应用学习笔记(一)

    万次阅读 多人点赞 2018-08-10 17:28:26
    三:51系列单片机的指令系统一般说明 (1)分类介绍: 数据传送类(28条) 算数操作类(24条) 逻辑运算类(25条) 控制转移类(17条) 位操作类 (17条) (2)指令中符号的意义: Rn ...

    *在学习51之前,最先接接触到的是Arduino,这个东西上手确实十分的简单,可以做出来一些小玩意,但是它的性能终究是不够强大,对于一些比较厉害的作品,用Arduino就显得心有余而力不足了,所以现在开始学习51和stm32。51的学习在大学本科阶段,在学校课堂上学到的基本上是一些理论知识,这些东西也确实有一定的必要,毕竟掌握了都是自己的。所以说我准备对这些理论的知识进行一个整理。好,废话不多说,现在切入正题。
    对于一般的大学教材,前面两章基本上都是对单片机的一些基本的介绍,主要就是一些对单片机硬件结构认识过程,这个主要是让我们有个总体的认识,开始适应单片机的学习。这些东西就忽略了吧。就从51系列单片机的指令系统开始吧。这里使用汇编语言编写程序的。(以上都是废话,自动忽略)*

    51系列单片机的指令系统

    一:单片机指令系统的概述
    (1)总共111条指令
    (单字节指令49条,双字节指令45条,三字节指令17条)
    (一个机器周期、12个时钟震荡周期指令64条,两个机器周期、24个时钟震荡周期指令45条、乘除两条指令位4个机器周期)
    (12MHZ晶振:机器周期位1us)
    (2)51指令不区分大小写
    (3)指令格式:操作码+操作数

    二:单片机的寻址方式
    (1)寄存器寻址方式:MOV A,Rn
    (2)直接寻址方式:MOV A,40H
    (寻址范围:第一:内部RAM的128个单元、第二:特殊功能寄存器SFR,除了以单元形式外还能用寄存器符号形式:即MOV A,80H==MOV A,P0)
    (3)寄存器间接寻址:(寄存器中放的是操作数地址、寄存器前面加标志符@)例如:MOV R1,#40H MOV A ,@R1
    (寻址范围:第一:访问内部RAM、第二:片外数据存储器的64k字节的间接寻址、第三:片外的数据存储器的低256字节、第四:堆栈区)
    (4)立即寻址方式:(操作数在指令中直接给出,需在操作数前面加上#,如果首位是A-F,前面还加0)例如:MOV A,#40H MOV A,#0FFH
    (5)基址寄存器加变址寄存器间址寻址方式:(以DPTR\PC作为基址寄存器,以累加器A作为变址寄存器)
    (6)位寻址方式:例如:MOV A,40H 把40H的值送到进位位C
    (寻址范围:第一:内部RAM位寻址区、第二:特殊功能寄存器中的可寻址位)
    (7)相对寻址方式:(目的地址=转移指令所在地址+转移指令字节数+rel(带符号的8位二进制数的补码数))

    三:51系列单片机的指令系统及一般说明
    (1)分类介绍:
    数据传送类(28条)
    算数操作类(24条)
    逻辑运算类(25条)
    控制转移类(17条)
    位操作类 (17条)
    (2)指令中符号的意义:
    Rn当前寄存器区8个工作寄存器
    Ri当前选中的寄存器区中可做间接寻址寄存器的2个寄存器
    Direct 直接地址,即8位内部数据存储单元或者特殊功能寄存器地址
    #data包含指令中的8位立即数
    #data包含指令中的16位立即数
    rel相对转移指令中的偏移量,8位带符号的补码数
    DPTR数据指针,可用作16位的数据地址寄存器
    bit内部RAM或者SFR中直接寻址位
    C(Cy)进位标志位或者位处理机制中的累加器
    addr11 11位目的地址
    addr16 16位目的地址
    @间接寻址寄存器
    (x)x中的内容
    ((X))由X寻址的单元中的内容
    ---->箭头右边内容被箭头左边所替代

    (持续更新中!!!)

    展开全文
  • 单片机报告

    2019-06-18 18:37:32
    《计算机基础实践》课程设计与制作 题目名称: 基于单片机的数显秒表设计与制作 学生姓名: 罗徐钢 专业班级: 自动1704 学 号: 5120175396 任课教师: 聂诗良 教授 所在学院: 信息工程学院 完成时间: 2018 年 ...
  • 单片机常用名词解释

    2017-08-10 16:42:12
    单片机常用名词解释
  • 单片机程序代码烧写方法有专用编程器烧写、编程器/调试器烧写ISP和IAP 单片机代码保护的实际应用:在实际应用中,以上介绍的单片机自身提供的代码保护方式是最基本的,实际用途中往往还需要考虑得更多。 单片机...
  • AVR系列的单片机内部集成了TWI总线,而且其...本文结合一个实例对TWI总线的内部模块、工作时序和工作模式进行了详细介绍,目的在于正确区分TWI总线和传统的I2C总线,对如何正确使用TWI总线编程也具有现实的指导意义
  • 单片机笔记(1)--单片机概述

    千次阅读 2020-04-23 14:35:07
    单片机概述 单片机单片机微型单片机,是将单片机主机(CPU、内存和I/O接口)集成在一小块硅片上的微型机。单片机为工业测控而设计,又称微控制器(MCU)。 ...
  • 1、多功能电脑时钟设计报告一、实训目的意义及任务要求1.1 目的意义单片机的使用领域已十分广泛,如智能仪器仪表、实时工控、通讯设备、导 航系统、家用电器等。 “学好单片机,工作就不成问题了。 ”老师说,可见...
  • 单片机入门知识

    万次阅读 多人点赞 2017-10-24 22:59:33
    作为一个大三老狗,才开始单片机入门,晚是晚了点,但是由于知识体系比大一大二稍加完善,所以看问题也相对于更加全面,所以写下...单片机就是指的一块集成芯片,上面集成了微处理器、存储器各种输入/输出接口。 单片
  • 51单片机蓝牙小车

    万次阅读 多人点赞 2019-02-25 20:41:23
    51单片机蓝牙小车(是我大二做的一个课程设计,小菜鸟一个,欢迎大家指正和参考。) 摘要 本次设计选择基于蓝牙遥控的多功能智能小车为对象。选用STC98C52RC单片机作为主控芯片,电机驱动采用L293N ,...
  • 单片机编程

    2014-11-05 18:16:03
     先声明一个概念,裸编程,我创造的名词,指的是在裸机上编写程序,裸机,在单片机领域就是指带着硬件的单片机控制系统,不要想歪咯。  在裸机上编程,就犹如在一片荒地上开垦,任何一锄头下去,都会碰到硬生生的...
  • 课题的目的意义以及和本课题有关的国内外现状分析:1.目的:本系统以单片机为主控单元,能够及时监测到系统故障和环境中有无火灾,火灾一旦发生将实现声光报警,并采取有效措施控制火情的发展,将火灾消灭在萌芽...
  • 论文范文字符的识别及单片机显示 第一章 绪论 1.1 研究的目的意义 字符识别应用的一个重要领域就是车牌中的字符识别[1-5]汽车牌照只涉及到地域简写以及一些特殊部门如部队武警还有公安的简称加上车牌号码总量也很...
  •  1 研究目的意义  随着家电工业、汽车工业、装潢业的迅速发展,市场对精整薄材、带材箔材的需求日益扩大,对成品薄带材箔材的精度要求也越来越高,开发研制高精度薄、带、箔材的精整设备是市场的需要。  ...
  • 单片机初学

    2017-11-23 19:26:00
    单片机学习 问:什么是单片机? 答:可编程电子设计的产物,也就是一个小芯片。可以用来编程,实现不同的功能。把具有数据处理能力的中央处理器(CPU)、随机存储器(RAM)、只读存储器(ROM)、多种I/O口和中断...
  • 1.了解单片机开发系统的组成结构; 2. 掌握I/0口的操作方法; 3.能够熟练使用protues和keil软件进行连线和编程,并熟练掌握仿真方法; 4.掌握LCD显示屏的显示原理; 5. 掌握C语言编程方法; 6.培养查找错误和改正错误的...
  • 单片机期末复习

    2021-01-05 17:31:32
    单片机期末复习名称解释单片机概述中断系统作用中断源入口地址 只针对MCS-51单片机 名称解释 A/Acc:累加器。 B:B寄存器。 DPTR:数据指针,一个16位的SFR,其高位字节寄存器用DPH表示,低位字节寄存器用DPL表示。...
  • 单片机 FPGA

    2018-01-07 23:55:23
    版权声明:本文为博主原创文章,未经博主允许不得转载。  有的学校先教汇编,再教C,但我认为,先... 单片机用的C语言叫C51,C51除了标准C的语法之外,还特意为单片机增添了几个语法而已,没有很大区别,那么我
  • 单片机程序优化

    千次阅读 2016-03-31 10:26:40
    PC 机编程基本上不用考虑空间的占用、内存的占用的问题,最终目的就是实现功能就可以了。 对于单片机来说就截然不同了,一般的单片机的Flash 和Ram 的资源是以KB 来衡量的,可想而知,单片 机的资源是少得可怜,...
  • 看到这篇文章的朋友,想必大家对pic单片机均具备一定兴趣,或对pic单片机具备一定使用需求。前文中,小编曾对pic单片机汇编语言进行过部分讲解。本文中,将对pic单片机的清零等指令加以讲解,以帮助大家更好掌握pic...
  • 单片机学习笔记

    千次阅读 多人点赞 2017-11-12 21:54:21
    2,晶振电路:决定单片机运行的节奏,即频率。 3,复位电路:重置开发板。 4,机器周期:一个机器周期包含12个时钟周期,在一个机器周期内,cpu就可以完成一个独立的操作。 5,时钟周期:也称为振荡周期,由晶振...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,181
精华内容 2,072
关键字:

单片机的目的及意义