zoukankan      html  css  js  c++  java
  • [51单片机学习笔记ONE]-----LED灯的多种使用方法

    一.交替闪烁8个LED灯,时间间隔为1s

     1 /******************************************************
     2 实验名称:       交替闪烁8个LED灯,时间间隔1s
     3 实验时间:       2014年12月2日
     4 ******************************************************/
     5     
     6 #include <reg51.h>
     7 
     8 void delay(unsigned char a);
     9 
    10 void main()
    11 {
    12     while(1)
    13     {
    14         /*根据原理图,P0置高电平灯亮*/
    15         P0 = 0x00;
    16         delay(45);
    17         
    18         /*根据原理图,P0置低电平灯灭*/
    19         P0 = 0xFF;
    20         delay(45);    
    21     }
    22 }
    23 
    24 /*延时1s,有误差。计算公式大约可以用((((c*2)+3)*b+3)*a)*/
    25 void delay(unsigned char a)      
    26 {
    27     unsigned char b,c; 
    28     for(;a>0;a--)
    29         for(b=152;b>0;b--)
    30             for(c=70;c>0;c--);
    31     
    32 }

    实验的代码很简单。但是实际操作过程中还是遇到了以下问题:

    1. 端口P0编号不能用小写。P字母必须大写。
    2. 给P0赋值时,虽然赋的是16进制的值,但是不能在后面加H。
    3. 最重要的是延时问题!

    要注意的是用C语言不大可能做出精确的延时效果,肯定会存在一定的误差!!!!

    首先先来了解几个单片机的周期知识:

    • 时钟周期:也称振荡周期,定义为频率的倒数,它是单片机中最基本,最小的时间单位。
    • 状态周期:它是时钟周期的两倍。
    • 机器周期:单片机的基本周期,完成一项基本操作,如取指令,存储器读写等,它由12个时钟周期(6个状态周期)构成。
    • 指令周期:单片机执行一条指令所需要的时间,一般是1-4个机器周期。

    在这里,我把晶振的频率设置为跟自己设备一样,为12MHZ,所以机器周期为1µs。而整个程序的时间可以根据Keil的调试功能查看。

    打开Keil的调试功能(就是菜单栏里放大镜里面有个d的那个图标),然后再在所需要的地方设置断点。如下图所示:

    如图所示,在15行,16行位置设置了断点。图片左侧部分,可以找到“sec”这一项,这一项就是执行到这步所需要的时间,然后可以根据时间差计算延时的时间。

    这里可以看到执行到15行的时间为0.00038900s

    执行到16行的时候时间为0.00039100s,所以执行P0=0x00的时间为2µs,即两个机器周期,因为这里用的是立即数寻址,取值一周期,执行一周期

    执行到19行的时间为0.97892600s,所以执行delay(45)这句代码的时间为0.978535s,接近1s,所以说是存在误差的。但是对于实验效果来说,也已经够了。

    最重要的还有一点就是,经过自己实验,不同的类型符号所需要的时间也是不一样的,比如我采用无符号整形,所需时间就是3.7s,所以差别还是很大的。

    不过总结来说,还是应该遵循一些简单的原则:

    • 尽量使用unsigned 型的数据结构。
    • 尽量使用char型,实在不够用再用int,然后才是long。
    • 如果有可能,不要用浮点型。
    • 使用简洁的代码,因为按照经验,简洁的C代码往往可以生成简洁的目标代码(虽说不是在所有的情况下都成立)。

    另外的话,在这里也把汇编的延时程序放出来。同样的,也是先来看用来实现这个目标的代码。

     1 /*******************************************************************************
     2 * 实 验 名         : LED闪烁的简单试验
     3 * 实验说明       : 得到8盏LED交替亮灭的实验效果
     4 *******************************************************************************/
     5     
     6     ORG     0000H        ;程序从此地址开始运行
     7     LJMP     MAIN        ;跳转到 MAIN 程序处
     8 
     9     ORG     030H        ;MAIN 从030H处开始
    10 MAIN:    
    11       MOV     P0 ,#00H    ;P0为低电平 LED 灯亮
    12     ACALL     DELAY        ;调用延时子程序
    13     MOV     P0 ,#0FFH
    14     ACALL     DELAY
    15     AJMP     MAIN        ;跳转到主程序处
    16 
    17 DELAY:    
    18     MOV     R5,#08H       ;将立即数传给寄存器R5
    19 F3:    
    20     MOV     R6,#0FAH
    21 F2:    
    22     MOV     R7,#0FAH
    23 F1:    
    24     DJNZ     R7,F1           ;若为0程序向下执行,若不为0程序跳转到
    25     DJNZ     R6,F2
    26     DJNZ     R5,F3
    27     RET
    28 
    29     END

    其中DJNZ是减一不为零转移指令。有两个参数,第一个是被减数,第二个是转移的地址编号。

    还有一点要注意的就是MOV指令时单周期指令,DJNZ是双周期指令。

    下面是延时分析:

     

    执行到MOV指令时的时间为0.00000200s

     执行到12行时,时间为0.00000400s,所以执行MOV P0,#00H这句语句的时间为2µs,同样的是因为采用了立即数寻址。

     可以看到延时程序的消耗时间大约为1s,计算分析过程:

     MOV  R5,#08H    ;执行了1次,单周期
    F3: 
     MOV  R6,#0FAH  ;执行了1*8次,单周期
    F2: 
     MOV  R7,#0FAH  ;执行了1*8*250次,单周期
    F1: 
     DJNZ  R7,F1       ;执行了8*250*250次,双周期
     DJNZ  R6,F2       ;执行了250*8次,双周期
     DJNZ  R5,F3       ;执行了8次,双周期

    所以总的时间为1+8+8*250+8*250*250*2+250*8*2+8*2=1006025µs

    所以采用汇编编写延时程序明显比C语言准确的多,当然也不是完全正确,还是有很小的误差,准确的延时应该用定时器来设计。


    二.LED二进制加法显示

     1 /*********************************
     2  ---------------------------------
     3  实验名称:    LED二进制加法显示
     4  实验时间:        2014/12/2
     5 *********************************/
     6 
     7 #include <reg51.h>
     8 
     9 void Delay(unsigned char x);
    10 
    11 void main()
    12 {
    13     unsigned char n = 0x00;
    14     while(1)
    15     {
    16         P0 = n;
    17         Delay(45);
    18         n++;
    19     }
    20 }
    21 
    22 void Delay(unsigned char x)
    23 {
    24     unsigned char y,z;
    25     for(;x>0;x--)
    26         for(y=152;y>0;y--)
    27             for(z=35;z>0;z--);
    28 }

     三.LED流水灯设计

     1 /***************************************
     2 ---------------------------------------
     3 实验名称:       流水灯实验
     4 实验说明:       延时实现LED灯流水线效果
     5 实验时间:        2014/12/2
     6 ***************************************/
     7 
     8 #include <reg51.h>
     9 #include <intrins.h>
    10 
    11 void Delay(unsigned char a);
    12 
    13 void main()
    14 {
    15     
    16     unsigned char x;
    17     x = 0x01;
    18     while(1)
    19     {
    20         P0 = x;
    21         Delay(45);
    22         x = _crol_(x,1);    //char型循环向左移
    23     }
    24 }
    25 
    26 void Delay(unsigned char a)
    27 {
    28     unsigned b,c;
    29     for(;a>0;a--)
    30         for(b=76;b>0;b--)
    31             for(c=35;c>0;c--);
    32 }

    intrins.h头文件内部函数描述:

    _crol_ 字符循环左移
    _cror_ 字符循环右移
    _irol_ 整数循环左移
    _iror_ 整数循环右移
    _lrol_ 长整数循环左移
    _lror_ 长整数循环右移
    _nop_ 空操作 (相当于8051 NOP 指令)
    _testbit_ 测试并清零位 (相当于8051 JBC 指令)
    具体用法:
    (1)循环移位用法如上代码。
    (2)_nop_ 空操作:
               P()=1;
             _nop_();
              P()=0;
    功能:即空指令。什么都不做,但是占用一个指令的时间。
    (3)_testbit_ 测试并清零位:
    功能:产生一个JBC 指令,该函数测试一个位,当置位时返回1,否则返回0。如果该位置为1,则将该位复位为0。8051 的JBC 指令即用作此目的。
    _testbit_只能用于可直接寻址的位;在表达式中使用是不允许的。

    四.LED跑马灯设计

     1 /*******************************************
     2 --------------------------------------------
     3 实 验 名:     LED跑马灯
     4 实验说明:     LED灯右移跑马灯
     5 *******************************************/    
     6   
     7 #include<reg51.h>
     8 #include<intrins.h>
     9   
    10 void Delay(unsigned char a);
    11  
    12 void main()
    13 {
    14     unsigned char n = 0xFE;
    15     while(1)
    16     {    
    17         P0 = n;
    18         Delay(45);
    19         n = _crol_(n,1);
    20     }
    21 }
    22  
    23 void Delay(unsigned char a)
    24 {
    25     unsigned char b,c;     
    26         for (;a>0;a--)
    27             for (b=152;b>0;b--)
    28                 for (c=35;c>0;c--);
    29 }           

    五.LED左右跑马灯设计

     1 /*******************************************
     2 --------------------------------------------
     3 实 验 名:     LED跑马灯
     4 实验说明:     LED灯左右跑马灯
     5 *******************************************/    
     6 
     7 #include<reg51.h>
     8 #include<intrins.h>
     9 
    10 void Delay(unsigned char a);
    11 
    12 void main()
    13 {
    14     unsigned char n = 0xFE;
    15     unsigned char i = 0;
    16     unsigned char j = 0;
    17     while(1)
    18     {
    19         for(;i<7;i++)
    20         {
    21             P0 = n;
    22             Delay(45);
    23             n = _crol_(n,1);    
    24         }    
    25 
    26         for(;j<7;j++)
    27         {
    28             P0 = n;
    29             Delay(45);
    30             n = _cror_(n,1);
    31         }
    32     }
    33 }
    34 
    35 void Delay(unsigned char a)
    36 {
    37     unsigned char b,c;
    38     for (;a>0;a--)
    39         for (b=152;b>0;b--)
    40             for (c=35;c>0;c--);
    41 }           

  • 相关阅读:
    springboot中如何获取配置文件的值
    自动获取当月天数
    控制输入前端时间格式及其它常见需求
    页面输出格式常用注解----@JsonIgnore,@JsonFormat,@JsonFormat
    echart动态生成标题
    echart柱状图中每个柱子超过阈值改变颜色状态
    如何把箭头放置在文本前面,并根据数据的正负显示向上和向下箭头
    02、GPIO初始化
    Linux文件目录的权限
    Linux执行命令./command与直接输入命令的区别
  • 原文地址:https://www.cnblogs.com/one-meter/p/4136424.html
Copyright © 2011-2022 走看看