熱門: 51單片機(jī) | 24小時(shí)必答區(qū) | 單片機(jī)教程 | 單片機(jī)DIY制作 | STM32 | Cortex M3 | 模數(shù)電子 | 電子DIY制作 | 音響/功放 | 拆機(jī)樂(lè)園 | Arduino | 嵌入式OS | 程序設(shè)計(jì)
|
發(fā)布時(shí)間: 2017-9-13 22:52
正文摘要:這是我制作的基于定時(shí)器制作的時(shí)鐘,但是精度有問(wèn)題,我覺(jué)得是程序的延時(shí)delay導(dǎo)致的,請(qǐng)大神幫忙改一下,要分秒不差最好;謝謝了。代碼如下: #include<reg52.h> sbit A1=P2^2; sbit B1=P2^3; sbit C1= ... |
|
作為學(xué)習(xí)例子去折騰一下是沒(méi)問(wèn)題的,但無(wú)大的實(shí)際應(yīng)用價(jià)值。 在有RTC的MCU中,可以用RTC來(lái)做時(shí)鐘; 在無(wú)RTC的MCU中,外擴(kuò)專用時(shí)鐘芯片。 |
wulin 發(fā)表于 2017-9-15 07:10 難得的學(xué)習(xí)資料,贊贊贊 |
| 你實(shí)際測(cè)試一下,然后看看定時(shí)器需要多少時(shí)間要修正一下誤差,這樣一點(diǎn)點(diǎn)的可以不斷修正到你所需要的精度 |
| http://m.raoushi.com/bbs/dpj-86306-1.html 可以看我的貼子,不用時(shí)鐘芯片,日誤差可以做到1秒內(nèi) |
|
本帖最后由 zl2168 于 2017-9-20 20:38 編輯 給你介紹應(yīng)用上述方法的案例。 實(shí)例94 模擬電子鐘(由80C51定時(shí)器產(chǎn)生秒時(shí)基) 先Proteus仿真一下,確認(rèn)有效。
實(shí)例94 模擬電子鐘(由80C51定時(shí)器產(chǎn)生秒時(shí)基).rar
(43.05 KB, 下載次數(shù): 10)
以上摘自張志良編著《80C51單片機(jī)仿真設(shè)計(jì)實(shí)例教程——基于Keil C和Proteus》清華大學(xué)出版社ISBN 978-7-302-41682-1,內(nèi)有常用的單片機(jī)應(yīng)用100案例,用于仿真實(shí)驗(yàn)操作,電路與程序真實(shí)可靠可信可行。書(shū)中電路和程序設(shè)計(jì)有詳細(xì)說(shuō)明,程序語(yǔ)句條條有注解。仿真電路和Hex文件能在清華出版社網(wǎng)站免費(fèi)下載,程序源代碼只能到書(shū)上看了。到圖書(shū)館借,或到新華書(shū)店翻閱,或到網(wǎng)上書(shū)店打折購(gòu)買。 |
| 單片機(jī)時(shí)鐘可分為2種:一是應(yīng)用時(shí)鐘芯片;二是用機(jī)內(nèi)時(shí)鐘。應(yīng)用時(shí)鐘芯片肯定比機(jī)內(nèi)時(shí)鐘精準(zhǔn),它取決于時(shí)鐘芯片的晶振頻率是否精準(zhǔn)。應(yīng)用機(jī)內(nèi)時(shí)鐘一般是學(xué)生實(shí)驗(yàn),讓學(xué)生學(xué)習(xí)定時(shí)器中斷、計(jì)時(shí)計(jì)次,無(wú)實(shí)用意義。用機(jī)內(nèi)時(shí)鐘達(dá)到比較精準(zhǔn),必須用中斷方式2,自動(dòng)恢復(fù)定時(shí)初值,用其他方式或延時(shí)肯定不好。用機(jī)內(nèi)時(shí)鐘時(shí),晶振最好用12MHz或6MHz,便于計(jì)算和計(jì)時(shí),機(jī)器周期分別為1us和2us,定時(shí)可取250us和500us,在中斷內(nèi)計(jì)次4000和2000就是1s,可建立1s標(biāo)志,在中斷外更新顯示和處理。這時(shí)這種方式的相對(duì)精準(zhǔn)度取決于晶振頻率的精準(zhǔn)度。 |
GOD丿小男孩 發(fā)表于 2017-9-14 09:06 //用查詢法計(jì)時(shí)的高精度51單片機(jī)時(shí)鐘程序 //K1鍵調(diào)整小時(shí)+,K2鍵調(diào)整分鐘+,K3鍵秒清0,初始狀態(tài)12:00:00 //定時(shí)器初始化程序根據(jù)晶振頻率選擇,走時(shí)精度可以控制在日誤差5秒 //主程序循環(huán)一次必須小于100us,子程序全部要用時(shí)間片輪詢編寫(xiě),必須用計(jì)數(shù)代替軟件延時(shí) //數(shù)碼管采用時(shí)分動(dòng)態(tài)顯示,約1ms顯示1位,按實(shí)際驅(qū)動(dòng)IC選擇顯示程序 #include <reg51.h> #define uint unsigned int #define uchar unsigned char sbit K1 = P3^5; //小時(shí)+ sbit K2 = P3^6; //分鐘+ sbit K3 = P3^7; //秒清0 //sbit dula=P2^6; //段選 //sbit wela=P2^7; //位選 sbit A1=P2^2; //位A sbit B1=P2^3; //位B sbit C1=P2^4; //位C uchar code table[]={ //0~F數(shù)組 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; uint Cnt100us; //定義100微秒變量 uchar hour=12,min=0,sec=0;//定義時(shí)、分、秒變量 /************************************** 定時(shí)器0初始化程序 100微秒@12.000MHz **************************************/ /* void Timer0Init() //100微秒@12.000MHz { TMOD = 0x02; //設(shè)置自動(dòng)重載模式 TL0 = 0x9C; //設(shè)置定時(shí)初值 TH0 = 0x9C; //設(shè)置定時(shí)重載值 TF0 = 0; //清除TF0標(biāo)志 TR0 = 1; //定時(shí)器0開(kāi)始計(jì)時(shí) } */ /*************************************** 定時(shí)器0初始化程序 100微秒@11.0592MHz ***************************************/ void Timer0Init() //100微秒@11.0592MHz { TMOD = 0x02; //設(shè)置自動(dòng)重載模式 TL0 = 0xA4; //設(shè)置定時(shí)初值 TH0 = 0xA4; //設(shè)置定時(shí)重載值 TF0 = 0; //清除TF0標(biāo)志 TR0 = 1; //定時(shí)器0開(kāi)始計(jì)時(shí) } /************************************ 計(jì)時(shí)子程序 ************************************/ void Time() { if(TF0==1) //如果查詢定時(shí)器溢出標(biāo)志TF0為1 { //定時(shí)器溢出周期100us TF0=0; //定時(shí)器溢出標(biāo)志TF0清0 Cnt100us++; //時(shí)間變量Cnt100us自+1 if(Cnt100us>=10000) //在此增減可以按萬(wàn)分之一秒調(diào)整精度 { Cnt100us=0; //變量Cnt100us清0 sec++; //秒自+1 if(sec>=60) //如果秒>=60 { sec=0; //秒清0 min++; //分自+1 if(min>=60) //分>=60 { min=0; //分清0 hour++; //小時(shí)自+1 if(hour>=24) //小時(shí)>=24 hour=0; //小時(shí)清0 } } } } } /************************* 按鍵掃描程序 *************************/ void key_scan() { static bit key1_sign,key2_sign,key3_sign; //按鍵自鎖標(biāo)志變量 static uchar count1,count2,count3=0; //消抖計(jì)數(shù)變量 if(K1==1) //檢測(cè)按鍵1沒(méi)有按下 { key1_sign=0; //按鍵1自鎖標(biāo)志清0 count1=0; //消抖計(jì)數(shù)1清0 } else //按鍵1按下 { count1++; //消抖計(jì)數(shù)1自+1 if(count1>=200) //200次檢測(cè)按鍵如果為0 { count1=200; //防止溢出 if(key1_sign==0) //按鍵1自鎖標(biāo)志為0 { key1_sign=1; //按鍵1自鎖標(biāo)志置1 hour++; //小時(shí)自+1 if(hour>=24) //小時(shí)>=24 hour=0; //小時(shí)清0 } } } if(K2==1) //檢測(cè)按鍵2沒(méi)有按下 { key2_sign=0; //按鍵2自鎖標(biāo)志清0 count2=0; //消抖計(jì)數(shù)2清0 } else //按鍵2按下 { count2++; //消抖計(jì)數(shù)2自+1 if(count2>=200) //200次檢測(cè)按鍵如果為0 { count2=200; //防止溢出 if(key2_sign==0) //按鍵2自鎖標(biāo)志為0 { key2_sign=1; //按鍵2自鎖標(biāo)志置1 min++; //分自+1 if(min>=60) //分>=60 min=0; //分清0 } } } if(K3==1) //檢測(cè)按鍵3沒(méi)有按下 { key3_sign=0; //按鍵3自鎖標(biāo)志清0 count3=0; //消抖計(jì)數(shù)2清0 } else //按鍵3按下 { count3++; //消抖計(jì)數(shù)3自+1 if(count3>=200) //200次檢測(cè)按鍵如果為0 { count3=200; //防止溢出 if(key3_sign==0) //按鍵3自鎖標(biāo)志為0 { key3_sign=1; //按鍵3自鎖標(biāo)志置1 Cnt100us= 0; //100微秒變量清0 sec = 0; //秒清0 } } } } /******************************** 顯示程序 573+573 ********************************/ /* void display() { static uchar xx=0; //時(shí)分顯示變量 static uchar yy=0; //計(jì)數(shù)延時(shí)變量 yy++; if(yy>=60) //1~255可調(diào),數(shù)碼管閃爍可減小,有鬼影可加大 { yy=0; switch(xx) { case 0: dula=0; P0=table[hour/10]; //顯示時(shí)十位段碼 dula=1; dula=0; wela=0; P0=0x7e; //顯示時(shí)十位位碼 wela=1; wela=0; xx=1; break; case 1: dula=0; P0=table[hour%10]; //顯示時(shí)個(gè)位段碼 dula=1; dula=0; wela=0; P0=0x7d; //顯示時(shí)個(gè)位位碼 wela=1; wela=0; xx=2; break; case 2: P0=table[min/10]; //顯示分十位段碼 dula=1; dula=0; P0=0x7b; //顯示時(shí)十位位碼 wela=1; wela=0; xx=3; break; case 3: P0=table[min%10]; //顯示分個(gè)位段碼 dula=1; dula=0; P0=0x77; //顯示分個(gè)位位碼 wela=1; wela=0; xx=4; break; case 4: P0=table[sec/10]; //顯示秒十位段碼 dula=1; dula=0; P0=0xef; //顯示秒十位位碼 wela=1; wela=0; xx=5; break; case 5: P0=table[sec%10]; //顯示秒個(gè)位段碼 dula=1; dula=0; P0=0xdf; //顯示秒個(gè)位位碼 wela=1; wela=0; xx=0; break; } } }*/ /******************************** 顯示程序 573+138 ********************************/ void display() { static uchar xx=0; //時(shí)分顯示變量 static uchar yy=0; //計(jì)數(shù)延時(shí)變量 yy++; if(yy>=60) //1~255可調(diào),數(shù)碼管閃爍可減小,有鬼影可加大 { yy=0; switch(xx) { case 0: P0=0x00; A1=1;B1=0;C1=1; //顯示時(shí)十位位碼 P0=table[hour/10]; //顯示時(shí)十位段碼 xx=1; break; case 1: P0=0x00; A1=0;B1=0;C1=1; //顯示時(shí)個(gè)位位碼 P0=table[hour%10]; //顯示時(shí)個(gè)位段碼 xx=2; break; case 2: P0=0x00; A1=1;B1=1;C1=0; //顯示時(shí)十位位碼 P0=table[min/10]; //顯示分十位段碼 xx=3; break; case 3: P0=0x00; A1=0;B1=1;C1=0; //顯示分個(gè)位位碼 P0=table[min%10]; //顯示分個(gè)位段碼 xx=4; break; case 4: P0=0x00; A1=1;B1=0;C1=0; //顯示秒十位位碼 P0=table[sec/10]; //顯示秒十位段碼 xx=5; break; case 5: P0=0x00; A1=0;B1=0;C1=0; //顯示秒個(gè)位位碼 P0=table[sec%10]; //顯示秒個(gè)位段碼 xx=0; break; } } } /******************************** 主程序 ********************************/ void main(void) { Timer0Init(); //初始化定時(shí)器 while(1) { key_scan();//按鍵掃描 Time(); //計(jì)時(shí) display(); //顯示 } } |
mengzhixinheng 發(fā)表于 2017-9-14 10:27 好的,謝謝了!我去網(wǎng)上找一下資料學(xué)習(xí)一下 |
GOD丿小男孩 發(fā)表于 2017-9-14 08:29 8位自動(dòng)重裝定時(shí)計(jì)數(shù)器的用法不懂你就看下資料,順便把定時(shí)器搞懂。隨便一份好的教程都會(huì)講的很詳細(xì),甚至你下一份STC的手冊(cè),上面還有很好的例程。這個(gè)比我給你幾句程序有用啊!如果覺(jué)得看完整的手冊(cè)煩;你就學(xué)哪里看哪里嘛 |
GOD丿小男孩 發(fā)表于 2017-9-14 08:29 聲明一個(gè)標(biāo)志位flag放入中斷中,每當(dāng)定時(shí)器溢出進(jìn)入中斷flag=1;已flag=1為條件寫(xiě)一個(gè)走時(shí)間的函數(shù),在內(nèi)部清零flag。溢出時(shí)間最大是多少是由定時(shí)器位數(shù)和晶振決定的。你用16位定時(shí)器0,12M晶振,如果非1T指令的51單片機(jī);那么執(zhí)行一條單周期指令是1us,這也是定時(shí)器的最小溢出時(shí)間。那么16位定時(shí)器計(jì)數(shù)65536us,如果你非要用他做時(shí)鐘,你就設(shè)置個(gè)50ms溢出多好啊1s進(jìn)入20次中斷,這樣與主程序的相互影響也小了。 |
| 沒(méi)有其他中斷耽誤時(shí)間嗎? |
|
所以要做一個(gè)精準(zhǔn)時(shí)鐘,是要花一些功夫的,花在如何保證一次中斷時(shí)間的高度準(zhǔn)確,做到一個(gè)晶振脈沖都不誤差是最高境界,普通51:6個(gè)晶振脈沖是可控最高境界。 如:樓主設(shè)計(jì)的中斷時(shí)間是1ms,一天中斷86400000次,如果每次誤差6個(gè)脈沖(半個(gè)機(jī)器周期),則日差:518.4M個(gè)脈沖。 對(duì)于12M晶振,相當(dāng)于43.2秒! |
wulin 發(fā)表于 2017-9-14 07:08 好的,但是自動(dòng)重裝總是寫(xiě)不對(duì)啊,你能寫(xiě)一個(gè)例子給我看一下嗎,還有你現(xiàn)在寫(xiě)的這個(gè)函數(shù)是放在哪的呢?主函數(shù)里還是中斷響應(yīng)函數(shù)里 |
大學(xué)小學(xué)生 發(fā)表于 2017-9-14 08:53 這個(gè)影響應(yīng)該可以忽略不計(jì),只有零點(diǎn)零幾微秒吧 |
| TMOD=0x01; TH0=0xfc;TL0=0x18; //設(shè)置1ms溢出 EA=1;ET0=1; TR0=1;在tr0=1之前的賦值寄存器配置不都是誤差嗎 積少成多 你可以考慮把這些時(shí)間從定時(shí)其中摳出去 |
|
我是如何把每個(gè)產(chǎn)品上的時(shí)鐘做到精確的呢? 產(chǎn)品中有一段自動(dòng)設(shè)置中斷時(shí)常數(shù)的程序,程序做以下工作 : 1、先在外端口輸入一個(gè)標(biāo)準(zhǔn)秒脈沖(自已做的基準(zhǔn),精度也取決于晶振)。 2、依據(jù)標(biāo)準(zhǔn)秒脈沖計(jì)算出本機(jī)所用晶振的準(zhǔn)確頻率。 3、根據(jù)所得到的晶振頻率計(jì)算出中斷時(shí)常數(shù)。 4、計(jì)算時(shí)常數(shù)需要補(bǔ)償?shù)牟糠郑⒆龀鲅a(bǔ)償方案(不做此步只能做到日差數(shù)秒內(nèi)) 以上這一切都是自動(dòng)完成的,所以10來(lái)秒就可以完成一個(gè)產(chǎn)品時(shí)鐘校準(zhǔn)。 如此時(shí)鐘的準(zhǔn)確度就取決于:所用晶振的穩(wěn)定度和標(biāo)準(zhǔn)秒脈沖發(fā)生器的晶振穩(wěn)定度了。想想如此是否能做到日差秒內(nèi)呢?1秒/24小時(shí)=1/86400=1.16*10^-5,這個(gè)級(jí)別的要求對(duì)于一般晶振穩(wěn)定度來(lái)說(shuō),不是問(wèn)題吧。 |
gaochang000000 發(fā)表于 2017-9-14 07:06 不是很懂第三點(diǎn),一直搞不懂中斷怎么把程序放外面,以減少進(jìn)入中斷的時(shí)間,就像我這個(gè)函數(shù)里的if語(yǔ)句,放外面就不靈了 |
cjjcjj1 發(fā)表于 2017-9-14 06:12 哦哦,好的 |
wc86110 發(fā)表于 2017-9-14 01:57 可能是中斷的問(wèn)題 |
|
如果程序?qū)懙煤茫话憔д褡龅饺詹蠲雰?nèi)不成問(wèn)題。 要點(diǎn): 1、必須知道晶振精確真正的頻率,而不是標(biāo)稱的頻率。如:標(biāo)12M,很可能是11.980234M。 2、用中斷進(jìn)行計(jì)時(shí),且中斷時(shí)常數(shù)的重裝方式很有講究(這是做到精確的精妙關(guān)鍵之處)。 如果沒(méi)有測(cè)試晶振頻率的手段,可以在做成時(shí)鐘之后,用網(wǎng)絡(luò)時(shí)間對(duì)比反推。 |
mengzhixinheng 發(fā)表于 2017-9-14 01:34 你好!我是想把if函數(shù)放外面的,但是放外面就程序沒(méi)反應(yīng),不知道應(yīng)該怎么寫(xiě),另外還有用8位自動(dòng)重裝定時(shí)計(jì)數(shù)器也能提高精度,但是也不知道程序該怎么寫(xiě),怎么寫(xiě)都不對(duì)!還有定時(shí)計(jì)數(shù)器最長(zhǎng)能設(shè)置多長(zhǎng)的溢出時(shí)間呢?非常感謝 |
|
本帖最后由 wulin 于 2017-9-14 08:23 編輯 二樓說(shuō)得對(duì),靠仿真做高精度時(shí)鐘毫無(wú)意義,在實(shí)際運(yùn)用中首先要選用高質(zhì)量晶振,程序中定時(shí)器要設(shè)為自動(dòng)重裝,用查詢法不要使用中斷法計(jì)數(shù),程序在記數(shù)時(shí)也可以調(diào)整精度,程序中要用計(jì)數(shù)法代替軟件延時(shí)delay,否則影響精度! if(TF0==1) //如果查詢定時(shí)器溢出標(biāo)志TF0為1 { //定時(shí)器溢出周期100us TF0=0; //定時(shí)器溢出標(biāo)志TF0清0 Cnt100us++; //時(shí)間變量Cnt100us自+1 if(Cnt100us>=10000) //在此可以按萬(wàn)分之一秒調(diào)整精度 { Cnt100us=0; //時(shí)間變量Cnt100us清0 Cnt1s++; //時(shí)間變量Cnt1s自+1 if(Cnt1s>=60) //如果時(shí)間變量Cnt1s>=60 { Cnt1s=0; //時(shí)間變量Cnt1s清0 Cnt1min++; //時(shí)間變量Cnt1min自+1 if(Cnt1min>=60) //時(shí)間變量分鐘>=60 { Cnt1min=0; //時(shí)間變量分鐘清0 Cnt1hour++; //時(shí)間變量小時(shí)自+1 .......... } } } } |
| 幾點(diǎn)建議:1、精確計(jì)時(shí)一般用時(shí)鐘芯片;2、仿真上用示波器測(cè)試,細(xì)調(diào)延時(shí);3、如果是實(shí)用的程序,可能會(huì)含有其它功能,最好把計(jì)時(shí)功能放到中斷里面,利用中斷的優(yōu)先權(quán)。 |
| 不是延時(shí)的問(wèn)題就是選擇的晶振有問(wèn)題 |
| 看了下你的程序,如果可以減少進(jìn)入中斷的次數(shù),就盡量減少,這個(gè)道理無(wú)論那個(gè)時(shí)候都合適。所以你的溢出時(shí)間能大就大些,不然以后你用多個(gè)定時(shí)器,多個(gè)中斷,找不完的問(wèn)題,還有可能找不到問(wèn)題,而且中斷的事情深得很,你要是把中斷玩得很好,以后寫(xiě)復(fù)雜些的程序時(shí)爽得很。還有你那時(shí)間的寫(xiě)法沒(méi)問(wèn)題,但是有些時(shí)候可以簡(jiǎn)化就盡量簡(jiǎn)化,網(wǎng)上的這種例子51的太多了。最后就是你設(shè)置一下晶振看看 |
| 參與人數(shù) 1 | 黑幣 +20 | 收起 理由 |
|---|---|---|
|
| + 20 | 回帖助人的獎(jiǎng)勵(lì)! |
| 如果程序沒(méi)問(wèn)題。那么要想靠仿真做出高精度時(shí)鐘,這對(duì)電腦,對(duì)軟件都有要求。所以不要強(qiáng)求,這是毫無(wú)意義的。如果你非要誤差小,那么至少定時(shí)器自動(dòng)重載。中斷里只做標(biāo)志,不寫(xiě)多余的代碼。12分鐘10秒是很大了這個(gè)誤差你那幾個(gè)delay影響不到,絕對(duì)有其他原因。 |
| 參與人數(shù) 1 | 黑幣 +20 | 收起 理由 |
|---|---|---|
|
| + 20 | 回帖助人的獎(jiǎng)勵(lì)! |
Powered by 單片機(jī)教程網(wǎng)