本內(nèi)容出自:智慧帽diy(cleverhat)
簡(jiǎn)介: 手頭有一個(gè)項(xiàng)目,需要一個(gè)“精確”的頻率計(jì),作為DIY一族,準(zhǔn)備動(dòng)手制作一個(gè)。在制作頻率計(jì)之前,需要一個(gè)能精確測(cè)量晶振頻率的東西。之前嘗試過NTP對(duì)時(shí),發(fā)現(xiàn)精度太差,不好使。所以準(zhǔn)備改用電波對(duì)時(shí)。百度搜了一下,動(dòng)手做電波表的朋友還真不少,但講得不夠詳細(xì),解碼部分還是“保密”的。只好嘗試自立更生了。
BPC電波鐘模塊 電波對(duì)時(shí)需要一個(gè)接收頭,接收授時(shí)中心發(fā)出的電波信號(hào)。我們國(guó)家的授時(shí)信號(hào)頻率為68.5Khz,發(fā)射臺(tái)在商丘。 接收頭實(shí)際上是一個(gè)窄帶信號(hào)接收器,其帶寬只有幾個(gè)赫茲,通常采用晶體濾波器來限制接收帶寬。由于工作頻率比較低,放大部分是比較 容易設(shè)計(jì)的,有一定無線電基礎(chǔ)的都可以做出來。
這里我們直接從某寶上買來一個(gè)完整的電波鐘模塊。花了15大洋,省了很多事。
radio-clock01.jpg (25.03 KB, 下載次數(shù): 141)
下載附件
2017-10-12 01:49 上傳
BPC模塊上用到的引腳有4條。 - VCC :電源,1.5~3.5V
- GND :地
- SIG :BPC授時(shí)信號(hào)輸出
- EN :模塊使能(低電平使能,高電平關(guān)閉模塊)
BPC解碼和校時(shí) 電波鐘模塊輸出的BPC信號(hào)如下圖,1分鐘包含3個(gè)幀,一個(gè)BPC幀的周期為20秒,除了第"0"秒外,其余19秒每秒一個(gè) 脈沖。方波秒脈沖有0.1S,0.2S,0.3S,0.4S四種脈沖寬度狀態(tài),分別表示四進(jìn)制的0, 1, 2, 3,現(xiàn)有的時(shí)間編碼都以二進(jìn)制表示時(shí)間信息, 是為了采用微處理器解碼方便。但四進(jìn)制只是數(shù)值的一種表示方式,并不影響微處理器把它作為二進(jìn)制處理,或者采取簡(jiǎn)單的變換就可將1位 四進(jìn)制數(shù)變成2位二進(jìn)制數(shù)。
radio-clock02.jpg (22.67 KB, 下載次數(shù): 152)
下載附件
2017-10-12 01:49 上傳
- P0設(shè)在每分鐘0,20, 40秒,以缺少秒脈沖使幀與幀隔開,同時(shí)作為幀起始預(yù)告。
- P1為幀標(biāo)志,P1=0表示幀起于第1秒,P1=1表示幀起始于21秒,P1=2表示幀起始于41秒。幀標(biāo)志是必需的,它用來確定整分的起始。 例如:當(dāng)接收完一組包含著“10時(shí)38分”的時(shí)間編碼時(shí),如果幀標(biāo)志標(biāo)明該幀為第二幀,就可以把下一幀的P1位置標(biāo)定為10時(shí)38分41秒, 再過20秒便是10時(shí)39分的起始。
- P2為預(yù)留位。用于需要擴(kuò)充信息。
- 時(shí)&分表示了時(shí)間
- 其他各位數(shù)據(jù)在本案中沒有用到,不做詳細(xì)說明
解碼MCU采用了TI公司的MSP430G2211IPW14,MSP系列的MCU以低功耗著稱,非常適合于電池供電的應(yīng)用。本例中, MCU大部分時(shí)間工作在約20uA的低功耗模式下。
BPC解碼軟件使用了MSP430G2211的TimeA,TimeA的計(jì)數(shù)器由32768Hz的晶振提供時(shí)鐘,從0~0xFFFF循環(huán)計(jì)數(shù)。每2秒 循環(huán)一周。BPC信號(hào)接在MCU的中斷請(qǐng)求上,在上下跳變沿均產(chǎn)生中斷,中斷服務(wù)程序中讀取TimerA計(jì)數(shù)器。根據(jù)脈沖寬度解碼BPC編碼的信號(hào)。 由于BPC信號(hào)的最小脈寬為0.1秒,軟件中還加入了濾波處理,可以濾除脈寬較窄的干擾信號(hào)。
- if (MCU_INT_GET(BPC_SIGNAL_PORT, BPC_SIGNAL_PIN)) {
- static uint16 wPrevToggle = 0; //前一次跳變時(shí)刻
- static uint16 wLastPulseWidth = 0; //前一次脈沖寬度
- static uint8 bLastPulsePolarity = 0; //前一次脈沖極性(1:正脈沖,0:負(fù)脈沖)
- static uint8 ucBpcBitPos = 0xFF; //BPC解碼位置,0xFF表示解碼狀態(tài)機(jī)復(fù)位
- static uint8 ucBpc[2]; //BPC碼數(shù)據(jù),只取包含"時(shí):分:秒"信息的前面2字節(jié)。
- uint16 wCurToggle; //本次跳變時(shí)刻
- uint16 wCurPulseWidth; //本次脈沖寬度
- uint16 wBpcSecond; //BPC解碼得到的時(shí)間秒數(shù)
- uint8 bCurPulsePolarity; //本次脈沖極性(1:正脈沖,0:負(fù)脈沖)
- uint8 ucTmp;
-
- MCU_INT_XOR_EDGE(BPC_SIGNAL_PORT, BPC_SIGNAL_PIN);
- MCU_INT_CLEAR(BPC_SIGNAL_PORT, BPC_SIGNAL_PIN);
- //正跳變,表示的是負(fù)脈沖(結(jié)束)
- bCurPulsePolarity = MCU_INT_GET_EDGE(BPC_SIGNAL_PORT, BPC_SIGNAL_PIN) ? 0 : 1;
- wCurToggle = GetCurTimerA();
- wCurPulseWidth = wCurToggle - wPrevToggle;
- if ((wCurPulseWidth < PULSE_FILTER_OUT) || (bCurPulsePolarity == bLastPulsePolarity)) {
- //本次跳變脈寬過短,將當(dāng)作干擾毛刺被過濾掉,脈寬同前一次合并
- //本次脈寬極性同前一次相同(跟在干擾毛刺后),脈寬也同前一次合并
- wLastPulseWidth += wCurPulseWidth;
- } else {
- //前一次脈寬數(shù)據(jù)有效,可以處理了。
- if (!bLastPulsePolarity && (wLastPulseWidth > PULSE_10ms * 100)) {
- //每幀數(shù)據(jù)頭部有1秒時(shí)間的空檔期,表示幀起始
- ucBpcBitPos = 14; //只用到前14bit數(shù)據(jù)
- ucBpc[0] = ucBpc[1] = 0;
- } else if (ucBpcBitPos != 0xFF) {
- if (!bLastPulsePolarity) {
- //負(fù)脈沖
- if (wLastPulseWidth < PULSE_10ms * 55)
- //BPC編碼正脈沖寬度最小600ms,<550ms為非法,解碼狀態(tài)機(jī)復(fù)位
- ucBpcBitPos = 0xFF;
- }
- else {
- //正脈沖
- ucTmp = 0xFF;
- if (wLastPulseWidth < PULSE_10ms * 45) {
- if (wLastPulseWidth > PULSE_10ms * 35) //400ms脈寬
- ucTmp = 3;
- else if (wLastPulseWidth > PULSE_10ms * 25) //300ms脈寬
- ucTmp = 2;
- else if (wLastPulseWidth > PULSE_10ms * 15) //200ms脈寬
- ucTmp = 1;
- else if (wLastPulseWidth > PULSE_10ms * 5) //100ms脈寬
- ucTmp = 0;
- }
- if (ucTmp == 0xFF) {
- //脈寬非法,解碼狀態(tài)機(jī)復(fù)位
- ucBpcBitPos = 0xFF;
- }
- else {
- //保存合法數(shù)據(jù)
- ucBpcBitPos -= 2;
- ucBpc[ucBpcBitPos >> 3] |= ucTmp << (ucBpcBitPos & 0x07);
- if (ucBpcBitPos == 0) { //一幀數(shù)據(jù)接收完成
- ucBpcBitPos = 0xFF; //解碼狀態(tài)機(jī)復(fù)位,等待下次數(shù)據(jù)
- wBpcSecond = ((ucBpc[1] << 2) | (ucBpc[0] >> 6)) & 0x0F; //小時(shí)
- wBpcSecond *= 3600;
- wBpcSecond += ((uint16)(ucBpc[0] & 0x3F)) * 60; //分鐘
- wBpcSecond += ((ucBpc[1] >> 4) & 0x03) * 20 + 21; //秒數(shù)
- //如果相臨近的兩次BPC校時(shí)都是準(zhǔn)確的(沒有誤碼),守時(shí)中斷應(yīng)該在BPC
- //信號(hào)的邊界前后,因此,秒數(shù)只可能差0或1秒。據(jù)此判斷校時(shí)成功
- if ((wBpcSecond - s_wRealSecond) < 2)
- s_wEvent |= BPC_FINISHED;
- s_wRealSecond = wBpcSecond;
- s_wTarSecond = wCurToggle + COUNT_1S;
- } //if (ucBpcBitPos == 0
- }
- }
- }
- wPrevToggle = wCurToggle;
- wLastPulseWidth = wCurPulseWidth;
- bLastPulsePolarity = bCurPulsePolarity;
- }
- }
復(fù)制代碼
守時(shí)信號(hào)輸出 TimerA的通道0工作在比較器模式,用作守時(shí)和UART波特率發(fā)生器.時(shí)間以12小時(shí)內(nèi)的秒數(shù)表示,從00:00:00或12:00:00 開始計(jì)數(shù),在每秒開始時(shí)將時(shí)間讀數(shù)從UART口發(fā)出去。MSP430G2211沒有專用的UART,需要軟件實(shí)現(xiàn)。UART數(shù)據(jù)格式為8位、無校驗(yàn)、1200波特率.總共 16bit數(shù)據(jù),需要用2個(gè)字節(jié)表示,加上每字節(jié)的起始位、停止位,總共20位。
- #pragma vector=TIMER0_A0_VECTOR
- __interrupt void Uart_ISR(void)
- {
- static int8 iBits = 0;
- static uint16 wMask;
-
- TACCTL0 &= ~TAIFG; //清中斷
- //每秒起始位置把16bits實(shí)時(shí)時(shí)間通過UART發(fā)送出去
- if ((iBits == 0) || (iBits == 10)) {
- MCU_IO_CLR(UART_TX_PORT, UART_TX_PIN); //起始位
- } else if ((iBits == 9) || (iBits == 19))
- MCU_IO_SET(UART_TX_PORT, UART_TX_PIN); //停止位
- else {
- //發(fā)送數(shù)據(jù)位
- if (s_wRealSecond & wMask)
- MCU_IO_SET(UART_TX_PORT, UART_TX_PIN);
- else
- MCU_IO_CLR(UART_TX_PORT, UART_TX_PIN);
- wMask <<= 1;
- }
- iBits++;
-
- if (iBits == 20) {
- //實(shí)時(shí)時(shí)間發(fā)送完畢,準(zhǔn)備在下一秒再次發(fā)送
- s_wTarSecond += COUNT_1S;
- TACCR0 = s_wTarSecond;
- s_wRealSecond++;
- if (s_wRealSecond >= ((uint16)12*3600))
- s_wRealSecond = 0; iBits = 0;
- wMask = 1;
- s_wEvent |= SECOND_EVENT;
- } else
- TACCR0 += UART_BIT_WIDTH;
-
- __low_power_mode_off_on_exit();
- }
-
復(fù)制代碼
守時(shí)時(shí)鐘校準(zhǔn) 本系統(tǒng)需要依靠標(biāo)稱頻率為32768Hz晶振提供時(shí)間基準(zhǔn)來守時(shí),晶振負(fù)載電容對(duì)頻率有微調(diào)作用.為了測(cè)定晶振實(shí)際工作 頻率,可測(cè)量一段時(shí)間內(nèi)的積累誤差。例如在10:00:00時(shí)進(jìn)行第一次BPC校時(shí),6個(gè)小時(shí)后在16:00:00進(jìn)行第二次BPC校時(shí),用串口工具接收并打印出 第二次BPC校時(shí)前后從UART口輸出的守時(shí)信號(hào)
校時(shí)之前 【2017-10-11 15:58:47:850】CB 0D 【2017-10-11 15:58:48:850】CC 0D 校時(shí)之后 【2017-10-11 15:59:47:827】07 0E 【2017-10-11 15:59:48:827】08 0E 從打印出來的數(shù)據(jù)可以看出,在校時(shí)前后兩點(diǎn)(0x0E08-0x0DCC = 60秒),對(duì)應(yīng)PC機(jī)系統(tǒng)時(shí)間為59:48:827-58:48:850 = 59.977秒,也就是說6小時(shí)內(nèi)積累的誤差為-0.023秒,可以忽略不計(jì)。否則可能需要調(diào)整負(fù)載電容來對(duì)頻率進(jìn)行微調(diào),或者也可以調(diào)整代碼中的 COUNT_1S的宏定義值來重新標(biāo)定“1秒”。
機(jī)芯驅(qū)動(dòng) 本設(shè)計(jì)初衷是要制作一個(gè)能夠準(zhǔn)確計(jì)時(shí)(無累積誤差)的東西.因此完成守時(shí)信號(hào)輸出就OK了,但既然動(dòng)了手,就準(zhǔn)備弄個(gè) 完整的電波鐘玩玩。拆解了一個(gè)多時(shí)不用的石英鐘,只將機(jī)芯步進(jìn)馬達(dá)的線圈引出,其他電路統(tǒng)統(tǒng)拆掉。這里我范了一個(gè)錯(cuò)誤,本以為馬達(dá)是1秒 走一步或二步,按此設(shè)計(jì)了驅(qū)動(dòng)代碼,結(jié)果馬達(dá)跑得非常別扭,走走退退,不知怎回事,弄了半天才發(fā)現(xiàn)問題所在,實(shí)際上這個(gè)機(jī)芯步進(jìn)馬達(dá)是每秒16步 的,差得也太遠(yuǎn)了。因此,建議朋友在拆機(jī)前先測(cè)一下原機(jī)的驅(qū)動(dòng)波形。
這種步進(jìn)電機(jī)的驅(qū)動(dòng)信號(hào)為正負(fù)交替的脈沖信號(hào)。脈沖寬度需要有個(gè)合理的范圍,拆機(jī)時(shí)沒有先用示波器測(cè)一下,只好自己 湊了。不過,就算測(cè)了也只能供參考,因?yàn)樵瓩C(jī)是1.5V供電的,現(xiàn)在改成3V,脈寬肯定需要調(diào)窄,理論上升到2倍電壓后脈寬應(yīng)是原來的1/4.我的這個(gè)馬達(dá) 用12ms脈寬驅(qū)動(dòng),工作得很Happy.朋友自己制作時(shí)可以自行調(diào)整MOTOR_PULSE_DUTY。
radio-clock03.jpg (21.79 KB, 下載次數(shù): 162)
下載附件
2017-10-12 01:49 上傳
為了實(shí)現(xiàn)正負(fù)極性的交替,使用了2個(gè)IO端口,輸出兩路移相的方波信號(hào).兩路方波信號(hào)之間的相差即為脈沖寬度。當(dāng)鐘面顯示 的時(shí)間同實(shí)際時(shí)間有偏差時(shí),需要改變脈沖周期以調(diào)整電機(jī)速度,使兩者趨于一致。機(jī)芯驅(qū)動(dòng)使用了TimeA的通道1,在其中斷服務(wù)中實(shí)現(xiàn)
- #pragma vector=TIMER0_A1_VECTOR
- __interrupt void Motor_drv_ISR(void)
- {
- if (TACCTL1 & CCIFG) {
- // 步進(jìn)電機(jī)驅(qū)動(dòng)信號(hào),一個(gè)周期分4個(gè)Stage,電機(jī)走2步。
- // S1,S3提供動(dòng)力輸出。
- // ------
- // | S1 |
- // | |
- // --------------| |--------------| |
- // S0 S2 | |
- // | |
- // ------
- // S3
-
- static uint8 ucStage = 0; //步進(jìn)電機(jī)每走一步分2個(gè)stage。
- uint16 wS0S2; //S0,S2時(shí)間長(zhǎng)度
-
- TACCTL1 &= ~CCIFG; //清中斷
- if (ucStage == 2 * STEP_1S - 1) {
- //每秒末進(jìn)入此處
- ucStage = 0;
- s_wDisplaySecond++;
- if (s_wDisplaySecond >= ((uint16)12 * 3600))
- s_wDisplaySecond = 0;
- }
- else
- ucStage++;
- //先假定鐘面時(shí)間總是偏"快"的,算一下"快"了多少
- if (s_wDisplaySecond > s_wRealSecond)
- wS0S2 = s_wDisplaySecond - s_wRealSecond;
- else //超了一圈(12小時(shí))
- wS0S2 = ((uint16)12 * 3600) - (s_wRealSecond - s_wDisplaySecond);
- //如果算下來"快"了9小時(shí)以內(nèi),認(rèn)為其確實(shí)"快"了,否則認(rèn)為實(shí)際是"慢"了3小時(shí)不到。
- //之所以不以6小時(shí)分界,是因?yàn)椴竭M(jìn)馬達(dá)可以無限放慢,有限地加快。
- if (wS0S2 < 2) //偏快不多,正常運(yùn)行
- wS0S2 = MOTOR_PERIOD - MOTOR_PULSE_DUTY; //正常運(yùn)行
- else if (wS0S2 < ((uint16)9 * 3600)) //偏快,降速運(yùn)行
- wS0S2 = MOTOR_PERIOD_SLOW - MOTOR_PULSE_DUTY;
- else //偏慢,加速運(yùn)行
- wS0S2 = MOTOR_PERIOD_FAST - MOTOR_PULSE_DUTY;
- //步進(jìn)電機(jī)驅(qū)動(dòng)信號(hào)4個(gè)stage一個(gè)循環(huán),走2步
- switch(ucStage & 0x03) {
- case 0:
- MCU_IO_CLR(MOTOR_DRVN_PORT, MOTOR_DRVN_PIN);
- TACCR1 += wS0S2;
- break;
- case 1:
- MCU_IO_SET(MOTOR_DRVP_PORT, MOTOR_DRVP_PIN);
- TACCR1 += MOTOR_PULSE_DUTY;
- break;
- case 2:
- MCU_IO_SET(MOTOR_DRVN_PORT, MOTOR_DRVN_PIN);
- TACCR1 += wS0S2;
- break;
- case 3:
- MCU_IO_CLR(MOTOR_DRVP_PORT, MOTOR_DRVP_PIN);
- TACCR1 += MOTOR_PULSE_DUTY;
- break;
- }
- }
- __low_power_mode_off_on_exit();
- }
復(fù)制代碼
使用方法 由于系統(tǒng)無法讀取鐘面顯示的時(shí)間,因此在系統(tǒng)上電啟動(dòng)時(shí),必須先把鐘面撥到00:00:00的默認(rèn)位置.上電時(shí),MCU默認(rèn)為 鐘面顯示時(shí)間和系統(tǒng)實(shí)際時(shí)間為00:00:00.系統(tǒng)在00:00:02啟動(dòng)BPC對(duì)時(shí),對(duì)時(shí)成功后,“實(shí)際時(shí)間”就準(zhǔn)確了,這時(shí),從UART輸出的守時(shí)信號(hào)也是準(zhǔn)確的了。 但鐘面時(shí)間需要一段時(shí)間后才能逐步同實(shí)際時(shí)間一致。以后,系統(tǒng)會(huì)在每天的00:00:02和12:00:02各啟動(dòng)BPC對(duì)時(shí)一次,如果對(duì)時(shí)成功或20分鐘內(nèi)不能對(duì)時(shí) 則自動(dòng)關(guān)閉BPC模塊以節(jié)約電池。對(duì)時(shí)成功,照明等會(huì)點(diǎn)亮2秒。
在每天的17:00-21:00, 05:00-9:00兩個(gè)時(shí)段內(nèi)是BPC發(fā)射臺(tái)是關(guān)閉的,在這兩個(gè)時(shí)段內(nèi)開機(jī)是無法對(duì)時(shí)的。
系統(tǒng)提供了一個(gè)按鈕,短按按鈕可以點(diǎn)燈5秒,以便夜間照明。長(zhǎng)按2秒以上可以立即打開BPC對(duì)時(shí),對(duì)時(shí)成功或5分鐘內(nèi)不成功 則自動(dòng)關(guān)閉BPC模塊。這些業(yè)務(wù)邏輯都在主程序中實(shí)現(xiàn)。
實(shí)物圖
radio-clock05.jpg (35.94 KB, 下載次數(shù): 135)
下載附件
2017-10-12 01:49 上傳
原理圖 JI為下載接口。MSP430G2211晶振匹配電容內(nèi)置,可配置,所以不需要外接電容
radio-clock04.jpg (34.32 KB, 下載次數(shù): 149)
下載附件
2017-10-12 01:49 上傳
0.png (77.58 KB, 下載次數(shù): 129)
下載附件
2017-10-12 01:45 上傳
全部資料下載地址:
clock_src.rar
(15.25 KB, 下載次數(shù): 290)
2017-10-12 01:45 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|