通过前几章,我们组建了我们自己的小单片系统,学会了编程烧写的方法,那么如何用它去控制LED呢,缘缘先带你去串串门,认识认识那此单片内的小空间吧:
一、了解单片内部结构,为后面编程控制打基础:
咱们其它的先不要看,会在以后用到的时候进行讲解说明,就瞧瞧这四个与外界相连的这部分空间吧:
51单片机在设计时有四个并行I/O口,每个端口都有8位准双向口,共占有32条引脚,每一条I/O线都能独立地用途输入或输出,每个端口都包括一个数据锁存器(即特殊功能寄存器P0-P3)。
锁存器是干什么的呢,广义上讲,就是可以定义数值,并写入数值的一种存储器,打个比方,就是咱们的U盘,我们往里面复制一首歌,这个歌就会一直存在,相当于被锁存了,当我们不要这首歌时,我们就会删除它,从某种意义上讲,这时,锁存器也改变了状态。
那么每个锁存器是怎么回事呢,如下图:
对于这个锁存器,从原理上很难说明白,什么地上拉电阻、场效应、与非门啦,搞上半天都弄晕了,不过我们可以这样理解,把它们看做是一组开关,每个里面都有8个,每个开关都接一根线。
那么怎么去控制他们呢,这里,我们记住一个概念,即在数字电路中,高电平是1,低电平是0,假设我们上面一个开关是这样的,也就是在没有定义的时候,它们是断开的:
当我们定义成1时,它接高电平,即与电源VCC相连:
当我们定义成0时,它接低电平,即与电源GND相连:
还记得上一章那个LED是怎么点亮的吗:
就是当电源与负极接通时,我们的LED就亮了,通过上面的讲解,我们明白了定义与它们电位对应关系,所以我们对LED控制时,只要定义成0与1就OK了,如果我们定义开关为1时,这时与高电平接通VCC,那么,我们的LED两边都是高电平,肯定点不亮了:
如果我们定义开关为0时,这时与低电平GND接通,这时电路处于通路状态,LED就被点亮了。
那么如何去控制这个开关呢,答案是用程序控制,就是我们常听说的玛雅文字——C语言,在开始程序前,我们有一点点小知识了解下,那就是上节说的,为了什么要接8个电阻,而且这8个电阻接在了P0口,这8个电阻有什么用,阻值有什么限定,那么,亲们,认真看看吧:
关于P0口接上拉电阻的解释:(可要记下哦)
P0口作为I/O口输出的时候时,输出低电平为0输出高电平为高组态(并非5V,相当于悬空状态,也就是说P0口不能真正的输出高电平)。给所接的负载提供电流,因此必须接上拉电阻(一电阻连接到VCC),由电源通过这个上拉电阻给负载提供电流。P0作输入时不需要上拉电阻,但要先置1。因为P0口作一般I/O口时上拉场效应管一直截止,所以如果不置1,下拉场效应管会导通,永远只能读到0。因此在输入前置1,使下拉场效应管截止,端口会处于高阻浮空状态,才可以正确读入数据。
由于P0口内部没有上拉电阻,是开漏的,不管它的驱动能力多大,相当于它是没有电源的,需要外部的电路提供,绝大多数情况下P0口是必需加上拉电阻的。
1.一般51单片机的P0口在作为地址/数据复用时不接上拉电阻。
2.作为一般的I/O口时用时,由于内部没有上拉电阻,故要接上上拉电阻!!
3.当p0口用来驱动PNP管子的时候,就不需要上拉电阻,因为此时的低电平有效;
4.当P0口用来驱动NPN管子的时候,就需要上拉电阻的,因为此时只有当P0为1时候,才能够使后级端导通。简单一点说就是它要驱动LCD显示屏显示就必须要有电源驱动,否则亮不了,而恰好P0口没有电源,所以就要外接电源,接上电阻是起到限流的作用;如果接P1、P2、P3端口就不用外接电源和电阻了。
P0口是开漏的,不管它的驱动能力多大,相当于它是没有电源的,需要外部的电路提供,绝大多数情况下P0口是必需加上拉电阻的;5、51单片机的P0口用作数据和地址总线时不必加上拉电阻。
有些IC的驱动能力并不强,如果P0口作为输入而加了不必要的上拉,有可能驱动IC无法将其拉回到低电平,从而使输入失败!
如果是驱动led,那么用1K左右的就行了。如果希望亮度大一些,电阻可减小,最小不要小于200欧姆,否则电流太大;如果希望亮度小一些,电阻可增大,增加到多少呢,主要看亮度情况,以亮度合适为准,一般来说超过3K以上时,亮度就很弱了,但是对于超高亮度的LED,有时候电阻为10K时觉得亮度还能够用。通常就用1k的。对于驱动光耦合器,如果是高电位有效,即耦合器输入端接端口和地之间,那么和LED的情况是一样的;如果是低电位有效,即耦合器输入端接端口和VCC之间,那么除了要串接一个1——4.7k之间的电阻以外,同时上拉电阻的阻值就可以用的特别大,用100k——500K之间的都行,当然用10K的也可以,但是考虑到省电问题,没有必要用那么小的。
对于驱动晶体管,又分为PNP和NPN管两种情况:对于NPN,毫无疑问NPN管是高电平有效的,因此上拉电阻的阻值用2K——20K之间的,具体的大小还要看晶体管的集电极接的是什么负载,对于LED类负载,由于发管电流很小,因此上拉电阻的阻值可以用20k的,但是对于管子的集电极为继电器负载时,由于集电极电流大,因此上拉电阻的阻值最好不要大于4.7K,有时候甚至用2K的。对于PNP管,毫无疑问PNP管是低电平有效的,因此上拉电阻的阻值用100K以上的就行了,且管子的基极必须串接一个1——10K的电阻,阻值的大小要看管子集电极的负载是什么,对于LED类负载,由于发光电流很小,因此基极串接的电阻的阻值可以用20k的,但是对于管子的集电极为继电器负载时,由于集电极电流大,因此基极电阻的阻值最好不要大于4.7K。
对于驱动TTL集成电路,上拉电阻的阻值要用1——10K之间的,有时候电阻太大的话是拉不起来的,因此用的阻值较小。但是对于CMOS集成电路,上拉电阻的阻值就可以用的很大,一般不小于20K,我通常用100K的,实际上对于CMOS电路,上拉电阻的阻值用1M的也是可以的,但是要注意上拉电阻的阻值太大的时候,容易产生干扰,尤其是线路板的线条很长的时候,这种干扰更严重,这种情况下上拉电阻不宜过大,一般要小于100K,有时候甚至小于10K。
根据以上分析,上拉电阻的阻值的选取是有很多讲究的,不能乱用。
想信通过以上的说明,你对单片机有了更深一层的了解了,那么,我们就对它进行控制吧!
二、编写程序,让梦想从此实现,实战LED:
以下内容有二进制转换,单位的换算,C语言基本函数,C语言数值类型,这些缘缘都在开头就讲过了,不懂的可以看看缘缘本系列的第一篇帖子:https://bbs.icxbk.com/index.php?app=group&ac=topic&id=2358
先看看原理图,就是有8个LED共同接在正极上:
那么,再看看我们的P口是怎么回事:
锁存器 |
端口定义 |
|||||||
P0 |
P0.7 |
P0.6 |
P0.5 |
P0.4 |
P0.3 |
P0.2 |
P0.1 |
P0.0 |
P1 |
P1.7 |
P1.6 |
P1.5 |
P1.4 |
P1.3 |
P1.2 |
P1.1 |
P1.0 |
P2 |
P2.7 |
P2.6 |
P2.5 |
P2.4 |
P2.3 |
P2.2 |
P2.1 |
P2.0 |
P3 |
P3.7 |
P3.6 |
P3.5 |
P3.4 |
P3.3 |
P3.2 |
P3.1 |
P3.0 |
注意,所以的P口在定义管脚是从0开始的,可不要记错了哦
为了方便实验,我们就用P0口的那8个端口去操作这8个LED,P0口就是上节我们焊了电阻的那一部分:
1、先点亮第一个二极管
我们先看看P0口的如何去控制这个LED吧:
好了,我们来写写控制LED的这个程序:
首先,为了定义是上面那种管脚的类型,我们先写入头文件,什么是头文件呢?头文件作为一种包含功能函数、数据接口声明的载体文件,主要用于保存程序的声明,而定义文件用于保存程序的实现 ,是不是很难理解呢,那就教你个方法,相当于头文件就是个称呼,比如,你称呼缘缘,缘缘我就知道你在叫缘缘了,所以,在51单片系统中,头文件就是定义了51单片机的存储类型,端口状态,功能结构与数据使用类型,只要是与51单片有关系的,都会在头文件中体现,那么如何在Keil文件中体现呢,我们用如下的方法进行:
#include
怎么样,简单吧,你只要写上这一句,关于单片内部的部分就不用管了,接下来我们写上这么两句话:
#define uchar unsigned char
#define uint unsigned int
这两句话的意思就是把unsigned char这种类型的数值用uchar这种名字替代,这样是为了方便写些,对于咱们英文不好的小白们来说,这绝对是一个值得称赞的好注意,写完了这两个,我们要操作的是P0锁存器的第一根端口,即P0.0,我们现在就把它定义一下吧,用一个名字替代它:
sbit D=P0^0;(这里的D是个名称的意思,如果不嫌麻烦,当然也可以写成缘缘的拼音的,因为C语言中不执行汉字,所以就只能写拼音了,亲们,以后记着哦)
那么,到这里,我们先说下上面的define与这里的sbit的区别:
define是泛指,即在程序中出现的与此相关的全部的都是这个类型,主要指数值类型,其意义是宏定义,而sbit是指特指,也就是针对某一个,在程序中常用于定义端口。
到现在,我们基本上明确了这些,我们就把上面的内容汇总到一起,这里记住C语言的几个特点:
1、所有的标点符号全是英文的,如果你在后面弄个中文的标点符号,这肯定编译不过去。
2、用于注释时,即不让这句话在程序中执行时,可以前面打上英文的//,如果要注解一段内容,方法是在最前面写上英文符号/*,后面写上*/。
3、在所有的程序中,只包含一个主程序,也就是main()函数,其写法是:
void main()
{
内容
}
4,一个大括号算是一个完整的程序,常写在函数的后边,里面会包含若干个子程序,也是有大号的哦,意思是会执行其中的内容。
5、一条单独的程序在结束时,以英文符号;结尾,含大括号的除外。
讲了这么多,是不是想写个真正控制LED的程序,那好,跟我一起来吧:
#include < reg52.h >
#define uchar unsigned char
#define uint unsigned int
sbit D=P0^0;
void main()
{
D=0;
}
现在我们完成了程序的书写,我们编译下吧,看看,有错误吗,没有,对吧,那好吧,用前面教的方法烧录进去吧,是不是奇迹出现了,这时,你觉得单片机难吗,不难对吗?这可不是我说的
2、分别依次点亮其它的二极管
我们即然能把一个LED点亮,当然其它的就不在话下了,我们只对上面的程序做一个小小的改动就行了,也就是更改sbit D=P0^0;最后面的这个数字,假设我们让第二个亮,就改成sbit D=P0^1,怎么样,简单吧,亲们,自己试试,把其它的都点亮吧!
3、点亮某几个二极管
只要能点亮一个LED,那么剩下就不是问题了,于是我们再多于定义几个不就行了,假高我要点亮1,3,5,7该怎么做呢,跟着缘缘一起写起来吧:
#include < reg52.h >
#define uchar unsigned char
#define uint unsigned int
sbit D1=P0^0;
sbit D2=P0^2;
sbit D3=P0^4;
sbit D4=P0^6;
void main()
{
D1=D2=D3=D4=0;
}
4、总线法点亮
总线是什么意思呢,亲们,看看这个说明吧:
我们用程序试验一下吧:
#include < reg52.h >
#define uchar unsigned char
#define uint unsigned int
void main()
{
P0=0xfc;
}
好了,那么亲们,发挥你的想像吧,自己乱定义几个,让灯亮吧!
5、延时讲解
以下的延时都会用到一个知识点,就是机器周期,也就是单片机在处理一个数值时用的时间,比如,让它加一下,也就是加个1,那么它就会费一个机器周期的时间去处理这个事情,那么如何去算呢,一个机器周期是晶震的12分频,那么,如果我们用12M的晶震的话,换算的机器周期就是12(晶震)×1/12=1US,为什么这么算呢,因为12M晶震的的意思是在1秒的时间内跳动了12000000次,(那么这个数是多少呢,自己算算吧,缘缘很懒的,就猜了下,是12M(兆)吧!)那么晶震每跳12下,这个单片机就会处理一个事务,所以在1秒内就会处理1000000,那么1次是多少呢,我们就用前面学过的公式进行转换吧,我们知道1S=1000MS,那么1000000次/1000=1000次,也就是说在1MS内,单片机执行了1000次,通过MS的转换得知1MS=1000US,那么1000次/1000就成变成了1次了,所以我们得出一个结论,就是单片机执行一次任务的时间是1US。(这是为了方便计算用的是12M的晶震,如果是其它的晶震,就不是这个值了,但算法是一样的。)
为了方便计算,以下延时按12M晶震计算:
对于延时,常用的有While循环法延时法,for嵌套延时法,中断延时法,还有IF延时法,下面我们分别去看看:
⑴while循环法:
While循环是单片程序中经常用到的一个程序,它的书写规则是:
while(表达式)
{
执行内容
}
一般地,在延时程序中时,没有用到后面的执行程序,所以通常的写法是:
a=50000;
while(a--);
是什么意思呢,就是先让a等于一个数,这个数是很大的,原因后面会有说明,当然,这个a要在前面定义的,然后进入while循环中,在while中,1表示真,0表示假,所以,当while后面括号内的数值变成0时,就会跳出这个循环,那么,当a=50000的时候,要在while中减上多少次才能跳出循环呢,答案就是50000次,我们说过机器在处理一次数值,即执行a减一下,就是-1的过程中,会占用1个机器周期,即1US,那么执行50000次是不是就是50000*1,就等于50000US,那么换算成毫秒是多少呢,就是50000/1000=50MS(毫秒),再换算成秒是多少呢,即50/1000=0.05秒,所以,如果在程序中加入这个的话,就会延时0.05秒,如果再想定得短了就让a的值小一些,想改得大一点了,就让a的值大一些,但有点要注意,因为单片机内部空间决定,只能处理65535这么大的数,超过这个数,将先减掉65535,用减剩下的数去赋值,亲们,可不要弄错哦。
那么,我们有没有让它再延长一下呢,这个很好办的,a=50000是0.05秒,如果我们想要得到0.10秒该如何使用呢,那就是再多加一次这样的延时函数,亲们,你们明白了吗?
a=50000;
while(a--);
a=50000;
while(a--);
这样就做到了延时0.10秒的功能了,是不是很简单啊!
⑵for延时法:
for循环是单片程序中经常用到的另一种延时程序,它通常以双for嵌套的方式出现,下面我们看看它的结构吧:
For (表达式1;表达式2;表达式3)
{ 语句(内部可为空)}
执行过程:
1.求解一次表达式1.
2.求解表达式2,若其值为真(非0 即为真),则执行for中语句。然后执行第3步。否则结束for 语句,直接跳出,不再执行第3步。
3.求解表达式3.
4.跳到第2步重复执行。
什么意思呢,我们用一个例子来说明这个问题:
如:for(j=110;j>0;j--);
第一步:进入for循环,这时,看到了把110这个赋给了j,这时,j的值是110,然后执行第二步;
第二步:判断j是不是在于0了,通过第一步我们得知j是110,110当然比0大了,这时就执行第三步;
第三步;当第二步判断完了的时候,这时我们看到j- -,- -也就是减1,
那么当j-1后是多少呢,是109对吧,这里就会返到前面第二步进行继续判断,看j是不是比0大,如果还是0大,那么就会再次执行第三步,如此循环,那么什么时候不执行了呢,也就是当j变成0时,不大于0了,这个程序就结束了,那么这条语句执行完需要多长时间呢,就是110US,亲们,懂了吗?
上面的情况只是在没有其它程序的情况下做的延时,如果有其它后缀程序时,就会在j- -一次的情况下去执行其它的程序,如下面这个程序:
for(j=200;j>0;j--)
{
a++;
}
我们看到,当j每减一次时,a就会加一次,那么这样下来,j=0时,a等于多少呢,答案是200,如果,我们再弄个跟上面一样的延时程序会怎么样呢,我们试试看吧(我把j换成x了,其实不管是j还是x,它们只是一种名称而已):
for(x=500;x>0;x--)
for(y=200;y>0;y--);
注意,当两个for套在一起时,前面一个没有分号哦,后面的相当于是它的了程序,好了,我们分析一下这个程序吧:
通过前面的程序我们得知,当执行时,先进入第一条语句,我们看到,给x赋值是500,这时x是大于0的,所以进行了x减1的操作,x减1后就是499了,那么这里会执行下面的程序,通过前面的讲解,这条语句不能执行的唯一条件就是y=0,即当中的那条程序不成立时才会跳出来,对于这样一条语句,前面说过,y会减200次,所以上面的这条语句的含义就是x减1下,y就会减上200次,那么如果当x也不执行时,唯一的条件就是判断x不大于0,即x减完后等于0时,就会跳出整个for循环,所以得到x是减了500次才跳出这个循环的,那么这个循环用了多长时间呢,我们知道x减一次,y就会减200次,y减200次占用的时间是200US,那么执行了500次以后是多少呢,就是x值与y的值相乘,即为500*200=10000US,如果亲们不好理解时,可以这样想下,y就是延时,x是指延时多少次,这样下来延时的效果是很明显的。
我们前面用了很长的片幅去讲解for循环的原理,那么在程序中是怎么样用的呢,一般以函数的形式出现:
void delayms(unsigned int z)
{
unsigned int i,j;
for(i=z;i>0;i--)
for(j=110;j>0;j--);
}
这是一个延时函数,现在我可以看到,delayms这个就是这个函数的名称,就相当于大家叫我缘缘一样,这个程序的时间是没有定义的,即 i=z,也就是当我们在以后的程序中调用时,我们要写上延时多少次就行了,那么在程序中怎么调用呢,很简单:
delayms(20);
记得加上;号哦,那么这个延时程序的延时是多长时间呢,就是20*110=2200US。
刚才我们用了减的,那么能用加的吗,答案是肯定的,这里只做一个简单的写法,原理是相通的哦:
for(x=0,x<2000,x++);
关于for就这么一回事,亲们,一定懂得怎么算时间哦。
⑶中断延时:因为涉及到中断,所以在后面的中断环节中专门进行讲解,亲们可不要错过哦。
⑷if延时:if是单片机程序中用判断用得最多的,if延时一般与while一起套用,方式如下:
if(表达式)
{
执行内容(可为空)
}
所以我们用if做为延时时,加上while,我们得知while中,1为真,0为假,所以程序这样写:
x=1;
while(x)
{
x++;
if(x=501)
x=0;
}
那么这样的延时时间是多少呢,那就是500-1=500US,因为while为0时进不去,所以我们让定义x的值为1,这地就会在while循环中一直执行x加1的程序,我们看到,当判定x的值达到501时,这时,又给X赋值为0了,在while中,X为0就会跳出来,所以这样就会达到延时目的。
以上就是延时的一些方法,大家可以自由应用哦。
※ 以上就是对延时的讲解说明,可以看出晶震频率对延时有着很大的影响,换而言之,如果晶震频率越高,单片机处理速度也就越快,例如:12MHZ下处理速度是1US,那么如果是24MHZ下是多少呢,12*1/24=0.5US就是原来的一半,也就是0.5US了,那么如果是11.0592MHZ的呢,即为12*1/11.0592=1.085US了,所以延时程序是根据晶震的频率来调节的,亲们,懂了吗?
6、让二极管亮一下灭一下
这个很简单吧,我们知道0就会亮,是1就会灭,那么加上延时就行了,缘缘笨,就用最简单的吧,我们用前一个程序做下例子哦:
#include < reg52.h >
#define uchar unsigned char
#define uint unsigned int
uint a;
sbit D=P0^0;
void main()
通过前几章,我们组建了我们自己的小单片系统,学会了编程烧写的方法,那么如何用它去控制LED呢,缘缘先带你去串串门,认识认识那此单片内的小空间吧:
一、了解单片内部结构,为后面编程控制打基础:
咱们其它的先不要看,会在以后用到的时候进行讲解说明,就瞧瞧这四个与外界相连的这部分空间吧:
51单片机在设计时有四个并行I/O口,每个端口都有8位准双向口,共占有32条引脚,每一条I/O线都能独立地用途输入或输出,每个端口都包括一个数据锁存器(即特殊功能寄存器P0-P3)。
锁存器是干什么的呢,广义上讲,就是可以定义数值,并写入数值的一种存储器,打个比方,就是咱们的U盘,我们往里面复制一首歌,这个歌就会一直存在,相当于被锁存了,当我们不要这首歌时,我们就会删除它,从某种意义上讲,这时,锁存器也改变了状态。
对于这个锁存器,从原理上很难说明白,什么地上拉电阻、场效应、与非门啦,搞上半天都弄晕了,不过我们可以这样理解,把它们看做是一组开关,每个里面都有8个,每个开关都接一根线。
当我们定义成1时,它接高电平,即与电源VCC相连:
当我们定义成0时,它接低电平,即与电源GND相连:
还记得上一章那个LED是怎么点亮的吗:
就是当电源与负极接通时,我们的LED就亮了,通过上面的讲解,我们明白了定义与它们电位对应关系,所以我们对LED控制时,只要定义成0与1就OK了,如果我们定义开关为1时,这时与高电平接通VCC,那么,我们的LED两边都是高电平,肯定点不亮了:
如果我们定义开关为0时,这时与低电平GND接通,这时电路处于通路状态,LED就被点亮了。
那么如何去控制这个开关呢,答案是用程序控制,就是我们常听说的玛雅文字——C语言,在开始程序前,我们有一点点小知识了解下,那就是上节说的,为了什么要接8个电阻,而且这8个电阻接在了P0口,这8个电阻有什么用,阻值有什么限定,那么,亲们,认真看看吧:
关于P0口接上拉电阻的解释:(可要记下哦)
P0口作为I/O口输出的时候时,输出低电平为0输出高电平为高组态(并非5V,相当于悬空状态,也就是说P0口不能真正的输出高电平)。给所接的负载提供电流,因此必须接上拉电阻(一电阻连接到VCC),由电源通过这个上拉电阻给负载提供电流。P0作输入时不需要上拉电阻,但要先置1。因为P0口作一般I/O口时上拉场效应管一直截止,所以如果不置1,下拉场效应管会导通,永远只能读到0。因此在输入前置1,使下拉场效应管截止,端口会处于高阻浮空状态,才可以正确读入数据。
由于P0口内部没有上拉电阻,是开漏的,不管它的驱动能力多大,相当于它是没有电源的,需要外部的电路提供,绝大多数情况下P0口是必需加上拉电阻的。
1.一般51单片机的P0口在作为地址/数据复用时不接上拉电阻。
2.作为一般的I/O口时用时,由于内部没有上拉电阻,故要接上上拉电阻!!
3.当p0口用来驱动PNP管子的时候,就不需要上拉电阻,因为此时的低电平有效;
4.当P0口用来驱动NPN管子的时候,就需要上拉电阻的,因为此时只有当P0为1时候,才能够使后级端导通。简单一点说就是它要驱动LCD显示屏显示就必须要有电源驱动,否则亮不了,而恰好P0口没有电源,所以就要外接电源,接上电阻是起到限流的作用;如果接P1、P2、P3端口就不用外接电源和电阻了。
P0口是开漏的,不管它的驱动能力多大,相当于它是没有电源的,需要外部的电路提供,绝大多数情况下P0口是必需加上拉电阻的;5、51单片机的P0口用作数据和地址总线时不必加上拉电阻。
有些IC的驱动能力并不强,如果P0口作为输入而加了不必要的上拉,有可能驱动IC无法将其拉回到低电平,从而使输入失败!
如果是驱动led,那么用1K左右的就行了。如果希望亮度大一些,电阻可减小,最小不要小于200欧姆,否则电流太大;如果希望亮度小一些,电阻可增大,增加到多少呢,主要看亮度情况,以亮度合适为准,一般来说超过3K以上时,亮度就很弱了,但是对于超高亮度的LED,有时候电阻为10K时觉得亮度还能够用。通常就用1k的。对于驱动光耦合器,如果是高电位有效,即耦合器输入端接端口和地之间,那么和LED的情况是一样的;如果是低电位有效,即耦合器输入端接端口和VCC之间,那么除了要串接一个1——4.7k之间的电阻以外,同时上拉电阻的阻值就可以用的特别大,用100k——500K之间的都行,当然用10K的也可以,但是考虑到省电问题,没有必要用那么小的。
对于驱动晶体管,又分为PNP和NPN管两种情况:对于NPN,毫无疑问NPN管是高电平有效的,因此上拉电阻的阻值用2K——20K之间的,具体的大小还要看晶体管的集电极接的是什么负载,对于LED类负载,由于发管电流很小,因此上拉电阻的阻值可以用20k的,但是对于管子的集电极为继电器负载时,由于集电极电流大,因此上拉电阻的阻值最好不要大于4.7K,有时候甚至用2K的。对于PNP管,毫无疑问PNP管是低电平有效的,因此上拉电阻的阻值用100K以上的就行了,且管子的基极必须串接一个1——10K的电阻,阻值的大小要看管子集电极的负载是什么,对于LED类负载,由于发光电流很小,因此基极串接的电阻的阻值可以用20k的,但是对于管子的集电极为继电器负载时,由于集电极电流大,因此基极电阻的阻值最好不要大于4.7K。
对于驱动TTL集成电路,上拉电阻的阻值要用1——10K之间的,有时候电阻太大的话是拉不起来的,因此用的阻值较小。但是对于CMOS集成电路,上拉电阻的阻值就可以用的很大,一般不小于20K,我通常用100K的,实际上对于CMOS电路,上拉电阻的阻值用1M的也是可以的,但是要注意上拉电阻的阻值太大的时候,容易产生干扰,尤其是线路板的线条很长的时候,这种干扰更严重,这种情况下上拉电阻不宜过大,一般要小于100K,有时候甚至小于10K。
想信通过以上的说明,你对单片机有了更深一层的了解了,那么,我们就对它进行控制吧!
以下内容有二进制转换,单位的换算,C语言基本函数,C语言数值类型,这些缘缘都在开头就讲过了,不懂的可以看看缘缘本系列的第一篇帖子:https://bbs.icxbk.com/index.php?app=group&ac=topic&id=2358
先看看原理图,就是有8个LED共同接在正极上:
那么,再看看我们的P口是怎么回事:
锁存器
端口定义
P0
P0.7
P0.6
P0.5
P0.4
P0.3
P0.2
P0.1
P0.0
P1
P1.7
P1.6
P1.5
P1.4
P1.3
P1.2
P1.1
P1.0
P2
P2.7
P2.6
P2.5
P2.4
P2.3
P2.2
P2.1
P2.0
P3
P3.7
P3.6
P3.5
P3.4
P3.3
P3.2
P3.1
P3.0
注意,所以的P口在定义管脚是从0开始的,可不要记错了哦
为了方便实验,我们就用P0口的那8个端口去操作这8个LED,P0口就是上节我们焊了电阻的那一部分:
1、先点亮第一个二极管
我们先看看P0口的如何去控制这个LED吧:
好了,我们来写写控制LED的这个程序:
首先,为了定义是上面那种管脚的类型,我们先写入头文件,什么是头文件呢?头文件作为一种包含功能函数、数据接口声明的载体文件,主要用于保存程序的声明,而定义文件用于保存程序的实现 ,是不是很难理解呢,那就教你个方法,相当于头文件就是个称呼,比如,你称呼缘缘,缘缘我就知道你在叫缘缘了,所以,在51单片系统中,头文件就是定义了51单片机的存储类型,端口状态,功能结构与数据使用类型,只要是与51单片有关系的,都会在头文件中体现,那么如何在Keil文件中体现呢,我们用如下的方法进行:
#include
怎么样,简单吧,你只要写上这一句,关于单片内部的部分就不用管了,接下来我们写上这么两句话:
#define uchar unsigned char
这两句话的意思就是把unsigned char这种类型的数值用uchar这种名字替代,这样是为了方便写些,对于咱们英文不好的小白们来说,这绝对是一个值得称赞的好注意,写完了这两个,我们要操作的是P0锁存器的第一根端口,即P0.0,我们现在就把它定义一下吧,用一个名字替代它:
sbit D=P0^0;(这里的D是个名称的意思,如果不嫌麻烦,当然也可以写成缘缘的拼音的,因为C语言中不执行汉字,所以就只能写拼音了,亲们,以后记着哦)
那么,到这里,我们先说下上面的define与这里的sbit的区别:
define是泛指,即在程序中出现的与此相关的全部的都是这个类型,主要指数值类型,其意义是宏定义,而sbit是指特指,也就是针对某一个,在程序中常用于定义端口。
到现在,我们基本上明确了这些,我们就把上面的内容汇总到一起,这里记住C语言的几个特点:
1、所有的标点符号全是英文的,如果你在后面弄个中文的标点符号,这肯定编译不过去。
2、用于注释时,即不让这句话在程序中执行时,可以前面打上英文的//,如果要注解一段内容,方法是在最前面写上英文符号/*,后面写上*/。
3、在所有的程序中,只包含一个主程序,也就是main()函数,其写法是:
void main()
{
内容
}
4,一个大括号算是一个完整的程序,常写在函数的后边,里面会包含若干个子程序,也是有大号的哦,意思是会执行其中的内容。
5、一条单独的程序在结束时,以英文符号;结尾,含大括号的除外。
讲了这么多,是不是想写个真正控制LED的程序,那好,跟我一起来吧:
#include < reg52.h >
#define uchar unsigned char
sbit D=P0^0;
void main()
{
D=0;
}
现在我们完成了程序的书写,我们编译下吧,看看,有错误吗,没有,对吧,那好吧,用前面教的方法烧录进去吧,是不是奇迹出现了,这时,你觉得单片机难吗,不难对吗?这可不是我说的
2、分别依次点亮其它的二极管
我们即然能把一个LED点亮,当然其它的就不在话下了,我们只对上面的程序做一个小小的改动就行了,也就是更改sbit D=P0^0;最后面的这个数字,假设我们让第二个亮,就改成sbit D=P0^1,怎么样,简单吧,亲们,自己试试,把其它的都点亮吧!
3、点亮某几个二极管
只要能点亮一个LED,那么剩下就不是问题了,于是我们再多于定义几个不就行了,假高我要点亮1,3,5,7该怎么做呢,跟着缘缘一起写起来吧:
#include < reg52.h >
#define uchar unsigned char
sbit D1=P0^0;
sbit D2=P0^2;
sbit D3=P0^4;
sbit D4=P0^6;
void main()
{
D1=D2=D3=D4=0;
}
4、总线法点亮
总线是什么意思呢,亲们,看看这个说明吧:
我们用程序试验一下吧:
#include < reg52.h >
#define uchar unsigned char
void main()
{
P0=0xfc;
}
好了,那么亲们,发挥你的想像吧,自己乱定义几个,让灯亮吧!
5、延时讲解
以下的延时都会用到一个知识点,就是机器周期,也就是单片机在处理一个数值时用的时间,比如,让它加一下,也就是加个1,那么它就会费一个机器周期的时间去处理这个事情,那么如何去算呢,一个机器周期是晶震的12分频,那么,如果我们用12M的晶震的话,换算的机器周期就是12(晶震)×1/12=1US,为什么这么算呢,因为12M晶震的的意思是在1秒的时间内跳动了12000000次,(那么这个数是多少呢,自己算算吧,缘缘很懒的,就猜了下,是12M(兆)吧!)那么晶震每跳12下,这个单片机就会处理一个事务,所以在1秒内就会处理1000000,那么1次是多少呢,我们就用前面学过的公式进行转换吧,我们知道1S=1000MS,那么1000000次/1000=1000次,也就是说在1MS内,单片机执行了1000次,通过MS的转换得知1MS=1000US,那么1000次/1000就成变成了1次了,所以我们得出一个结论,就是单片机执行一次任务的时间是1US。(这是为了方便计算用的是12M的晶震,如果是其它的晶震,就不是这个值了,但算法是一样的。)
为了方便计算,以下延时按12M晶震计算:
对于延时,常用的有While循环法延时法,for嵌套延时法,中断延时法,还有IF延时法,下面我们分别去看看:
⑴while循环法:
While循环是单片程序中经常用到的一个程序,它的书写规则是:
while(表达式)
{
执行内容
}
一般地,在延时程序中时,没有用到后面的执行程序,所以通常的写法是:
a=50000;
while(a--);
是什么意思呢,就是先让a等于一个数,这个数是很大的,原因后面会有说明,当然,这个a要在前面定义的,然后进入while循环中,在while中,1表示真,0表示假,所以,当while后面括号内的数值变成0时,就会跳出这个循环,那么,当a=50000的时候,要在while中减上多少次才能跳出循环呢,答案就是50000次,我们说过机器在处理一次数值,即执行a减一下,就是-1的过程中,会占用1个机器周期,即1US,那么执行50000次是不是就是50000*1,就等于50000US,那么换算成毫秒是多少呢,就是50000/1000=50MS(毫秒),再换算成秒是多少呢,即50/1000=0.05秒,所以,如果在程序中加入这个的话,就会延时0.05秒,如果再想定得短了就让a的值小一些,想改得大一点了,就让a的值大一些,但有点要注意,因为单片机内部空间决定,只能处理65535这么大的数,超过这个数,将先减掉65535,用减剩下的数去赋值,亲们,可不要弄错哦。
那么,我们有没有让它再延长一下呢,这个很好办的,a=50000是0.05秒,如果我们想要得到0.10秒该如何使用呢,那就是再多加一次这样的延时函数,亲们,你们明白了吗?
a=50000;
while(a--);
a=50000;
while(a--);
这样就做到了延时0.10秒的功能了,是不是很简单啊!
⑵for延时法:
for循环是单片程序中经常用到的另一种延时程序,它通常以双for嵌套的方式出现,下面我们看看它的结构吧:
For (表达式1;表达式2;表达式3)
什么意思呢,我们用一个例子来说明这个问题:
如:for(j=110;j>0;j--);
第一步:进入for循环,这时,看到了把110这个赋给了j,这时,j的值是110,然后执行第二步;
第二步:判断j是不是在于0了,通过第一步我们得知j是110,110当然比0大了,这时就执行第三步;
第三步;当第二步判断完了的时候,这时我们看到j- -,- -也就是减1,
那么当j-1后是多少呢,是109对吧,这里就会返到前面第二步进行继续判断,看j是不是比0大,如果还是0大,那么就会再次执行第三步,如此循环,那么什么时候不执行了呢,也就是当j变成0时,不大于0了,这个程序就结束了,那么这条语句执行完需要多长时间呢,就是110US,亲们,懂了吗?
上面的情况只是在没有其它程序的情况下做的延时,如果有其它后缀程序时,就会在j- -一次的情况下去执行其它的程序,如下面这个程序:
for(j=200;j>0;j--)
{
a++;
}
我们看到,当j每减一次时,a就会加一次,那么这样下来,j=0时,a等于多少呢,答案是200,如果,我们再弄个跟上面一样的延时程序会怎么样呢,我们试试看吧(我把j换成x了,其实不管是j还是x,它们只是一种名称而已):
for(x=500;x>0;x--)
for(y=200;y>0;y--);
注意,当两个for套在一起时,前面一个没有分号哦,后面的相当于是它的了程序,好了,我们分析一下这个程序吧:
通过前面的程序我们得知,当执行时,先进入第一条语句,我们看到,给x赋值是500,这时x是大于0的,所以进行了x减1的操作,x减1后就是499了,那么这里会执行下面的程序,通过前面的讲解,这条语句不能执行的唯一条件就是y=0,即当中的那条程序不成立时才会跳出来,对于这样一条语句,前面说过,y会减200次,所以上面的这条语句的含义就是x减1下,y就会减上200次,那么如果当x也不执行时,唯一的条件就是判断x不大于0,即x减完后等于0时,就会跳出整个for循环,所以得到x是减了500次才跳出这个循环的,那么这个循环用了多长时间呢,我们知道x减一次,y就会减200次,y减200次占用的时间是200US,那么执行了500次以后是多少呢,就是x值与y的值相乘,即为500*200=10000US,如果亲们不好理解时,可以这样想下,y就是延时,x是指延时多少次,这样下来延时的效果是很明显的。
我们前面用了很长的片幅去讲解for循环的原理,那么在程序中是怎么样用的呢,一般以函数的形式出现:
void delayms(unsigned int z)
delayms(20);
记得加上;号哦,那么这个延时程序的延时是多长时间呢,就是20*110=2200US。
刚才我们用了减的,那么能用加的吗,答案是肯定的,这里只做一个简单的写法,原理是相通的哦:
for(x=0,x<2000,x++);
关于for就这么一回事,亲们,一定懂得怎么算时间哦。
⑶中断延时:因为涉及到中断,所以在后面的中断环节中专门进行讲解,亲们可不要错过哦。
⑷if延时:if是单片机程序中用判断用得最多的,if延时一般与while一起套用,方式如下:
if(表达式)
{
执行内容(可为空)
}
所以我们用if做为延时时,加上while,我们得知while中,1为真,0为假,所以程序这样写:
x=1;
while(x)
{
x++;
if(x=501)
x=0;
}
那么这样的延时时间是多少呢,那就是500-1=500US,因为while为0时进不去,所以我们让定义x的值为1,这地就会在while循环中一直执行x加1的程序,我们看到,当判定x的值达到501时,这时,又给X赋值为0了,在while中,X为0就会跳出来,所以这样就会达到延时目的。
以上就是延时的一些方法,大家可以自由应用哦。
※ 以上就是对延时的讲解说明,可以看出晶震频率对延时有着很大的影响,换而言之,如果晶震频率越高,单片机处理速度也就越快,例如:12MHZ下处理速度是1US,那么如果是24MHZ下是多少呢,12*1/24=0.5US就是原来的一半,也就是0.5US了,那么如果是11.0592MHZ的呢,即为12*1/11.0592=1.085US了,所以延时程序是根据晶震的频率来调节的,亲们,懂了吗?
6、让二极管亮一下灭一下
这个很简单吧,我们知道0就会亮,是1就会灭,那么加上延时就行了,缘缘笨,就用最简单的吧,我们用前一个程序做下例子哦:
#include < reg52.h >
#define uchar unsigned char
uint a;
sbit D=P0^0;
void main()
那么每个锁存器是怎么回事呢,如下图:
那么怎么去控制他们呢,这里,我们记住一个概念,即在数字电路中,高电平是1,低电平是0,假设我们上面一个开关是这样的,也就是在没有定义的时候,它们是断开的:
根据以上分析,上拉电阻的阻值的选取是有很多讲究的,不能乱用。
二、编写程序,让梦想从此实现,实战LED:
#define uint unsigned int
#define uint unsigned int
#define uint unsigned int
#define uint unsigned int
{ 语句(内部可为空)}
执行过程:
1.求解一次表达式1.
2.求解表达式2,若其值为真(非0 即为真),则执行for中语句。然后执行第3步。否则结束for 语句,直接跳出,不再执行第3步。
3.求解表达式3.
4.跳到第2步重复执行。
{
unsigned int i,j;
for(i=z;i>0;i--)
for(j=110;j>0;j--);
}
这是一个延时函数,现在我可以看到,delayms这个就是这个函数的名称,就相当于大家叫我缘缘一样,这个程序的时间是没有定义的,即 i=z,也就是当我们在以后的程序中调用时,我们要写上延时多少次就行了,那么在程序中怎么调用呢,很简单:
#define uint unsigned int
谢谢 一直想学单片机 借你的帖子好好学下