第一章(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口的逻辑输出,在控制逻辑上可理解为:

流程图

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

程序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-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-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<<i;   
            Delay300ms();       
        }
    }
}

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

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


图1-2-2

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


图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-3

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

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容