实物制作视频教程:
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可知,只需要改变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所示。
每次赋值都是前一次的数值左移一次,根据此规律,可以设计如下代码:
程序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-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所示。
网络学习以下词条,将有助于理解本节内容。
逻辑电平、拉电流、灌电流、发光二极管特性
下节预告:延时函数、系统时钟
作者水平有限,编写过程中难免出现不当之处,还望读者诸君不吝赐教,或许您有好的建议,欢迎与我联系QQ:136678431,作者将报以实质性奖励。