利用51單片機(jī)制作一個(gè)秒表的詳細(xì)過程
前面的話:
和很多朋友一樣,在學(xué)51單片機(jī)的過程中我們肯定會(huì)涉及到制作一個(gè)秒表,牽涉到把單片機(jī)的多個(gè)部分組合起來使用,這對于我們初學(xué)者來說可能顯得有些困難,我同大家一樣,百思不得其解,最后頭都弄大了才把這個(gè)秒表制作出來,為了給以后的朋友們一些思路,一些參考,所以在這里我把自己制作的整個(gè)詳細(xì)過程整理出來供大家參考。我調(diào)試出來是沒有問題的,各方面都穩(wěn)定運(yùn)行,由于我水平有限,中間可能會(huì)有不對的地方,歡迎大家指正,我們一起學(xué)習(xí),一起進(jìn)步!
我將分為三個(gè)部分來介紹:1.整體思路,2.硬件電路方面,3.軟件編程方面。
1.整體思路
利用51單片機(jī)制作秒表時(shí),我介紹精確到十分位(即0.1s)的制作,并讓其擁有啟動(dòng),暫停,復(fù)位三個(gè)功能。
用到的單片機(jī)部分:定時(shí)器部分,獨(dú)立按鍵的檢測與應(yīng)用,數(shù)碼管的顯示,并結(jié)合一些簡單的程序即可實(shí)現(xiàn)。
用5位數(shù)碼管來進(jìn)行顯示,分別顯示秒的十分位,秒的個(gè)位,秒的十位,分的個(gè)位,分的十位。用定時(shí)器定時(shí)50ms,2個(gè)定時(shí)器中斷即是0.1s,即秒的十分位,20個(gè)定時(shí)器中斷即是1s,60個(gè)1s即是1分鐘,通過程序?qū)?位數(shù)碼管的值分離出來,并進(jìn)行顯示。這就是我在數(shù)碼管顯示方面的思路,如果不是太清楚,結(jié)合我下面軟件編程方面的程序來看你可能就會(huì)明白,我會(huì)在那部分做詳細(xì)介紹,看完了可能你就懂了。
利用獨(dú)立按鍵設(shè)置啟動(dòng)/暫停鍵和清零鍵,利用獨(dú)立按鍵的檢測,若啟動(dòng)/暫停按鍵按下,秒表則啟動(dòng)或者暫停,按下復(fù)位鍵,秒表清零復(fù)位。我在程序后面全都有注釋,不用擔(dān)心??赐昴憔蜁?huì)明白了。
這是我制作的的流程圖:
“ms100”表示秒的十分位,"s"表示秒的個(gè)位,“s1”表示秒的十位,“min”表示分的個(gè)位,“min1”表示分的十位。
“cnt”表示秒的計(jì)數(shù)位,即多少個(gè)一秒,定時(shí)滿一秒加1,“minu”表示分的計(jì)數(shù)位,即多少個(gè)一分鐘,一分鐘加1,
這個(gè)流程圖提供了大致思路,要結(jié)合下面的程序部分一起看??赡苡行┖喡?,請多多包涵!
看起來是不是好簡單?有木有?請繼續(xù)往下看。
2.硬件電路方面
每個(gè)人的硬件可能都不一樣,(哪些控制數(shù)碼管的位選,哪些控制數(shù)碼管的段選,哪些控制獨(dú)立按鍵等等),大家在自己制作過程中都要仔細(xì)考慮,我在這里就用我自己的硬件進(jìn)行介紹。
這是我的數(shù)碼管的電路圖:(將J12用杜邦線接到對應(yīng)的P0口,將J16用杜邦線接到對應(yīng)的P1口)
位選端我用P1口控制,段選端我用P0口控制。在圖中我已標(biāo)明秒的個(gè)位s,秒的十位s1,分的個(gè)位min,分的十位min1所對應(yīng)的數(shù)碼管。該數(shù)碼管是共陰極。
秒的十分位我用的是另一個(gè)共陽數(shù)碼管。我用P3口控制該數(shù)碼管的段選,其圖如下:
而獨(dú)立按鍵我用的P2^7控制“啟動(dòng)/暫停鍵”,P2^6控制復(fù)位鍵:電路圖如下:(將JP5通過杜邦線接到P3口)
硬件電路基本上就是這些,由于分和秒之間要用小數(shù)點(diǎn)分隔開,所以分的個(gè)位對應(yīng)的數(shù)碼管的段碼要用帶小數(shù)點(diǎn)的段碼,其余的數(shù)碼管則不用。
硬件方面是為看懂軟件程序做準(zhǔn)備,下面我們就開始我們的軟件程序方面吧!
3.軟件程序方面
#include
#define uchar unsigned char
#define uint unsigned int
#define PIN0 P0 //數(shù)碼管段選端
#define PIN1 P1 //數(shù)碼管位選端
#define PIN3 P3 //十分位數(shù)碼管的段碼端
sbit start=P2^7; //啟動(dòng)/暫停按鈕
sbit reset=P2^6; //復(fù)位按鈕
uint cnt=0; //1s的次數(shù)的計(jì)數(shù)值
uint num=0;
//進(jìn)入中斷次數(shù)的計(jì)數(shù)值(一次50ms)
uint num1=0; //0.1s的次數(shù)的計(jì)數(shù)值
uchar code table[]={0x3f,0x06,0x5b,
0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f};
//不帶小數(shù)點(diǎn)的共陰極數(shù)碼管段碼0-9uchar code table1[]={0xbf,0x86,
0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
//帶小數(shù)點(diǎn)的共陰極數(shù)碼管段碼0-9
char code table2[]={0xc0,0xf9,0xa4,
0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//不帶小數(shù)點(diǎn)的共陽極數(shù)碼管段碼0-9
void Display(uint w,uint a,uint b,uint c,uint d); //聲明顯示函數(shù)
void InitTimer0(); //聲明定時(shí)器初始化函數(shù)
void Delayms(uint x); //聲明延時(shí)函數(shù)
void main()
{
uint ms100=0; //秒的十分位
uint s,s1,min,min1,minu=0;
//依次是秒的個(gè)位,秒的十位,分的個(gè)位,分的十位
InitTimer0(); //初始化定時(shí)器
while(1) //進(jìn)入死循環(huán)
{
if(start==0) //檢測啟動(dòng)/暫停鍵是否按下
Delayms(20); //延時(shí)消抖
{
if(start==0) //消抖之后再次檢測
{
TR0=!TR0; //定時(shí)器的控制位取反
while(!start); //等待按鍵釋放
}
}
if(reset==0) //檢測復(fù)位鍵是否按下
{
Delayms(20); //延時(shí)消抖
if(reset==0) //消抖之后再次檢測
{
num=0;
num1=0; //進(jìn)入中斷次數(shù)值清0
ms100=0; //十分位的計(jì)數(shù)值清0
cnt=0; //秒的計(jì)數(shù)值清0
minu=0; //分的計(jì)數(shù)值清0
while(!reset); //等待按鍵釋放
}
}
if(num1>=2) //檢測是否達(dá)到0.1s,(兩個(gè)50ms)
{
num1=0; //進(jìn)入中斷次數(shù)值清零
ms100++; //秒的十分位的計(jì)數(shù)值加1
if(ms100>=10) //若秒的十分位計(jì)數(shù)值達(dá)到10
{
ms100=0; //清零秒的十分位的計(jì)數(shù)值
num1=0;//進(jìn)入中斷次數(shù)值置0
}
}
if(num>=20) //判斷計(jì)時(shí)時(shí)間是否達(dá)到1s(20個(gè)50ms)
{
num=0;
//進(jìn)入中斷次數(shù)值置0
cnt++; //秒的計(jì)數(shù)值加1
if(cnt>=60) //判斷是否達(dá)到60s
{
cnt=0; //若達(dá)到60s,將秒的計(jì)數(shù)值置零
minu++; //分的計(jì)數(shù)值加1
if(minu>=60) //判斷是否達(dá)到60分鐘,一般秒表是用不到60分的,這是為了程序的嚴(yán)謹(jǐn)性
{
TR0=!TR0; //達(dá)到60分鐘則關(guān)閉定時(shí)器并清零所有的計(jì)數(shù)值
num1=0;
num=0;
ms100=0;
cnt=0;
minu=0;
}
}
}
s=cnt%10; //從秒的計(jì)數(shù)值里面分離秒的個(gè)位
s1=cnt/10; //從秒的計(jì)數(shù)值里面分離秒的十位
min=minu%10; //從分的計(jì)數(shù)值里面分離分的個(gè)位
min1=minu/10; //從分的計(jì)數(shù)值里面分離分的十位
Display(ms100,s,s1,min,min1); //顯示這5個(gè)數(shù)
}
}
void InitTimer0() //定時(shí)器初始化函數(shù)
{
TMOD=0x01; //選擇定時(shí)器0的工作方式1
TH0=(65536-45872)/256; //裝初值(定時(shí)50ms),晶振11.0592MHz
TL0=(65536-45872)%256;
EA=1; //打開總中斷
ET0=1; //打開定時(shí)器中斷
TR0=0; //先不要啟動(dòng)定時(shí)器
}
void TIMER0() interrupt 1 //定時(shí)器中斷服務(wù)函數(shù)
{
TH0=(65536-45872)/256; //重裝初值
TL0=(65536-45872)%256;
num++; //讓進(jìn)入中斷次數(shù)值加1,用于判斷是否達(dá)到1s
num1++; //讓進(jìn)入中斷次數(shù)值加1,用于判斷是否達(dá)到0.1s
}
void Delayms(uint x) //延時(shí)xms
{
uint i,j;
for(i=x;i>0;i--)
for(j=110;j>0;j--);
}
void Display(uint w,uint a,uint b,uint c,uint d) //定義顯示函數(shù)
{
PIN3=table2[w]; //秒的十分位的段選
Delayms(5);
PIN0=table[a]; //秒的個(gè)位的段選
PIN1=0x7f; //秒的個(gè)位的位選
Delayms(5);
PIN0=table[b]; //秒的十位的段選
PIN1=0xbf; //秒的十位的位選
Delayms(5);
PIN0=table1[c]; //分的個(gè)位的段選(帶小數(shù)點(diǎn))
PIN1=0xdf; //分的個(gè)位的位選
Delayms(5);
PIN0=table[d]; //分的十位的段選
PIN1=0xef; //分的十位的位選
Delayms(5);
}
由于在打字時(shí)可能不小心會(huì)弄錯(cuò)一丁點(diǎn)程序,我都是一個(gè)字母一個(gè)字母手打的,所以請大家理解思路即可,根據(jù)這個(gè)思路自己去寫,這樣得到的知識(shí)才是屬于自己的。畢竟 “讀別人的故事,悟自己的人生”。
下面來幾張實(shí)物圖給大家看一下我這個(gè)秒表的實(shí)際效果。
編譯結(jié)果:
燒寫進(jìn)單片機(jī)的效果:(沒有按啟動(dòng)按鈕之前)
按了啟動(dòng)按鈕之后,秒表將會(huì)自動(dòng)開始計(jì)時(shí),由于無法演示動(dòng)態(tài)過程,只能在中間按下暫停鍵給大家看看效果:
按下復(fù)位鍵后:
由于我用的是一個(gè)比較完整的51開發(fā)板,所以里面有很多部分,大家只需要注意觀察數(shù)碼管部分就好。
下面再來一個(gè)完整的圖:
結(jié)束語
至此,利用51單片機(jī)制作的秒表就已經(jīng)完成了!若是文章中有什么不懂的地方或者是自己在制作過程中遇到什么問題都可以聯(lián)系我,或者給我留言,我一定竭盡全力幫助大家!我們一起學(xué)習(xí),一起進(jìn)步!希望大家都能在看完這篇文章后自己動(dòng)手成功制作出一個(gè)秒表,祝愿大家學(xué)好單片機(jī)!當(dāng)然,由于自身水平有限,文章中肯定會(huì)存在錯(cuò)誤或者是考慮不周的的地方,懇請不吝賜教!謝謝大家!
(PS:我寫了一整天了,反復(fù)的看,反復(fù)的改,現(xiàn)在頭昏眼花,但是只要能對大家有所幫助,一切都是值得的。)
評(píng)論