日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

51单片机初学3-从零开始制作一款电子时钟

發(fā)布時(shí)間:2023/12/14 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 51单片机初学3-从零开始制作一款电子时钟 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

今天我們用STC89C52制作一款簡單的單片機(jī)作品:電子時(shí)鐘。除了基本的走時(shí)功能,還能手動(dòng)調(diào)節(jié)時(shí)間,設(shè)置鬧鐘,待機(jī)喚醒。

本文包括硬件與軟件設(shè)計(jì)。

我認(rèn)為電子時(shí)鐘需要考慮的兩點(diǎn):一是計(jì)時(shí)準(zhǔn)確,誤差小;二是省電,使其能在移動(dòng)電源供電下超長待機(jī)。

硬件設(shè)計(jì):

首先我們需要構(gòu)思好系統(tǒng)框架:

基本的時(shí)鐘電路與復(fù)位電路不用多說,我們用八位數(shù)碼管來作為時(shí)間顯示方式(顯示樣式為:12-00-00),其中P0口控制其段,P2口控制其位;以八個(gè)點(diǎn)動(dòng)按鈕作為鍵盤輸入;蜂鳴器、LED分別作為提示音和指示燈。

接下來就可以設(shè)計(jì)原理圖:

可以看到數(shù)碼管的接線較復(fù)雜,其原理暫不多說(可參考文章51單片機(jī)初學(xué)2-數(shù)碼管動(dòng)態(tài)掃描_#liufenges#的博客-CSDN博客_數(shù)碼管動(dòng)態(tài)掃描),可以看到兩個(gè)數(shù)碼管的1、2、3、4、5、7、10、11是分別連起來的,然后引出來連接到P0口;兩個(gè)數(shù)碼管的6、8、9、12共8個(gè)腳與P2口連接。

需要注意,數(shù)碼管位控制與P2口之間加入了一個(gè)鎖存器74HC373,其作用是在待機(jī)時(shí)方便關(guān)閉數(shù)碼管。其11腳是地址鎖存端口,將其接高電平時(shí),鎖存器為透明模式,輸入與輸出完全相同,這里我直接接入VCC;1腳為輸出鎖存,高電平時(shí)無輸出,低電平才有輸出,這里我們用P3.6來控制其輸出。下圖是74HC373引腳圖及其功能。

為了簡化電路,蜂鳴器與LED共用一個(gè)I/O口;

單片機(jī)的數(shù)據(jù)串口引出來接到排針上,方便程序燒錄。

需要注意,為了防止數(shù)碼管燒壞,在P0口應(yīng)串聯(lián)470歐姆的限流電阻(原理圖中未畫出)。

所以得到所需材料:

STC89C52芯片(1塊),40P底座(1只),面包板(2片),3461BS數(shù)碼管(2只),點(diǎn)動(dòng)按鈕(9只),LED燈(1只),74HC373鎖存芯片(1片),10K 9P排阻(4只),470歐電阻(15只),12M晶振(1只),30pF瓷片電容(兩個(gè)),排針(15針),led燈,有源蜂鳴器一只(關(guān)于有源與無源蜂鳴器的區(qū)別可在網(wǎng)上查閱),PNP型三極管一只。

最后我們按照原理圖焊接元件。以下為成品圖片可供參考

為了使作品看起來簡潔,我們采用雙主板設(shè)計(jì),上層為數(shù)碼管、鍵盤,下層為單片機(jī)最小系統(tǒng),兩層主板使用小螺栓固定。

由于定做PCB時(shí)間較長,所以我使用洞洞板來制作電路板(若是不擅長電子焊接,最好是制作PCB),可以看到飛線很多,兩塊主板之間有較多的連接線(為了防止焊點(diǎn)受力而脫落,可以將線繞在洞洞之間)。注意焊接單片機(jī)底座時(shí),不要把單片機(jī)裝在底座上,以免焊接時(shí)燒壞單片機(jī)芯片;同樣,焊接晶振時(shí),要盡可能快,避免長時(shí)間給晶振加熱而損壞晶振;安插單片機(jī)芯片時(shí)要注意對齊引腳,以免折斷或者接觸不良,插好后可以用萬用表測量一遍所有引腳是否與底座導(dǎo)通;排阻公共端判斷方法:在排阻最左邊或者最右邊會(huì)有個(gè)白色小點(diǎn),有白點(diǎn)的一端為公共端;點(diǎn)動(dòng)按鈕有四個(gè)引腳(一組常開觸點(diǎn),一組常閉觸點(diǎn)),可按照原理圖所示將兩個(gè)引腳接入。

單片機(jī)程序開發(fā)常用 keil軟件(這里我們以Keil uVision3為例):

首先新建工程(點(diǎn)擊project→new→選擇一個(gè)文件地址后保存),然后選擇CPU型號。

STC89C52是完全兼容AT89C52的(因?yàn)镾TC是國產(chǎn)芯片,keil中沒有STC芯片,只能用其他芯片代替),所以我們選擇AT89C52即可(首先點(diǎn)Atmel,下拉之后,可以找到AT89C52)。

之后會(huì)彈出詢問窗口:Copy standard 8051 Startup code to Project Folder and Add File to project?(是否復(fù)制8051啟動(dòng)編碼到工程文件夾?),點(diǎn)擊確認(rèn)即可。若點(diǎn)擊取消,在創(chuàng)建文件時(shí)也會(huì)自動(dòng)添加。

可以看到創(chuàng)建了一個(gè)Target1的工程文件,下拉時(shí)候還有一個(gè)Source Group1的文件夾。這個(gè)文件夾里有個(gè)STARTUP.A51的文件,這就是剛才復(fù)制的8051啟動(dòng)編碼,里面包含51單片機(jī)的寄存器、I/O口等地址的分配,這些都是軟件自動(dòng)生成的,一般不需要去更改。

之后添加C程序文件:File→new。然后會(huì)創(chuàng)建一個(gè)text1的空白文件。然后我們點(diǎn)擊保存(或者Ctrl+S),選擇保存地址(保存在一個(gè)容易找到的地方,后面需要用到),輸入文件名,注意文件名要加后綴.c保存為C文件。如果是用匯編語言寫程序,則加后綴.ASM。

接著右擊Source Group1,在菜單中找到Add Files To Group ‘Source Group1’點(diǎn)擊(這個(gè)選項(xiàng)在菜單中有加粗顯示)。然后將剛才的c程序文件添加至工程,關(guān)閉對話框。可以看到Source Group1下多了之前的C文件。

然后就可以寫程序了。

程序編寫:

定義單片機(jī)C程序的頭文件#include<reg51.h>

為了方便后面寫程序時(shí),搞混I/O口,我們可以先定義一些功能引腳。例如蜂鳴器,我們查看原理圖可以看到,蜂鳴器是由P3.1控制的,所以我們定義P3.1為蜂鳴器:sbit fm=P3^1;(‘sbit’是單片機(jī)用于定義引腳的關(guān)鍵字,在C語言中是沒有這個(gè)關(guān)鍵字的;P3.1之間的點(diǎn)在程序中要用‘^’表示),這樣,在之后的程序中,如果我們要用到蜂鳴器,只要讓fm等于0或者等于1,就可以控制蜂鳴器的工作了,而不再需要使用P3^1了。

然后我們還要對數(shù)碼管進(jìn)行編碼,數(shù)碼管需要顯示的字符較多,我們可以使用一個(gè)數(shù)組來定義:

char codeduan[]={0xc0,0xcf,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x7f,0xbf,0xff,0x89};

(char數(shù)據(jù)類型:在單片機(jī)中,char數(shù)據(jù)類型所占空間最少,只有1個(gè)字節(jié)(八位二進(jìn)制),但他的范圍為?-128~127?(signed有符號型),unsigned為0~255。所以如果該變量數(shù)據(jù)范圍不大,一般用char類型,這樣做可以節(jié)省單片機(jī)空間)

接著定義全局變量 sec,min,hour.之所以定義為全局變量,是為了讓這三個(gè)量所有函數(shù)中都是能使用的。

在本作品中,延時(shí)函數(shù)必不可少,比如數(shù)碼管掃描,走時(shí)都需要延時(shí)函數(shù)(常用的方法還有定時(shí)器中斷)。關(guān)于延時(shí)函數(shù)的計(jì)算問題可自行百度,為了方便,我們可以直接使用STC-IPS軟件自動(dòng)生成,只要輸入需要延時(shí)的時(shí)間,軟件可以自動(dòng)生成一個(gè)延時(shí)函數(shù),直接復(fù)制粘貼就可以(最小時(shí)間為1us)。? ?由于我們需要多種時(shí)間的延時(shí),所以我們可以先把需要的延時(shí)函數(shù)先寫在前面,方便之后的調(diào)用。

定義好需要的變量,我們就可以開始寫主函數(shù)了。這里我們把數(shù)碼管掃描與計(jì)時(shí)作為主程序,數(shù)碼管掃描與計(jì)時(shí)同時(shí)進(jìn)行。

接著編寫調(diào)時(shí)子函數(shù),鬧鐘子函數(shù)。在主程序插入判定條件,以此調(diào)用子函數(shù)。

為了添加更多花樣,還添加了一個(gè)開機(jī)‘動(dòng)畫’? motos();(詳情看后面的程序)

需要注意的是,子函數(shù)應(yīng)置于主函數(shù)前面,否則編譯時(shí)會(huì)提示 未定義子函數(shù) 。

再說說鍵盤的處理。鍵盤排列與鍵位設(shè)置如下。

K1、K2控制光標(biāo)的左右移動(dòng),K3、K4控制數(shù)字加減,K5為確定鍵,K6為調(diào)時(shí)(長按4秒進(jìn)入),K7設(shè)置鬧鐘,K8待機(jī)模式。

其他細(xì)節(jié)暫不多說,看程序即可。

完整程序如下:

/*電子時(shí)鐘程序:基本電子時(shí)鐘功能,能調(diào)節(jié)時(shí)間,能設(shè)置鬧鐘(已刪減),有待機(jī)模式(已刪減)*/ /*LED數(shù)碼管顯示器設(shè)定; P0.0---P0.7段控線,接LED的顯示段a,b,c,d,e,f,g,dp. P2.0---P2.7位控線,從左至右************鍵位設(shè)置*******************W3(+) W1(光標(biāo)左移) W5(確認(rèn)) W2(光標(biāo)右移) W6(調(diào)時(shí)) W7(鬧鈴) W8 (喚醒) W4(-) **************************************/ #include<reg51.h> #include<intrins.h> //定義單片機(jī)的頭文件 sbit fm=P3^1; //定義單片機(jī)蜂鳴器 sbit plays=P3^6; //定義73HC373輸出控制位// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // char codeduan[]={0xc0,0xcf,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x7f,0xbf,0xff,0x89}; //數(shù)碼管段編碼// 0 1 2 3 4 5 6 7 8 9 dp - 空 H // char codebite[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x00}; //數(shù)碼管位編碼 char sec=0,min=0,hour=0; void Delay1ms() //@12.000MHz,1ms延時(shí)函數(shù),用于數(shù)碼管動(dòng)態(tài)輸出 {unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i); } void Delay50ms() //@12.000MHz,用于蜂鳴器提示音,30ms {unsigned char i, j, k;i = 2;j = 95;k = 43;do{ do{ while (--k);} while (--j);} while (--i); } void adjust() //時(shí)間調(diào)整模式子程序 {int H=0,cursor=3;char ks,twi,temps[8],K[8];temps[2]=11;temps[5]=11;fm=0;Delay50ms();fm=1; //蜂鳴器響一聲提示進(jìn)入時(shí)間調(diào)整模式while(P1!=0xef) //如果沒有按下K8,則執(zhí)行循環(huán){if(H<180) {twi=0;} //進(jìn)入調(diào)整模式后,光標(biāo)閃爍if(H>180) {twi=1;} if(H==360) {H=0;} for(ks=0;ks<8;ks++){if(cursor==1&&twi==0){temps[0]=12;temps[1]=12;}else{temps[0]=sec%10; //求余計(jì)算秒個(gè)位temps[1]=sec/10;} //求商計(jì)算秒十位if(cursor==2&&twi==0){temps[3]=12;temps[4]=12;}else{temps[3]=min%10; //求余計(jì)算分個(gè)位temps[4]=min/10;} //求商計(jì)算分十位if(cursor==3&&twi==0){temps[6]=12;temps[7]=12;}else {temps[6]=hour%10; //求余計(jì)算時(shí)個(gè)位temps[7]=hour/10;} //求余計(jì)算時(shí)十位 P2=codebite[ks]; //數(shù)碼管輸出選位,從第0位開始//P0=codeduan[temps[ks]]; //輸出段,輸出要顯示的數(shù)字//Delay1ms(); //延時(shí)1ms,防止數(shù)碼管串碼H++;P0=codeduan[12];}if(P1==0xfe) /*按下‘左’鍵,將光標(biāo)左移 */{ K[1]=1;}if(K[1]==1&&P1!=0xfe){K[1]=0; cursor++;}if(P1==0xfd) /*按下‘右’鍵,將光標(biāo)右移 */{ K[2]=1;}if(K[2]==1&&P1!=0xfd){K[2]=0; cursor--;}if(cursor<1) { cursor=3;} if(cursor>3) { cursor=1;}if(P1==0xfb) /*按下‘上’鍵,將數(shù)字加一 */{ K[3]=1;}if(K[3]==1&&P1!=0xfb){ K[3]=0; switch(cursor){case 1:sec++;break;case 2:min++;break;case 3:hour++;break;default:break;}} if(P1==0xf7) /*按下‘下’鍵,將數(shù)字減一 */{ K[4]=1;}if(K[4]==1&&P1!=0xf7){ K[4]=0; switch(cursor){case 1:sec--;break;case 2:min--;break;case 3:hour--;break;default:break;}}if(sec>59) {sec=0; } /*對時(shí),分,秒范圍進(jìn)行限制 */if(sec<0) {sec=59;}if(min>59) {min=0; }if(min<0) {min=59;} if(hour>23){hour=0; }if(hour<0) {hour=23;} }return; //如果檢測到K8按下,則跳出循環(huán),返回主函數(shù) } /*開機(jī)動(dòng)畫子程序*/ void motos() {int mot=0;char m;char motobit[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};char motoduan[8]={0xcf,0xa4,0xc0,0xa4,0x8e,0xc7,0xbf,0xbf}; /*編碼顯示“--LF2021” */while(mot<1800){for(m=0;m<8;m++){ P2=motobit[m]; //數(shù)碼管輸出選位,從第0位開始//P0=motoduan[m]; //輸出段,輸出要顯示的數(shù)字//Delay1ms(); //延時(shí)1ms,防止數(shù)碼管串碼P0=codeduan[12];mot++;}}fm=0;Delay50ms();fm=1;Delay50ms();fm=0;Delay50ms();fm=1;return; } /*主程序,包含數(shù)碼管顯示以及計(jì)時(shí)*/ void main(){ int num=0,ks=0;char k,temp[8],moto=1;plays=0; /*打開鎖存器74HC373使能端 */motos(); /*調(diào)用開機(jī)動(dòng)畫 */temp[2]=11;temp[5]=11;while(1){for(k=0;k<8;k++){ temp[0]=sec%10; //求余計(jì)算秒個(gè)位temp[1]=sec/10; //求商計(jì)算秒十位temp[3]=min%10; //求余計(jì)算分個(gè)位temp[4]=min/10; //求商計(jì)算分十位temp[6]=hour%10; //求余計(jì)算時(shí)個(gè)位temp[7]=hour/10; //求商計(jì)算時(shí)十位P2=codebite[k]; //數(shù)碼管輸出選位,從第0位開始//P0=codeduan[temp[k]]; //輸出段,輸出要顯示的數(shù)字//num++;Delay1ms(); //延時(shí)1ms,防止數(shù)碼管串碼P0=codeduan[12];if(P1==0xdf) //每次循環(huán)判斷是否按下K1鍵{ if(num%10==0&&P1==0xdf) //每10次循環(huán),10ms,判斷K1是否仍然按下{ ks++; //如果每10次循環(huán)K1均按下,ks則自加一次if(ks==300) //如果KS記到300,表明k1已經(jīng)連續(xù)按下4s,則進(jìn)入時(shí)間調(diào)整模式,并將Ks清零{ks=0;adjust();} } //如果K1仍然按下,則將KS+1}else{ks=0;} //如果K1不再按下,則清零ks if(num==865) //經(jīng)過與電腦時(shí)鐘對比,找到最合適的值,以下為計(jì)時(shí)程序{sec++;num=0; if (sec==60){sec=0;min++;if (min==60){min=0;hour++;if (hour==24){hour=0;}}} }}} }

程序燒錄:

由于單片機(jī)不能直接運(yùn)行C語言或者匯編語言程序,必須將程序編譯成hex文件才能寫入單片機(jī)。寫好程序后,若是首次編譯,通常不會(huì)自動(dòng)生成hex文件,需要進(jìn)行如下設(shè)置:點(diǎn)擊圖中1處按鈕“Option for Target”,在彈出的窗口中點(diǎn)擊“Output”,然后勾選“Create HEX file”。點(diǎn)擊確定后,點(diǎn)擊序號4處的編譯按鈕,即可編譯程序。

如果編譯無誤,則會(huì)顯示0錯(cuò)誤,0警告。并提示‘creating hex file from“#工程名#”’,說明HEX文件已經(jīng)創(chuàng)建成功。

篇幅有限,這里暫不展示proteus仿真過程,可自行按照原理圖構(gòu)建電路并驗(yàn)證程序。

這里直接將程序?qū)懭雴纹瑱C(jī)產(chǎn)品中。

我們需要用到軟件STC—IPS,這是專門用于STC系列單片機(jī)的程序燒錄軟件,也附帶一些輔助功能,比如定時(shí)器函數(shù)、上文提到的延時(shí)函數(shù)自動(dòng)生成。下圖是其窗口界面。

燒錄之前,我們需要使用USB-TTL將電腦與單片機(jī)連接。連接方式如下圖所示。

我們先要選擇對應(yīng)的單片機(jī)型號,連接單片機(jī)之后,若提示“串口打開失敗”,則點(diǎn)擊“掃描”,電腦會(huì)自動(dòng)找到對應(yīng)的串口。

接著,我們點(diǎn)擊“打開程序文件”,選擇剛才生成的hex文件,然后點(diǎn)擊“下載/編程”即可將程序下載到單片機(jī)。若點(diǎn)擊下載之后無反應(yīng),則關(guān)閉單片機(jī)電源重新打開,程序便可寫入單片機(jī)。

這樣,整個(gè)作品就算完成了。以下是其寫入程序后的成品圖。

總結(jié):

?功耗計(jì)算(暫時(shí)找不到標(biāo)準(zhǔn)的5V、3V電源):

充電寶供電:電壓5.15V,電流30~40mA,功耗5.15X(30~40)=154.5mW~206mW;

三節(jié)鎳氫電池:電壓3.91V,電流20mA左右,功耗3.91X20=78.2mW。

總的來說,功耗還是偏高,經(jīng)過測試,主要的功率都消耗在數(shù)碼管。單片機(jī)的功耗不超過10mW,所以待機(jī)時(shí)將數(shù)碼管關(guān)閉能有效減小功耗。

?誤差問題:本時(shí)鐘經(jīng)過實(shí)測,還是有可見的誤差。

可調(diào)的誤差:運(yùn)行程序需要占用很多機(jī)器時(shí)間,總時(shí)間=延時(shí)函數(shù)的時(shí)間+其他程序執(zhí)行時(shí)間。而其他程序執(zhí)行時(shí)間是很難計(jì)算的,只能經(jīng)過對比調(diào)試來壓縮延時(shí)函數(shù)的時(shí)間。

欲盡可能減小誤差,需要與標(biāo)準(zhǔn)時(shí)鐘(電腦或者手機(jī)的網(wǎng)絡(luò)時(shí)間)進(jìn)行對比,計(jì)算出誤差,然后調(diào)節(jié)延時(shí)函數(shù)的時(shí)間。

比如:我們延時(shí)函數(shù)剛開始設(shè)置為1000ms,經(jīng)過與標(biāo)準(zhǔn)時(shí)間對比1小時(shí)發(fā)現(xiàn),我的時(shí)鐘慢了1S,說明我時(shí)鐘的誤差為1/3600=0.0002778s=0.2778ms=277.8us(為了更精確計(jì)算出誤差,我們可以提高對比時(shí)間,時(shí)間越長,誤差越好計(jì)算)。這樣,我們就可以把延時(shí)函數(shù)的時(shí)間減小278us,那延時(shí)函數(shù)就要設(shè)置為1000000-278us=999722us.為了調(diào)節(jié)的方便,我們可以使用兩級級延時(shí),一級延時(shí)函數(shù)以ms為單位,二級延時(shí)函數(shù)以us為單位,這樣就很方便調(diào)試。

不可調(diào)的誤差是晶振的溫漂問題,晶振的震蕩頻率是按照25℃環(huán)境制作的,如果溫度偏大或者偏小,其震蕩頻率都會(huì)有略微變化,進(jìn)而影響CPU執(zhí)行速度,造成走時(shí)不準(zhǔn)。

更為先進(jìn)的辦法是使用wifi模塊esp8266從網(wǎng)絡(luò)獲取時(shí)間,再將時(shí)間送給單片機(jī),這樣,走時(shí)不準(zhǔn)的問題就能得到徹底的解決。還能使用LCD1602或者LCD12864作為顯示器,這樣可以顯示更多的內(nèi)容,就可以加入更多的花樣(以后會(huì)專門介紹這兩款顯示器)。

關(guān)于esp8266的用法,稍微復(fù)雜些,以后再做介紹。

本文僅供參考,如有不足,還請指出。

總結(jié)

以上是生活随笔為你收集整理的51单片机初学3-从零开始制作一款电子时钟的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。