(一)
信任许多喜好电子的朋友,对单片机这个词应该都不会生疏了吧。不过有些朋友或许只传闻他叫单片机,他的全称是什么或许并不太清楚,
更不必说他的英文全称和简称了。单片机是一块在集成电路芯片上集成了一台有必定规划的微型核算机。简称为:单片微型核算机或单片机 (Single Chip Computer)。单片机的运用处处可见,运用领域广泛,首要运用在智能仪表、实时操控、通讯、家电等方面。
首要,你必须有学习单片机的热心,不是说今日去图书馆看了一个下午关于单片机的书,而明日玩上半天,后天就不知道那个本书在讲什 么东西了。
第二,已然你想学好单片机,你必须得舍得花钱,假如不买些芯片回来自己着手焊焊拆拆的(可是在后期会介绍给咱们一个很好用的硬件
仿真软件,并不需要你用试验板和仿真器了,直接在你的PC上完结,可是软件究竟是软件,从某个特定的含义上来说是并不能替代硬件的),即便
你每天捧着本书,把那本书翻烂,也永久学不会单片机的!刚触摸单片机的朋友,看了材料,必定会对以下几个词见的比较多,可是详细的概
念仍是比较含糊,现作如下阐明:
(1)编程器 编程器是用来烧单片机芯片的,是把HEX或许BIN文件烧到单片机ROM里的,供单片机运转的。
(2)试验板 试验板是专为初学者依据某些要求而特做的板,一般上面就有一个单片机的最小体系,运用者只需写好程序,烧好芯片,放
到上面加以验证的这么一个东西。有了试验板,对与初学者来说,省去了焊个最小体系的费事。可是关于电子开发人员来说,作用并不是很大
(3)仿真器 仿真器是直接把HEX或许BIN文件暂时放在一个芯片里,再经过这个芯片的引脚衔接到试验板或许体系上作业。这样以来,可 以省去了来回插拔芯片带来的不必要费事。
第三,想学单片机的必需品–PC。因为写程序,编译或许是仿真都是经过PC完结的。假如没有PC,什么也做不了!!!有了PC最好还要可
以上网,因为假如你没有可以和你沟通单片机的人,遇到自己处理不了的问题,一向都想不通,那么估量你学习单片机的热心就会跟着时刻的
推移而渐渐耗尽。假如你能上网经过论坛或许QQ群,问题就很快得到处理。这样的学习功率必定很高!真实的高手是从论坛中泡出来的!
有了上述3个条件后,你就可以开端学你的单片机了。可是,真的做起来并没有我所说的那么简略。你必定会遇到许多许多的问题。比方
为了让单片机完结某个功用,你或许不知道怎样去写某个程序。或是你看懂了材料上某个相似的程序,你自己却写不出来。遇到相似的状况,
记住:千万不要急噪,就行!
(二)
说了这么多了,信任你也看了许多材料了,手头应该也有必备的东西了吧!(不要忘了上面讲过几个条件的哦)。那个单片机终究有什么
功用和作用呢?先不要着急!接下来让咱们点亮一个LED(搞电子的应该知道LED是什么吧^_^)
咱们在单片机最小体系上接个LED,看咱们能否点亮它!对了,上面也有好几次提到过单片机最小体系了,所谓单片机最小体系便是在单片机
上接上最少的外围电路元件让单片机作业。一般只须衔接晶体、VCC、GND、RST即可,一般状况下,AT89C51的31脚须接高电平。
#include《reg51.h》 //头文件界说。或用#include《at89x51.h》其详细的差异在于:后者界说了更多的地址空间。
//在Keil装置文件夹中,找到相应的文件,比较一下便知!
sbit P1_0 = P1 ^ 0; //界说管脚
void main (void)
{
while(1)
{
P1_0 = 0;//低电平有用,假如把LED反过来接那么便是高电平有用
}
}
就那么简略,咱们就把接在单片机P1_0上的LED点亮了,当然LED是低电平,才干点亮。因为咱们把LED的正经过电阻接至VCC。
P1_0 = 0; 相似与C言语中的赋值句子,即把 0 赋给单片机的P1_0引脚,让它输出相应的电平。那么这样就能到达了咱们预先的要求了。
while(1)句子仅仅让单片机作业在死循环状况,即一向输出低电平。假如咱们要试着点亮其他的LED,也相似上述句子。这儿就不再讲了。
点亮了几个LED后,是不是让咱们联想到了富贵的街区上活动的彩灯。咱们是不是也可以让几个LED顺次按次序亮呢?答案是必定的!其
实显现的原理很简略,便是让一个LED灭后,另一个当即亮,顺次轮番下去。 假定咱们有8个LED别离接在P1口的8个引脚上。硬件衔接,在
P1_1–P1_7上再接7个LED即可。例程如下:
#include《reg51.h》
sbit P1_0 = P1 ^ 0;
sbit P1_1 = P1 ^ 1;
sbit P1_2 = P1 ^ 2;
sbit P1_3 = P1 ^ 3;
sbit P1_4 = P1 ^ 4;
sbit P1_5 = P1 ^ 5;
sbit P1_6 = P1 ^ 6;
sbit P1_7 = P1 ^ 7;
void Delay(unsigned char a)
{
unsigned char i;
while( –a != 0)
{
for(i = 0; i 《 125; i++); //一个 ; 表明空句子,CPU空转。
} //i 从0加到125,CPU大约就耗时1毫秒
}
void main(void)
{
while(1)
{
P1_0 = 0;
Delay(250);
P1_0 = 1;
P1_1 = 0;
Delay(250);
P1_1 = 1;
P1_2 = 0;
Delay(250);
P1_2 = 1;
P1_3 = 0;
Delay(250);
P1_3 = 1;
P1_4 = 0;
Delay(250);
P1_4 = 1;
P1_5 = 0;
Delay(250);
P1_5 = 1;
P1_6 = 0;
Delay(250);
P1_6 = 1;
P1_7 = 0;
Delay(250);
P1_7 = 1;
}
}
sbit 界说位变量,unsigned char a 界说无符字符型变量a,以节约单片机内部资源,其有用值为0~255。main函数调用Delay()函数。
Delay函数使单片机空转,LED持续点亮后,再灭,下一个LED亮。while(1)发生循环。
(三)
上面咱们讲了怎样使LED发生活动,可是你是否发现一个问题:写的太冗长了!能不能再简略点呢?可以!可以运用C51的内部函数
INTRINS.H完结。函数unsigned char _crol_(unsigned char a, unsigned char n) 可以使变量a循环左移n位,假如咱们先给P1口赋
0000 0001那么当n为1时,便会发生和上面相同的作用!
#include《intrins.h》
#include《reg51.h》
void Delay(unsigned char a)
{
unsigned char i;
while( –a != 0)
{
for(i = 0; i 《 125; i++);
}
}
void main(void)
{
unsigned char b, i;
while(1)
{
b = 0xfe;
for(i = 0; i 《 8; i++)
{
P1 = _crol_(b, 1);
b = P1;
Delay(250);
}
}
}
INTRINS.H函数中的unsigned char _cror_(unsigned char a, unsigned char n)右移也可以完结相同的作用!这儿就不再累述。
流水灯的把戏许多,我还写过那种拉幕式的活动等,程序很简略,有爱好的朋友,可以自己试着写写!
对了,讲了那么多,有些朋友必定还不知道编译软件怎样用?这儿给咱们介绍几个吧?WAVE(伟福)咱们必定传闻过吧!还有一个
便是KEIL2,我用的便是KEIL2,下面就来讲讲怎样运用KEIL2这个编译软件!
1.装置软件,这个应该不必再讲了吧!
2.装置完后,发动KEIL软件左击Project–》New Project–》输入文件名–》挑选咱们所以运用的芯片(这儿咱们一般用到Atmel的
AT89C51或AT89C2051,点确认。
3.点File–》New–》输入咱们编写的程序,保存为.C文件。(一般状况下,咱们保存的文件名和前面的工程名相同。)
4.打开Target 1 –》右击Source Group 1 –》Add Files to Group ‘Source Group 1’–》挑选方才保存的.C文件点击ADD后,封闭对
话框。这样.C文件就被加到了Source Group 1 下。
5.右击Target 1–》OpTIons for ‘Target 1’ –》Target中填写晶体的巨细,Output中,在Create HEX Files 前打上钩,点确
定。
6.点Project–》Rebuild All Traget Files ,若提示
creaTIng hex file from “XXX”。。.
“XXX” – 0 Error(s), 0 Waring(s)。
表明编译和生成HEX文件成功!接下来的便是把HEX文件烧到单片机中,或是仿真器上,看是否到达预先的意图!
嘿嘿!现在是否自己好有成就感了,假如让你去做个流水彩灯,开发一个简略的产品,只需加上驱动电路,就可以做出美丽的活动彩灯
了!到现在为止,你应该知道单片机的功用有多强壮了吧,假如单纯的用数字电路或模仿电路的常识去规划一个活动彩灯,或许要花点时刻
和时刻才行,有了单片机,那就不相同了,你只需写程序操控他就行!有人说过这样一句话,也并不无道理的,学单片机,程序思维很重要!
(四)
呵呵,朋友!信任你的流水灯也做的不错了吧,现在能玩出几种把戏了?你或许会说,只需你想得到,想怎样流就怎样流!呵呵,是的。
可是工程师们规划这么一个单片机,并不是只为了让它做流水灯的,那样也太糟蹋点了吧 。。. ^_^
学过数字电路的朋友,必定着手做过8路或许6路的抢答器。用朴实的数字电路常识来做,自己规划电路,感到比较困难!抢答器上用的显
示器多为7段数码管,这儿咱们来讲讲,怎样用单片机让数码管显现0-9。抢答器的完结,咱们放到后边再来讨论,因为抢答器还触及了键盘的
内容。8段数码管分为共阴和共阳两种。8段数码管是由8个LED组成(还包含一个小数点)。若为共阳,则8个LED的阳级是衔接在一同的,同理
若为共阴,则阴极衔接在一同。8个LED对应的标号如下:({0x3f, 0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0-9数字)
a 0 1 2 3 4 5 6 7 8 9
__ 0011 1111,0000 0110,0100 1111,0101 1011
f | | b
|__|
|g | c
e |__| 。 dp
d
一般状况下,为了核算或取码的便利,咱们把a-dp顺次接到单片机某个口上的Px.0–Px.7上。x表明0,1,2,3其间的一个。这样咱们只
要给某个口,赋一个值,则相应的LED段就被点亮,可是在硬件衔接上要留意了:单片机或许不能直接驱动LED,所以咱们可以经过操控三级管
的导通或截止,来操控LED的亮与灭!
假如咱们把共阴的数码管的a–dp顺次接到单片机的P0.0–P0.7上,留意:P0口需接上拉电阻。何为上拉电阻,简略的说,便是把电平拉
高,以进步驱动才能。那么比方:P0 = 0X3F;则显现为数字 0 。因为0X3F 即为2进制的 0011 1111 咱们低位往高位数,顺次为1111 1100,
其I/O的电平别离为高、高、高、高、高、高、低、低,即对应的a–dp 为亮、亮、亮、亮、亮、亮、灭、灭,由上图咱们可以看出g和dp段不
亮其他段均亮,即为咱们所看到的数字 0 字样。其他的数字或字符,也同理可以得到。可是有些朋友就会问,那咱们每取一个字模,岂不是
很费事?还有自己考虑凹凸电平什么的?^-^ 呵呵,其实网上有许多LED取模软件,假如有必定核算机编程言语的朋友,也可以试着自己写个
取模的程序,让核算机为咱们核算,比方上述0X3F的数值。
#include《reg51.h》
void Delay(unsigned char a)
{
unsigned char i;
while( –a != 0)
{
for(i = 0; i 《 125; i++);
}
}
void main(void)
{
P0 = 0X3F; //显现 0
Delay(250);//延时
P0 = 0X00;//时刻短的封闭显现,若不封闭,或许会形成显现含糊不清。
P0 = 0X06; //显现 1
Delay(250);
P0 = 0X00;
。。. //以下显现数字2-F,略。
}
看到这儿,想必咱们必定可以把0-F显现出来了吧!可是假如要你显现两位数,三位数呢?或许,有的朋友会这么想:在P0口上接一个
数码管,再在P1口上接个数码管!可是,假如要显现4位、5位的数字呢?那岂不是一块AT8951都接不过来!难到就不能接4位或5位以上的吗?
必定不是的!
提到这儿,咱们来讲讲数码管的显现方法,可分为两种:动态扫描和静态显现。上面咱们所说的即为静态显现。可是假如咱们选用动态扫
描显现,那么就可以处理上面的问题,即可以显现多个数码管了。上面咱们所说的静态显现把数码管的COM脚接至VCC或GND端,其他的接至PX
口上,这样只需PX口上输出相应的凹凸电平,就可以显现对应的数字或字符。可是假如咱们选用动态扫描的方法,比方显现6个数码管,硬件
衔接可以这样处理:a–dp仍是接至P0.0–P0.7上,还有6个COM脚再接至别的口的P2.0–P2.5。P0口作段选(操控数字字符)P2口作位选(选
通哪个数码管导通)这样咱们操控P0和P2口就可以操控6个数码管了。可是,细心的朋友,会问这样的问题:P2位选,是让数码管一个一个亮
的,那仍是不能操控6个一同亮或灭嘛!? ^_^ 想想好象是对的哦?怎样办。。.莫非错了?
嘿嘿,问你个问题?黑夜里,拿着一支烟,在你面前快速的晃动,你会发现什么样的现象?是不是原本不接连的点变成了一条看上去连
续的曲线或许直线!再回过头来,细心想想咱们的数码管!原理是相同的,你可别忘了,咱们的单片机可是一个核算机哦,核算机的运算速
度,咱们可想而知吧!
这儿再说说51单片机的机器周期和时钟周期等概念。所谓机器周期便是拜访一次存储器的时刻。而1个机器周期包含12个时钟周期。假如
单片机作业在12M晶体下,那么一个时钟周期为:1/12奇妙。一个机器周期12*1/12 = 1奇妙。假如晶体为6M,时钟周期和机器周期各是多少呢
?在汇编中,咱们还要关怀,指令履行的机器周期犬牙交错,有1个周期、2个周期和4个周期等。
说着说着,跑了这么远了。。.仍是回到本来的论题,假如咱们把位选的P2也看作上面的“烟”一划而过,那么咱们看到的是不是6个一同亮
或一同灭了! ^_^ 哈哈,本来如此。。. 记住,在任何某一时刻,有且只需一个数码管能发光。假如你能把这句话理解了,你是真理解
我的意思了!朋友,现在给你个使命,让6个数码管别离显现1、2、3、4、5、6。看你自己可以搞定不?你自己先试着写写看咯。。.
#include《reg51.h》
void Delay(unsigned char a)
{
unsigned char i;
while( –a != 0)
{
for(i = 0; i 《 125; i++);
}
}
void main(void)
{
while(1)
{
P0 = 0x06;//1的码段
P2 = 0x01;//选通一位,或许P2_0 = 1;
Delay(20);//延时约20毫秒
P0 = 0X00;//封闭显现
P0 = 0x5b;//2的码段
P2 = 0x02; //选通一位,或许P2_1 = 1;
Delay(20);
P0 = 0X00;
P0 = 0x4f;//3的码段
P2 = 0x04; //选通一位,或许P2_2 = 1;
Delay(20);
P0 = 0X00;
P0 = 0x66;//4的码段
P2 = 0x08; //选通一位,或许P2_3 = 1;
Delay(20);
P0 = 0X00;
P0 = 0x6d;//5的码段
P2 = 0x10;//选通一位,或许P2_4 = 1;
Delay(20);
P0 = 0X00;
P0 = 0x7d;//6的码段
P2 = 0x20;//选通一位,或许P2_5 = 1;
Delay(20);
P0 = 0X00;
}
}
(五)
信任咱们必定见过数字时钟,教学楼大厅必定有吧。每次路过,基本上仅仅随意瞟上一眼,底子没去想过他的作业原理什么。可是今日
你也可以把他做出来了,是不是觉得自己很有成就感呢!呵呵! ^_^
接上面所讲的,咱们先来做个简略的试验:在一个数码管上轮番显现0–9这10个数字。还楞着干什么,快着手写程序呀!好象有点难哦,
要不先不要往下看了,嘿嘿,关机吧,自己先去想想,怎样样?
#include《reg51.h》
unsigned char code SEG_TAB[ ] ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0-9数字
void Delay(unsigned int a) //unsigned int 界说为无符整形,取值规模为0–32768
{
unsigned char i;
while( –a != 0)
{
for(i = 0; i 《 125; i++);
}
}
void main(void)
{
unsigned char i;
while(1)
{
for(i = 0; i 《 10; i++)
{
P0 = SEG_TAB[ i ]; //取SEG_TAB数组中的值
P2 = 0X01;
Delay(1000);
}
}
}
是不是显现从0–9,跳动显现,你的心是不是也跟着一同跳呀,离咱们的方针又迈进了一步!不错,持续尽力!
上面只显现了一个数码管的数字0–9,可是怎样样要让他显现6个数字呢?这样咱们就可以做个时钟出来玩玩了!还记不记得咱们前面
讲过的P2口的位选作用!嘿嘿,没忘掉就好!
#include《reg51.h》
unsigned char hour = 12, min = 0, sec = 0;
unsigned char code SEG_TAB[ ] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0-9数字
void Delay(unsigned char a)
{
unsigned char i;
while( –a != 0)
{
for(i = 0; i 《 125; i++);
}
}
void disp(void)
{
P0 = SEG_TAB[ sec % 10 ];//显现秒的个位
P2 = 0X01;
Delay(15);
P2 = 0;
P0 = SEG_TAB[ sec / 10 ];//显现秒的十位
P2 = 0X02;
Delay(15);
P2 = 0;
P0 = SEG_TAB[ min % 10 ];//显现分的个位
P2 = 0X04;
Delay(15);
P2 = 0;
P0 = SEG_TAB[ min / 10 ];//显现分的十位
P2 = 0X08;
Delay(15);
P2 = 0;
P0 = SEG_TAB[ hour % 10 ];//显现时的个位
P2 = 0X10;
Delay(15);
P2 = 0;
P0 = SEG_TAB[ hour / 10 ];//显现时的十位
P2 = 0X20;
Delay(15);
P2 = 0;
}
void main(void)
{
while( 1 )
{
disp( );
}
}
编译烧录芯片后,调查运转现象。矣。。.怎样一向显现12:00:00,莫非是时钟没有发动?仍是,别的的原因呢? 哦,本来是3个变量
sec,min,hour初始化后,其值一向没有改动!那咱们怎样样才干让他改动数值呢?有的朋友必定会这么以为:让秒个位延时1秒,后加1,
而秒十位延时10秒后,再加1,一向加到6,分个位加1,顺次类推。。.这样的主意是不错,可是朋友你有没有想过C言语的一般延时(除非你
把他放到中止里)极不准确!这样累计下来,一天24小时的差错,必定很大很大,我从前也用延时的方法写过时钟,1个小时差错8秒,那是
个什么概念!一天24小时就要24*8=192,约为3分钟,一个月便是10分钟。。.有没有其他的方法可以改进些呢?有!这儿就要触及到单片机中
另一个比较重要的中心部分:单片机的中止和守时器的运用!想写出比较准确(这儿说的只的相对前面的做法而言比较准确罢了,假如要做
愈加准确的时钟,用时钟芯片比较好点,常用的有DS12887和DS1302等)的时钟程序,就必定要调用中止和守时器。仍是咱们先看看教材和书
吧,究竟人家出的书,必定比我要写的体系多了,下面咱们再来简略的讲讲!
(六)
什么是中止呢?讲个比较浅显的比方:比方你正在家中看电视,忽然电话响了,你的榜首反响是什么?是不是先跑曩昔接电话!接完电话
后,持续看电视。这便是个中止的比方,中止是由电话引起了,你跑曩昔便是呼应中止,接电话便是中止的处理!接完电话后,接续看电视,
即康复中止,等候下个中止的到来!
可是这个好象和单片机没什么联络呀?有的朋友或许会这样疑问。是的。单片机当然不会看电视了,也不会接电话了 ! ^_^ 可是,类
比一下:比方单片机正在履行某个使命,忽然要有更重要的事情,要求单片机呼应,单片机就会应对呼应,去履行更为重要的使命(中止处理
),本来的使命就持续等候(现场的维护)。履行完更重要的使命后,回到中止的入口处,持续履行本来的使命(现场中止的康复)。51系列
的单片机共有5个中止源,别离为:外中止0 、守时器T0中止、外中止1、守时器T1中止、串口中止。
或许,有些朋友现已大约领会了其间的意思,有些朋友还模模糊糊。不过没联系,咱们持续往下看,下面咱们来讲讲单片机的守时器是什
么?怎样作业的?守时器,咱们从字面上就可以看出其大约的意思吧?简略的说:便是起守时作用!也便是让单片机计数。守时器分为:方法
0方法1、方法2和方法3等4种作业方法。有些朋友必定会问:守时器怎样发动?电扇的守时器,信任咱们必定都用过吧!可是单片机的守时器,
该怎样发动呢?总不应也用手一拧守时器吧! ^_^ 当然不是,咱们只需给单片机一些指令,就可以发动守时器了!下面咱们就守时器0,来说
说怎样发动守时器0。
TMOD = 0X01;//设置守时器0 作业方法0
TH0 = (65536 – 5000) / 256;//载入高8位初值
TL0 = (65536 – 5000) % 256;//载入低8位初值
TR0 = 1; //发动守时器
^_^,简略吧,这样咱们就可以把守时器发动了。其间TMOD为T/C方法操控存放器:
D7 D6 D5 D4 D3 D2 D1 D0
_ _
GATE C/T M1 M0 GATE C/T M1 M0
|_________ __________| |_________ __________|
| T/C1 | | T/C0 |
C/T便是counter(记数器)和TImer(守时器)的挑选位,若值为1,则作计数器用。为0,则为守时期用!GATE为门控位。M1和M0作业方
式的挑选:若M1=0;M0=0 则为方法0:13位守时/记数器。若M1=0;M0=1则为方法1,16守时/记数器。若M1=1;M0=0则为方法2,主动装载8位
守时/记数器。若M1=1;M0=1则为方法3,只适用于T/C0,2个8位守时/记数器。
说了一大堆,感到有点困惑了吧。那咱们仍是来说说上面的。TMOD= 0X01;//至于为什么是0X01,咱们看:咱们挑选的是守时器0方法0,
所以T/C1全为0,而T/C0的M1为0。M0为1,所以D0-D7为0X01;0X01表明的是16进制数,这个咱们应该都知道吧!还有D0-D7表明的是2进制数。
还需要转化一下!
TH0 = (65536 – 5000) / 256;//载入高8位初值。若在12M晶体下,守时5000微秒,即为5毫秒;可是假如不是在12M下,那又该怎样核算
了呢?假如是11.0592M呢?还记不记得,咱们前面讲过的机器周期和时钟周期的概念? ^_^忘了,仍是看看前面吧!呵呵!没事,学习嘛,忘
了再翻翻书,看看就可以了!其实上诉的5000 = 1 * C 很明显C=5000,可是假如是11.0592M那么就不是1了,应该是1.085了,那么5000 =
1.085 * C,则C就为5000 / 1.085 = ? 详细多少,咱们自己去算算吧?同理TL0也是相同的! 可是,细心的朋友会发现网上或许是材料上的
TH0,TL0并不是和上面相同的,而是直接TH0 = 0XEC;TL0 = 0X78 是不是和上面的相同的,别忘了单片机也是核算机的一种哦。用C的话,直
接写上核算公式就行,核算就交给单片机完结。
TR0 = 1;这句便是发动守时器0,开端记数!哦,还有一点,有些朋友会问,你是65536是哪里来的呢?呵呵你可别忘了:设置守时器0
作业方法0是16位的(2的16次方是多少,自己算算就知道了)简略吧?可是怎样和中止一同运用呢?请持续看下面的解说!
TMOD = 0X01;//设置守时器0 作业方法0
TH0 = (65536 – 5000) / 256;//载入高8位初值
TL0 = (65536 – 5000) % 256;//载入低8位初值
TR0 = 1; //发动守时器
EA = 1;//开总中止
ET0 = 1;//开守时器中止。若为0则表明封闭!
这样咱们,就初始化守时器T0和中止了,也便是守时器满5毫秒后,发生一次中止。发生中止后,咱们怎样处理呢?嘿嘿!细心想想?
^_^
每次中止后,咱们可以让一个变量自加1,那么200次中止后,不便是1秒的时刻了吗?比起上面咱们说的延时来出来是不是愈加准确多了呢?
那是必定的!可是想想1秒种的时刻就让单片机发生那么屡次的中止,单片机会不会累着呢?恩,那么欠好。假如在12M的晶体下,T0每次中
断不是可以发生最多65.336毫秒的时刻吗?那么咱们让他每50毫秒中止一次好了!这样咱们就20次搞定一秒的时刻了! ·爽·
好了,讲了那么多,现在咱们来写个时刻的程序吧! ^_^
#include《at89x51.h》
#define HI ((65536 – 50000) / 256)
#define LO ((65536 – 50000) % 256)
#define _TH0_TL0_ (65536 – 50000)
#define M 20 //(1000/25)
/**********************************************************************************************/
unsigned hou = 12, min = 0, sec = 0;
unsigned char SEG_TAB_B[ ] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //0-9数字
unsigned char SEG_TAB_A[ ] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//0.-9.数字
/*********************************************************************************************/
void Delay(unsigned char a)//延时程序a*1MS
{
unsigned char j;
while(a– != 0)
{
for (j = 0; j 《 125; j++);
}
}
/*********************************************************************************************/
void Disp(void)//数码管显现
{
P2_0 = 1;
P1 = SEG_TAB_B[ hou / 10 ];
Delay(5);
P2_0 = 0;
P2_1 = 1;
P1 = SEG_TAB_A[ hou % 10 ];
Delay(5);
P2_1 = 0;
P2_2 = 1;
P1 = SEG_TAB_B[ min / 10 ];
Delay(5);
P2_2 = 0;
P2_3 = 1;
P1 =S EG_TAB_A[ min % 10 ];
Delay(5);
P2_3 = 0;
P2_4 = 1;
P1 = SEG_TAB_B[ sec / 10 ];
Delay(5);
P2_4 = 0;
P2_5 = 1;
P1 = SEG_TAB_B[ sec % 10 ];
Delay(5);
P2_5 = 0;
}
/********************************************************************************************/
void IsrTImer0(void) interrupt 1 using 1 //守时50ms
{
static unsigned char count = 0; //界说静态变量count
count++;
if(count == M)
{
count = 0;
sec++;
if(sec == 60)
{
min++;
sec = 0;
if(min == 60)
{
hou++;
min = 0;
if(hou == 24)
{
hou = 0;
}
}//if
}//if
}//if
}
/******************************************************************************************/
void Timer0Init(void) //守时器0
{
TMOD = 0x01;
TH0 = HI;
TL0 = LO;
TR0 = 1;
ET0 = 1;
EA = 1;
}
/******************************************************************************************/
void main(void) //主函数
{
Timer0Init();
while(1)
{
Disp();
}
}
简略吧,仍是有点看不懂哦,那你自己渐渐领会吧,假如你自己能写个时钟程序来,那么你的51单片机也就学了80 % 了。中止和
守时/记数器器,是个很重要的东西,简直用到单片机的当地都会触及到中止和守时!所以咱们要好好把握哦! ^_^
哈哈,赶忙编译HEX文件,搭好硬件,烧入单片机,上电看看作用先!呵呵,现在你应该有成就感了吧,想不到一个时钟竟然那么
简略, 嘿嘿!可是问题来了!时钟尽管做出来了,可是他的精度怎样样呢?一两个小时,或许看不出什么差错,可是一天或许一年呢?
晕,我的天呀,要是按年来算的话,那这个时钟底子没有实用价值!人家都说用C写不出,精度高的时钟程序来的!!!是不是有点懊悔
了,去学汇编吧!可是已然挑选了C,那么就不要懊悔!嘿嘿,想想C的高档言语,怎样会输给汇编呢 ^_^ 呵呵!看下面这段代码:
static unsigned char count = 0;
TR0 = 0;
TL0 += (_TH0_TL0_ + 9) % 256;
TH0 += (_TH0_TL0_ + 9) / 256 + (char)CY;
TR0 = 1;
count++;
在中止处理服务程序中,咱们参加上面的代码。 TR0 = 0; 先封闭守时器T0,然后从头给TH0和TL0 赋值,再敞开 TR0 = 1;烧入单片
机看看作用,怎样样,你榜首次准确多了吧。可是仍是有差错!抑郁!为什么呢?那是硬件形成的差错,咱们可以用软件来补偿!咱们先
把时钟点亮,让他走上几个小时或许是几天,看看究竟差错是多少!取个平均值。(这儿比方咱们10小时快1秒)那么可以经过以下句子
if(hour % 10 = 0)
{
sec–;
}
来补偿!这样或许会呈现这样的现象:秒直接跳变!咱们可以再经过细分来完结,不要10小时那么大,小些的就行!详细的操作仍是留给
朋友们吧!
(七)
这回咱们来讲讲键盘,咱们必定见过银行柜员机吧,取钱输入暗码就要用到键盘,超市购物取回存放物品要输入暗码,还有你现在在
用的PC机的键盘。可是键盘的是怎样作业的呢?一般有2种方法:(1)扫描法,不断扫描键盘的状况,送CPU判别并处理。假如键盘数目一
大的话,明显不适合(2)线反转法,经过队伍状况的改动来判别有无键被按下!
现在咱们在P1口接个4*4的键盘,P1.0--P1.3接行,P1.4—P1.7接列,再接4个4K7的上拉电阻至VCC。代码如下:
//—-键盘扫描法程序——-
//—-用数码管显现相应的键值—–
//P1.0--P1.3接行——-
//P1.4—P1.7接列——-
#include《reg51.h》
unsigned char code tab[ ]={0x3F,0x06,0x5B,0x4F,
0x66,0x6D,0x7D,0x07,
0x7F,0x6F,0x77,0x7C,
0x39,0x5E,0x79,0x71};//0到F的16个键植
/******************************************************************************/
void Delayt(unsigned char t)//延时函数
{
unsigned char i;
for(t=0;i《=t;t++)
for(i=0;i《255;i++);
}
/******************************************************************************/
bit pkey(void)//判别键的否被按下,经过返回值确认
{
P1=0xf0;
if(P1!=0xf0)
{
Delayt(25);
if(P1!=0xf0)
return 1;
else
return 0;
}
else
return 0;
}
/******************************************************************************/
void main(void)//主函数
{
unsigned char key,j,k,s;
while(1)
{
if(pkey()==1)
{
P1=0xfe;
k=0xfe;
for(j=0;j《4;j++)
{
s=P1&0xf0;
switch(s)
{
case 0xe0: key=4*j+0; break;
case 0xd0: key=4*j+1; break;
case 0xb0: key=4*j+2; break;
case 0x70: key=4*j+3; break;
default: break;
}
k=(k《《1)|0x01;
P1=k;
}//for
}//if
//if((P1&0xf0)==0xf0)
P0=tab[key];
P2=1;
Delayt(50);
}//while
}
还有一种便是线反转法,完结如下:
1.和扫描法相同,把列线置低电平,行置高,读行状况
2.与1相反,把行置低,列置高,读列状况
3.若有键按下,则为2次所读状况的成果即为键地点的方位,这样2次输出和2次读入可以完结键的辨认!!!
子函数如下:
unsigned char key_vscan(void)
{
unsigned char row, col;
P1 = 0xF0;
row = P1&0xF0;
row = row&0xF0;
P1 = 0x0F;
col = P1&0x0F;
col = col&0x0F;
return(key_val(row|col));
}
下面咱们再来介绍介绍一键多能的程序,即按下一个键,可以履行不同的指令!
void main (void)
{
unsigned char b = 0;
while( 1 )
{
if(P1_0 == 0)
{
Delay(10);
if(P1_0 == 0)
{
b++;
if( b == N )//N为键的功用数目
{
b = 0;
}
while(P3_2 == 0);//等候键松开
}
}
switch( b )
{
case 1: P2_0 = 0xFE;
break;
case 2: P2_1 = 0xfd;
//。。.。。.。。.。。.。.add your code here!
}
}
}
(八)//以上的文字写于2005年5月,因为时刻联系,一向未能将此完结,最近闲着无聊又接着写了些文字,以下写于2006年6月5日!
在这儿我想对上面一点,作个简略的阐明,假如你是刚学单片机,那么你写的代码是VERY GOOD的,可是假如把上面的代码运用于产品的话,那么我可以告知你,上面所写的按键辨认代码全部是废物代码,^_^,这下傻了吧,呵呵。为什么?我的按键不是可以正常作业吗?
请看这儿:
if(P1_0 == 0)
{
Delay(10);//问题就在这儿,你让CPU在这儿空转?
if(P1_0 == 0)
{
//。。.add your code here.
}
}
进入第1个if判别句子后,就进入了Delay(10);再看Delay函数,彻底让CPU履行(;空句子),所以在做大的产品或许代码时,这个是十分消耗单片机内部资源的。有什么方法吗?呵呵,那是必定的。
处理方法大致有如下2种:
1.将延时函数放在中止中,在中止里查询延时的标志位。/*不仅仅用于键盘辨认,亦可以用于其他的延时代码,见EX1*/
2.直接在中止中查询按键的标志位。//见EX2。
EX1:
unsigned char Delaytime;
void Delay(unsigned char Delaytime)//
{
while(Delaytime !=0 );//等在这儿,直到Delaytime为0。
}
void Timer0_interrupt(void) interrupt 1 using 2
{
if(Delaytime != )
Delaytime–;
//。。.add your other code here
}
Delay函数详细延时多长时刻,就要看你设定的T0守时器中止和Delaytime的乘积,比方你的守时器中止为50MS,Delaytime为20的话,那么50MS*20=1S。
EX2:
#define Press_key = P2 ^ 7;//界说按键的I/O
void P_key(void)
{
char new_value,old_value;
new_value = Press_key;
if(new_value && !old_value)//辨认按键。
{
Turn_On_LEd( );
//。。.add your other code here.
}
old_value = new_value;
}
void Timer0_interrupt(void) interrupt 1 using 2
{
P_key();
// 。。.add your other code
}
当然在实践进程傍边,并不是如此简略简练的,还期望咱们可以触类旁通哦。。. ^_^。
(九)
写了这么多了,咱们也看了这么多了,感觉怎样样?咱们也觉得不难吧。其实51也就那么简略,真的很期望咱们看完这篇文字今后,很自傲的说,51单片机也现已入门。这是对我写怎样多文字最好的答复。时隔13个月之久再来持续写这些东西,没有曾经的激_情和热心,所以就敷衍了事结束,期望咱们不要在背地里骂我哦,^_^。当然以上讲的仅仅最简略的一些东西,单片机的功用十分之强壮,只需你能想得到,就必定可以用单片机来完结的。
当然单片机和外部其他的芯片还有许多,比方数字温度传感器DS18B20,实时时钟芯片DS1302,还有比方拜访AT24CXX的EEPROM存储器等,更多的电路,还要靠咱们在平常的学习进程傍边,渐渐把握