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