第一章(1.2—跑马灯的实现)

实物制作视频教程:
http://v.youku.com/v_show/id_XMzIxNDkzNTYxMg==.html?spm=a2hzp.8244740.0.0
这次的实物电路是接着上一节的电路制作的,希望看过第一节的读者没有丢弃之前的电路板,若你是第一次看到本视频,点此可阅读前一节内容。

如果你的计算机无法编译我提供的工程文件,你需要看下面的视频教程
Keil软件添加STC单片机并创建工程视频教程

原理图、源代码、元件清单等资料下载 戳我下载

1.2.1 单片机准双向口的电器特性

STC15系列单片机IO口默认工作在准双向口模式,与STC89系列单片机不同,STC89系列单片机的P0端口默认为开漏模式。STC15系列单片机准双向口模式的拉电流为150μA~150μA,灌电流为20mA。
单片机I/O口作为输出使用时,可由软件控制输出0或1,通常称之为逻辑“0”和逻辑“1”,当输出逻辑“0”时,可用万用表测得单片机IO口电压为0V,当输出逻辑“1”时,万用表测得单片机IO口电压为5V或3.3v(3.3v单片机)。
注:实际上逻辑“0”和逻辑“1”测得电压与本文的理论值有所误差,逻辑“0”通常测得的电压并不是绝对0V,但通常都小于0.1V。逻辑“1”测得的电压会随着工作电压不同而变化。
原理图中D1至D8八只发光二极管的负极与单片机P0端口的P0.0至P0.7一一对应,所有发光二极管均串联了一只100欧姆的电阻。大多数发光二极管的工作电压在1.8 -3.2V之间。在实际制作过程中,读者根据选用的二极管工作电压选择100至500欧姆的电阻。
所有LED的阳极串联电阻后,并联到5V电源处,利用发光二极管单向导通的特性,当单片机IO口的输出逻辑“0”时,LED正负极两端的电势差大于导通电压时,LED开始发光工作。
通过编程,依次控制P0组端口每个I/O的逻辑输出,达到跑马灯效果。
思考:本原理图中的LED接法称之为“共阳极”接法,共阴极接法应该如何设计电路?

1.2.2 控制逻辑的实现

大到机器人,小到传感器,都离不开单片机的I/O口操作,跑马灯就是单片机I/O口的入门课。STC系列单片机可通过如下指令操作某一I/O口。

P00 = 0;        //P0.0端口输出逻辑“0”
P01 = 1;        // P0.1端口输出逻辑“1”

用同样的方法,可操作其他组端口:

P20 = 0;        //P2.0端口输出逻辑“0”
P31 = 1;        //P3.1端口输出逻辑“1”
P41 = 1;        //P4.1端口输出逻辑“1”

我们可以这样操作I/O口,是因为单片机的头文件中定义好了这些内容,在STC15F2K60S2.h头文件中,部分定义如下:

sfr P0          =   0x80;  
sbit P00        =   P0^0;
sbit P01        =   P0^1;
sbit P02        =   P0^2;
sbit P03        =   P0^3;
sbit P04        =   P0^4;
sbit P05        =   P0^5;
sbit P06        =   P0^6;
sbit P07        =   P0^7;
sfr P1          =   0x90;   
sbit P10        =   P1^0;
sbit P11        =   P1^1;
sbit P12        =   P1^2;
sbit P13        =   P1^3;
sbit P14        =   P1^4;
sbit P15        =   P1^5;
sbit P16        =   P1^6;
sbit P17        =   P1^7;

也可以通过下面的方法实现I/O口的输出控制。

P0^0 = 0;
P1^1 = 1;   
P2^3 = 1;

在实际的项目开发中,为了提高代码的阅读性及可维护性,经常需要使用“sbit”位定义从新定义IO口,如下面的代码所示。

#include"STC15F2K60S2.h"
sbit led0 = P0^0;
void main()
{
    led0 = 1; 
    led0 = 0;  
}

led0 = 1;等价于P0^0 = 0;P00 = 0;这样设计程序的好处是,一旦硬件需要做出修改,只需要在程序开头修改led0所对应的IO口即可。
跑马灯实际上就是控制8个IO口的逻辑输出,在控制逻辑上可理解为:

第一章(1.2—跑马灯的实现)_第1张图片
流程图

根据上面的流程图,可设计较为简单的程序:

程序C1-2-1

#include"STC15F2K60S2.h"
#include"intrins.h"

sbit led1 = P0^0; 
sbit led2 = P0^1;   
sbit led3 = P0^2;   
sbit led4 = P0^3;   
sbit led5 = P0^4;   
sbit led6 = P0^5;   
sbit led7 = P0^6;   
sbit led8 = P0^7; 
        
void Delay300ms()       //@11.0592MHz
{
    unsigned char i, j, k;

    _nop_();
    _nop_();
    i = 13;
    j = 156;
    k = 83;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}


void main(void)
{
    
    while(1)
    {
        led1 = 0;
        Delay300ms();
        led1 = 1;

        led2 = 0;
        Delay300ms();
        led2 = 1;

        led3 = 0;
        Delay300ms();   
        led3 = 1;

        led4 = 0;
        Delay300ms();
        led4 = 1;

        led5 = 0;
        Delay300ms();
        led5 = 1;

        led6 = 0;
        Delay300ms();
        led6 = 1;

        led7 = 0;
        Delay300ms();
        led7 = 1;

        led8 = 0;
        Delay300ms();
        led8 = 1;
    }
}

除了前文中操作I/O口的方法,还可以直接操作一组I/O口,示例代码:

P0 = 0x01;  //P0^0输出1,P0^1至P0^7输出0
P0 = 0xFF;  //P0^0至P0^7全部输出1

这种写法的优点在于一次性可操作一组端口的8个I/O,1字节十六进制长度是8位,与P0口的8个I/O口相对应,对应关系如表1-2-1所示。


第一章(1.2—跑马灯的实现)_第2张图片
表1-2-1

由表1-2-1可知,只需要改变P0端口的赋值,即可实现跑马灯效果,完整代码如下。

程序代码C1-2-2

#include"STC15F2K60S2.h"
#include"intrins.h"
        
void Delay300ms()       //@11.0592MHz
{
    unsigned char i, j, k;

    _nop_();
    _nop_();
    i = 13;
    j = 156;
    k = 83;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

void main(void)
{
    while(1)
    {           
        P0 = 0xFE;  
        Delay300ms();
        P0 = 0xFD;  
        Delay300ms();   
        P0 = 0xFB;  
        Delay300ms();
        P0 = 0xF7;  
        Delay300ms();   
        P0 = 0xEF;  
        Delay300ms();
        P0 = 0xDF;  
        Delay300ms();   
        P0 = 0xBF;  
        Delay300ms();
        P0 = 0x7F;  
        Delay300ms();       
    }
}

1.2.3代码优化

代码C1-2-1与代码C1-2-2可以很直观的让程序阅读者读懂代码含义,但这两种写法都显得过于臃肿。下面介绍2种方式优化上述代码。

通过观察程序C1-2-2,可发现P0端口每次赋值是有规律的,对于才接触单片机的读者,可能难以发现十六进制数的规律,转换为二进制后,能较为容易的发现赋值规律,如表1-2-2所示。


第一章(1.2—跑马灯的实现)_第3张图片
表1-2-2

每次赋值都是前一次的数值左移一次,根据此规律,可以设计如下代码:

程序C1-2-3

#include"STC15F2K60S2.h"
#include"intrins.h"
        
void Delay300ms()       //@11.0592MHz
{
    unsigned char i, j, k;

    _nop_();
    _nop_();
    i = 13;
    j = 156;
    k = 83;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

void main(void)
{   
    unsigned char i,m;  

    while(1)
    {
        for(i=0,m=0xFE;i<8;i++)
        {           
            P0 = m; 
            m = m<<1;
            Delay300ms();       
        }

    }
}

主函数还可以这样设计

void main(void)
{   
    unsigned char i;    

    while(1)
    {
        for(i=0;i<8;i++)
        {           
            P0 = 0xFE<

思考:两种主函数对比,能发现什么不同吗?

将C-1-2-3.hex文件烧录到单片机后,会发现显示效果与视频中的显示效果二相同,并不是预期的显示效果一。这是因为,每次执行左移指令后,会自动将数据最低位补“0”,如图1-2-2所示。


第一章(1.2—跑马灯的实现)_第4张图片
图1-2-2

由此可见,直接使用左移指令,并不难达到预期效果,当然这种现实方式也值得学习。若想实现演示视频中的第一种显示效果,可以使用“循环移位”,如图1-2-3所示。


第一章(1.2—跑马灯的实现)_第5张图片
图1-2-3

使用循环移位后,最高位多出的“1”不会被丢掉,而是“移动”到数据的最低位。若使用51汇编,可使用循环移位指令(RL 循环左移)实现,虽然Keil Cx51编译器完整的实现了ANSI C标准,但ANSI C并不支持类似汇编中循环移位指令、布尔跳转指令(JBC)、空操作等指令。为了让用户在使用C语言编程时可以使用一些汇编语言的操作,Cx51编译器提供了INTRINS.H库,添加INTRINS.H库后,可以像汇编那样通过循环移位指令设计跑马灯程序,代码如下。

程序C1-2-4

#include"STC15F2K60S2.h"
#include"intrins.h"
        
void Delay300ms()       //@11.0592MHz
{
    unsigned char i, j, k;

    _nop_();
    _nop_();
    i = 13;
    j = 156;
    k = 83;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

void main(void)
{   
    unsigned char i,m=0xFE; 

    while(1)
    {
        for(i=0;i<8;i++)
        {           
            P0 = m;
            m = _crol_(m,1); //循环左移一次
            Delay300ms();       
        }
    }
}

本程序中使用的_crol_用于无符号字符型(unsigned char),INTRINS.H还包含支持其他类型数据类型的循环移位操作,如表1-2-3所示。

第一章(1.2—跑马灯的实现)_第6张图片
表1-2-3

网络学习以下词条,将有助于理解本节内容。

逻辑电平、拉电流、灌电流、发光二极管特性

下节预告:延时函数、系统时钟

作者水平有限,编写过程中难免出现不当之处,还望读者诸君不吝赐教,或许您有好的建议,欢迎与我联系QQ:136678431,作者将报以实质性奖励。

你可能感兴趣的:(第一章(1.2—跑马灯的实现))