摘要:本文介紹一款基于Arduino NANO開發的,帶2.4G無線收發報功能的摩爾斯電碼訓練器的系統設計思路。發射端通過NANO闆的外部中斷引腳采集電鍵輸入的脈沖PPM序列同時驅動喇叭播放電鍵音,然後對采集到的PPM序列時序進行量化分析,解析得到當前輸入的摩爾斯電碼,在LCD屏幕上顯示對應的字符。與此同時将量化好的脈位數據及字符編碼、空格等信息利用串口數據透傳模塊發送出去。接收端接收到數據後,解析出原PPM序列及字符編碼、空格信息,驅動喇叭播放電鍵音,并在LCD屏幕上顯示對應的字符及空格。并且,以電鍵輸入特定編碼:1、可以實現國際通用字碼及數字長碼模式與數字短碼模式的相互切換;2、可以啟動國際通用字碼及數字長碼的自動播報。
1. 引言
摩爾斯電碼是一種國際通用的信号代碼,以"點"和"劃"的組合對字母、數字、符号進行編碼,它可以利用電台的載波進行傳輸,也可以利用聲音、圖形來進行信息傳遞。一直以來,摩爾斯碼都是一種重要的通信方式,在航空、航天、航海等領域都有不可取代的地位,目前也是無線電愛好者進行通聯"必備軟件"。用電台進行摩爾斯碼的抄報與發報需要進行大量的訓練才能達到正常使用的水平,因此,摩爾斯電碼練習器是不可或缺的。而目前市面上的練習器,主要有兩種:一種隻可以通過聽來練習;一種可以聽,可以解碼顯示,接電台可以進行發報。筆者在進行練習時想到,能不能開發一種可以聽、可以顯示,而且不接電台就可以進行模拟收發報的訓練器(要持有電台并且使用電台要經曆一個比較漫長的過程)。因此,筆者開發這一套基于Arduino的、帶2.4G無線傳輸功能的摩爾斯電碼練習器。
2. 系統硬件組成
本文使用的處理核心為Arduino NANO闆,其搭載的ATmega328單片機足以完成系統功能。
由于系統需具備較高的實時性,電鍵信号使用NANO闆的外部中斷0即D2引腳來進行采集,電鍵實質上就是一個開關,因此構建一個開關電路,當電鍵按下時為高電平,放開時為低電平。為了防止由于電壓波動産生誤中斷,在信号與地之間加入了濾波電容,如下圖:
屏幕顯示選用了IIC接口的LCD2004液晶顯示屏幕。該屏幕有4行顯示區,每行可顯示20個字符。因此将屏幕分為上下兩個顯示區,第1、2行顯示發送或本機電鍵輸入的電碼字符,第3、4行顯示接收到的電碼字符,界面設計如下圖所示:
聲音播放用NANO的D3口進行控制,即高電平時發聲,低電平時停止發聲。聲源可由有源蜂鳴器提供。本文選用NE555芯片産生音頻脈沖,D3口驅動繼電器模塊(本文中繼電器使用是成品模塊)通斷,控制脈沖信号的通斷以驅動喇叭發聲,同時喇叭音量、音調可以通過電位器進行調節。電路原理圖如下:
無線傳輸部分使用的是UART接口的數據透明傳輸模塊,工作頻率為2.4GHz。該模塊由于使用的是UART傳輸,隻需要連接TX和RX,簡化了硬件結構,同時程序設計也相對簡化了,隻要設置好波特率和通訊協議即可。接下來開始介紹軟件設計思想。
制作過程
3. PPM采集到摩爾斯電碼的轉換
1) 摩爾斯碼與PPM的聯系
摩爾斯碼由"點"和 "劃"組合而成,例如字母"a",它對應的摩爾斯碼為"·–",字母"B"對應的摩爾斯碼為"–···"。而以電鍵輸入摩爾斯碼時,則以電鍵按下的時間長短來表示點和劃。如輸入"·"時按下電鍵的時間大約為0.04秒左右,得到的是一個寬度大約0.04秒的脈沖,輸入"–"的時間大約是0.15秒,得到的是一個寬度為0.15秒的脈沖信号。因此,以電鍵輸入一個摩爾斯電碼,實質上就是形成了一串由多個寬度不一的脈沖組合而成的脈沖序列,這個脈沖序列就構成了一個PPM信号(脈沖位置調制信号)。如下圖所示:
不同的字母對應不同的點劃組合,從而對應着各不相同的PPM序列,隻要能夠正确采樣這些PPM信号,就可以解析出電鍵所輸入的摩爾斯電碼。
2) 采樣PPM解析摩爾斯碼
a) PPM采樣方法
PPM采樣的關鍵在于獲得各高、低電平脈沖的寬度及其在脈沖序列中的位置。Arduino NANO的外部中斷可以捕捉外部電平跳變,并且Arduino官方庫中的micros()函數調用時可以讀取到系統從開機到當前所經曆的微秒數,有了這兩個工具則可以非常方便地進行PPM采樣。方法如下:首先,将Arduino的外部中斷0的觸發方式設置為跳變觸發,即脈沖的上升沿和下降沿都能觸發中斷,每次中斷到來,就調用micros()函數讀取一個當前時刻,但這個時刻必須區分是上升沿時刻還是下降沿時刻,才能計算脈沖的寬度,例如第一個下降沿的時刻減去第一個上升沿的時刻,就能計算出第一個脈沖的寬度;第二個上升沿的時刻減去第一個下降沿的時刻,就能計算出第一個低電平間隔的寬度(或稱為負脈沖)。值得注意的是,為什麼還要采集低電平的時間呢?低電平對于解碼的意義不大,但由于我們要在接收端"複現"發射端電鍵輸入的聲音,因此必須采集低電平間隔,才能在接收端"一比一"地複現電鍵音。為了實現PPM的完全采集,可以在進入中斷後讀取D2引腳上的電平:如果是高電平,則本次中斷是上升沿觸發;如果是低電平,則是下降沿觸發,如此在不同的觸發狀态下分别記錄上升沿時刻及下降沿時刻,則可以計算正負脈沖的寬度。同時,在每個上升沿置高D3引腳,在每個下降沿置低D3引腳,從而控制喇叭按照電鍵輸入的電碼播放聲音。
接下來,繼續深入地來看如何處理PPM。摩爾斯碼是由點和劃組合得來,但所有字母的點劃組合中點和劃的總數不是一定的,例如:A的點劃總數是兩個(·–),B的點劃總數是四個(–···),D的點劃總數是三個(–··),E的點劃總數是一個(·),所有數字長碼的點劃總數為五個,一些标點符号為6個,那麼每一次采樣,應該如何來存儲這些PPM數據呢?本文中使用的方法是将所有電碼的點劃總數按七個點劃處理,增加的一位是為了方便加入功能轉換編碼(例如長碼轉數字短碼,自動播報啟動),将七個正脈沖數據存儲于一個數組中(代碼中的in_code[]),将間隔的6個負脈沖數據存儲于另一個數組中(代碼中的in_l_code[])。那麼,是不是每一次采集都要等待采集7個正脈沖呢?比如電鍵輸入"A",實質上隻需采集到兩個正脈沖及一個間隔負脈沖就完成了采集,假如緊接着輸入"B",那麼:1、輸入A時如何判斷輸入完畢;2、接着輸入B,B和A之間是否加入空格?為了解決這個問題,就必須建立兩個判斷機制,首先是判斷字碼輸入完畢,然後是判斷兩個輸入的字碼之間是否存在空格。
這兩個判斷機制,可以建立在時間間隔的基礎上。對于一個相對标準的發報手法,點和劃的長度、點和劃之間的間隔、字碼間的間隔都可以得到一個比較固定的時間範圍,因此,可以在每個下降沿到來後置位一個标志位,用于控制定時計數的啟動,如果定時計數達到設定的阈值(設定好的字碼間隔),那麼表示該字碼輸入完畢;如果在正常的點劃間隔内能夠進入下一次中斷(上升沿觸發),那麼定時計數标志位會被清除,停止計數;另外,在每一個下降沿到來時,用另一個計數值來記錄采集到正脈沖的個數,如果定時計數被清除,那麼正脈沖記錄值從0加到6時則表示7個點劃采集完畢,即字碼輸入完畢。字碼輸入完畢後,置位相應的标志為,進行解碼、顯示并且發送數據了。完成了一個字碼的顯示後,下一個字碼的顯示位置默認是加1,并且置位空格檢測标志位,同樣是利用定時計數的方法,如果計數未達到設定的阈值,由于上升沿觸發中斷,那麼空格檢測标志位及計數會被清零,那麼這個字碼就會緊挨着上一個字碼顯示;如果計數值達到了設定的阈值,則把在原有的顯示位置上再加1,那麼下一個輸入的字碼與上一個字碼間會增加一個空格。以上的檢測過程在一個1毫秒的定時中斷函數中處理。經過大量數據樣本采集及實際測試,字碼間的間隔時間阈值設定為150毫秒比較合适,空格符加入的時間間隔阈值設定為200毫秒較為合适。調整這兩個阈值,也可以起到調節發報速度的作用,對于初學發報的可以适當調大兩個值。對于發報較為熟練的可以适當減小兩個值。
發射及接收界面
外部中斷處理函數的代碼
定時中斷函數代碼
b) PPM數據解析摩爾斯碼
将PPM數據解析為摩爾斯碼,要經過兩個步驟:1、将PPM按位置量化為二進制編碼,每一個PPM序列量化為一個14個位的編碼,并用一個16位的變量來存儲這個編碼;2、根據編碼映射摩爾斯碼。
為實現第一個步驟,筆者對點和劃的脈沖寬度以及間隔時間進行了大量采樣(數據樣本來源于發報速度較為标準的資深無線電愛好者),下面是點和劃的部分采樣值,單位為微秒。
"點"的脈寬采樣
"劃"的脈寬采樣
從以上數據可以看到,"點"的脈寬在40000-60000微秒之間(0.04秒-0.06秒),"劃"的脈寬在150000—300000微秒之間(0.15秒-0.3秒)。由于不同的使用者發報手法不同,得到的點劃脈寬也不相同,為了提高系統的識别率,放寬了脈沖的範圍:50—120000微秒(0.00005秒—0.12秒)識别為"點",大于120000但小于450000微秒(0.12秒—0.45秒)識别為"劃",其餘的都識别為錯誤輸入。确定好了脈寬範圍以後,接下來對PPM信号進行量化編碼。在一個16位的二進制編碼中,以0-13位(最低位為第0位)來存放編碼,每兩位的組合來表示一個點劃位,以"01"表示"點","11"表示劃,"00"表示空位,存放順序為:電碼先輸入的放在二進制的低位,後輸入的依次往高位存放,則字碼"A"對應的二進制編碼為:00 00 00 00 00 00 11 01 (·–),轉換為十六進制為:0x000d;"B"對應的二進制編碼為:00 00 00 00 01 01 01 11(–···),轉換為十六進制為:0x0057。将所有字碼按照這個規則轉換為十六進制編碼,當采集完一個PPM序列後按上述規則進行量化編碼,将量化得到的編碼放入到"switch"結構中進行判斷,就可以快速地得出當前電鍵輸入的字碼,如經過對比沒有找到對應的編碼,則判斷為輸入錯誤,在屏幕上會顯示"*"提示。下面是功能程序段:
PPM—量化編碼示例代碼
摩爾斯字碼提取示例代碼
4. 串口數據發送
電鍵PPM采集、解碼顯示的同時,還要将電鍵PPM數據、摩爾斯碼以及空格信息發送到接收端,其中PPM數據用于"複現"發射端的電鍵音。在采集PPM數據時,采樣的是脈沖寬度的微秒數,存儲這些數據使用了"long int"長整形變量,這樣的一個變量是32位的,占4個字節,而發送時一次是發送一個字節,為了提高效率,首先對PPM數據進行"壓縮",一個簡單的辦法就是直接除以1000,這樣就等同于發送PPM的毫秒數,接收端用毫秒數據來播報電鍵音與原電鍵音相差僅是微秒級的,不影響效果,并且經過壓縮,一個脈沖寬度數據就可以縮減為16位,即兩個字節,這樣極大地減少了發送的數據量,更重要的是:有效數據位實際不足16位,因為正常的點劃輸入,脈沖寬度最多達到0.45秒,即450毫秒,将"450"轉換為二進制為:111000010,隻占到了9位,這樣一來給通訊協議的制定帶來了方便之處。
采集到的PPM數據包括7個正脈沖和6個負脈沖,經過"壓縮"後放在13個16位的變量中,然後将它們"拆"成26個字節,并且按照制定好的通訊協議"擺放"到數據幀中。整個數據幀的長度為30個字節,其中第"0"字節為"幀頭"—0xf0,第"1"字節到第"26"字節存放PPM數據,"27"-"28"字節存放摩爾斯字碼的二進制編碼,最後一個字節存放"顯示空格标志",當其值為0x01時不空格,0x03時插入空格。為了讓數據字節中不出現"0xf0",在"拆"數據時作了一些處理。由于PPM數據經壓縮後實際有效位隻有9位, 因此可以将這個數據的"低7位"提取出來(&0x007f)放到一個字節中,剩下的"右移7位"(先&0xff80,後>>7)放到另一個字節中,這樣一來就可以保證每個數據字節的最高位保持為"0"即不會出現與幀頭重複的"0xf0",以保證接收端能夠準确地解析數據。
數據拆分示意圖
數據幀協議示意圖
數據處理流程:
1、 從外部中斷采集電鍵輸入的PPM序列,将采集到的正負脈寬依次放置于數組"in_code[]"中,in_code[]的數據首先被用于解析摩爾斯碼的二進制編碼,放置于16位變量mos_code中;
2、将in_code[]數組(正脈沖)及in_l_code[]數組(負脈沖)中的數據"壓縮",依次放置在數組"val[]"中,正負脈沖的排列順序按照PPM的時序排列;
3、将val[]數組中的各元素及mos_code進行拆分提取,放置于"s_date[]"數組中(從第1字節至第28字節),第0字節放置幀頭0xf0,末尾放置空格标志。然後依次從串口發送數據。數據發送完畢後,清空所有數組,準備進行下一輪數據處理。
數據處理及發送代碼
5. 串口接收數據
制定好了通訊協議,串口收到數據後就可以準确無誤地進行數據解析了。在Arduino官方庫中,可以用串口事件來接收處理數據,類似于串口中斷的效果。為了提高系統的實時性,串口接收數據利用串口事件來處理。當串口收到數據後,先判斷串口緩沖區的字節數是否滿足30個字節,如果滿足這個條件,則進行數據判讀。進入數據判讀,首先是要尋找"幀頭-0xf0",找到它之後,緊随其後的29個字節就是我們需要的數據字節。整個接收處理過程就是發送的"逆過程"。首先是将"整理"好的30個字節依次放入"r_date[]"中,然後按照幀協議将第1-26字節的數據"合成",放入"r_val[]"數組中,這個就是發送端采集到的PPM脈寬數據,這個數據用于"一比一"地播放發送端的電鍵音。第27、28字節則"合成"摩爾斯碼的二進制編碼,29字節決定了當前收到的字符前面是否加入空格,最後在LCD屏幕的接收區顯示。
串口接收、處理數據代碼
從以上所有代碼中可以看到,電鍵采集以及數據接收都受到一個标志位"by_flag"的限制,這個标志為0時,可以正常收發報,當電鍵輸入特定的功能碼時,它會被置1時,系統會進入字碼自定播報模式,即自動播報"A-B,0-9"的摩爾斯碼,此時不響應任何操作,自動播報完畢後該标志位會清0,恢複到正常收發報模式。另外,系統還支持國際通用字碼及數字短碼模式的切換,這些切換都是通過電鍵輸入特定的功能編碼實現的。
摩爾斯碼及系統功能碼表
6. 結束語
該訓練器目前能夠滿足發報訓練的要求,可以讓初學者的發報速度接近标準速度。但系統還有很多地方可以改進優化。1、對于過長的"劃"輸入(電鍵按下不放)會造成數據幀的數據位溢出,從而在解碼端出現電鍵音"失真",目前還需要找到更好的方法來解決這個問題。2、在電鍵連續輸入字碼時,發報的速度需與系統的識别速度高度吻合,發報過快會出現輸入錯誤提示,太慢又會加入空格,此問題還需進一步優化解決。3、目前系統隻支持手動鍵輸入,後續還需增加自動鍵識别功能。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!