モールス信号解析器は、アマチュア無線でのCW送信用電鍵や実際のCW受信機出力などからロジックレベルのモールス信号を受け取り、その信号に対応するASCII文字をシリアル通信(UART)でPCなどに出力するものです。 受け取ったモールス信号は、信号の断続時間からをmCode様式に組み合わせてから、プログラムメモリに記録したm_Code一覧でASCII文字に変換しています。
| 文字 | モールス | mCode |
|---|---|---|
| --- 長短点 --- 長さ | ||
| A | ・- | 1000 0000 0000 0010 |
| B | -・・・ | 0111 0000 0000 0100 |
| C | -・-・ | 0101 0000 0000 0100 |
<mCodeとは>
このプログラムのために開発したモールス信号固有のコードで、16bitで構成しています。
16bitのうちの
上位12bitが、長点[0]、短点[1の区別]を表し、
下位 4bitが、符号の長さを表します。
解析プログラムは、1mSec毎に入力信号をモニターし、そのロジックレベルが変化したときに信号の継続時間を測定し処理を行います。 一般に信号のある時間をMarkと呼び、信号のない時間は、Spaceと呼ばれています。Markの継続時間がプログラムの先頭で定義しているDot-Barの閾値時間(thBar) より短ければ、そのMarkを短点と判断し、長ければ、長点と判断します。Spaceが、Mark-文字の閾値時間(thChar) より短い時は、一文字の情報を送信中であり、thChar より長い Spaceを感知したときは、一文字のコード情報が終了したとを意味します。
これらの情報から、受信した一文字のmCodeを組み立て、モールス一覧と比較し、そのモールス信号が意味しているASCII文字に変換します。変換された文字はUARTからシリアル送信されます。なお、単語閾値(thWord) より長い Spaceを感知したときは、単語の区切りを表しているので、受信した文字の表示に続けて空白文字(スペース)がシリアル送信されます。
以下に、処理のフローチャートを示します。プログラムのソースリストと見比べてください。
<プログラム>
//********************************************************************
// <モールス信号解析> 20130321 State版 B17_MorseDecd
// モールス信号(負論理)を入力すると、そのMarkとSpaceの時間を
// mSec単位で測定し、文字に翻訳しUARTで送信する。
// モールスの通信速度は、短点が100mSと想定しているが、プログラム上の
// 定義で変更することができる。
// Complier: Microchip xc8
// Company: Microchip Technology, Inc.
//
// --- Copyright (C) 2013 Kazuo Iwamoto All Rights Reserved. ---
//********************************************************************
// Change History:
// Rev Description
// ---- -----------------------------------------
// 0.0 Initial release
// ********************************************************************/
// ************** モールスの通信速度 *********************************
#define thBar 200 // Markの短点と長点との分離
#define thChar 200 // SpaceのMark間隔と文字間隔との分離
#define thWord 500 // Spaceの文字間隔と単語間隔との分離
// ************** ノイズの防止時間 ***********************************
#define NzLength 50 // mSec単位の時間
//--------------------------------------------------------------------
#define CWinput !PORTCbits.RC6 // CW入力を反転して使用
#define LED LATCbits.LATC0 //
// 制御ステート
#define waitMkStart 0
#define waitMkEnd 1
#define waitSpEnd 2
/** INCLUDES *******************************************************/
#include <xc.h>
//-------------- コンフィグレーション --------------------------------
#pragma config MCLRE = OFF
#pragma config PWRTEN = OFF, BOREN = OFF, BORV = 30
#pragma config WDTEN = OFF, WDTPS = 32768
#pragma config STVREN = ON
#pragma config FOSC = IRC // 内部クロック
#pragma config PLLEN = ON, CPUDIV = NOCLKDIV, USBDIV = OFF
#pragma config FCMEN = OFF, IESO = OFF, HFOFST = OFF
#pragma config LVP = OFF, XINST = OFF, BBSIZ = OFF
#pragma config CP0 = OFF, CP1 = OFF, CPB = OFF, CPD = OFF
#pragma config WRT0 = OFF, WRT1 = OFF
#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTRB = OFF
//------------ プロトタイプ ---------------
char sigToASC(int,int);
//------------ 変数 -----------------------
char NzCount; // ノイズ防止回数定義
char CWclean; // ノイズを排除したCW信号
int Time_mS; // 現時間mS単位
int Period; // 前の信号間隔
int MarkLen; // Markの長さ
int SpaceLen; // Spaceの長さ
char state; // CW 受信のステート
unsigned int tmpCode; // CWcode作成で使用する
char tmpLen; // CWcode作成で使用する
// ----- モールスコードの短長点・長さを一覧 -----
// 上位Byte:短長点コード 下位Byte:長さ
// 長さの順に並べてある。0は無効データ。
// 段落は < 、 訂正は ] で表す。
//--------------------------------------------------------------------
const unsigned int m_code[] = {
0,0x5006,0xB406, 0, 0, 0, 0,0x8406, // !"#$%&'
0x4805,0x4806, 0,0xA805,0x3006,0x7806,0xA806,0x6805, //()*+,-./
0x0005,0x8005,0xC005,0xE005,0xF005,0xF805,0x7805,0x3805, //01234567
0x1805,0x0805,0x1C06, 0,0xFF08,0x7005, 0,0xCC06, //89:;<=>?
0x9406,0x8002,0x7004,0x5004,0x6003,0x8001,0xD004,0x2003, //@ABCDEFG
0xF004,0xC002,0x8004,0x4003,0xB004,0x0002,0x4002,0x0003, //HIJKLMNO
0x9004,0x2004,0xA003,0xE003,0x0001,0xC003,0xE004,0x8003, //PQRSTUVW
0x6004,0x4004,0x3004, 0, 0,0xAC06, 0, 0, //XYZ[\]^_
0 };
//----------------------高優先割込み----------------------------------
// 高優先割込みは時間測定に使用
//--------------------------------------------------------------------
void interrupt Hi_Isr(void){ // 16KHzで割込みPIR1 1 CCP1IF
// ----- CW 関連低優先割込み ---------------------------------------
if(TMR0IF){ // CCPFからの割込みを確認
TMR0IF = 0; // 割込みフラッグをクリア
// -------------------------------------------------------------
if(Time_mS++ >= 999)Time_mS = 999; // 現時間を更新
if(CWclean != CWinput){ // CWの状態が不一致なら
if(NzCount++ >= NzLength){ // 変化を連続確認したら
CWclean = CWinput; // 状態を反映
Period = Time_mS; // 信号間隔を保存
Time_mS = 0; // 現時間をリセット
NzCount = 0; // ノイズ防止をリセット
}
}else{ // CWの状態が一致なら
NzCount = 0; // ノイズ防止期間をリセット
}
// ----------------------------------------
}
} //
//----------------------メインプログラム------------------------------
void main(void){
//------------ 変数 -----------------------
char mChar;
OSCCON = 0b01010010; // 内部クロック4Mhz
LATC = 0b00000000; // PortC
TRISC = 0b11111100; // PortC RC0,1出力
ANSEL = 0b00000000; // デジタル
ANSELH = 0b00000000; // デジタル
NzCount = 0; // ノイズ防止回数定義
CWclean = 0; // ノイズを排除したCW信号
Time_mS = 0; // 現時間mS単位
Period = 0; // 前の信号間隔
MarkLen = 0; // Markの長さ
SpaceLen = 0; // Spaceの長さ
state = waitMkStart; // CW 受信のステート
// ------------ Timer 0 設定 ----------------------------
// システムクロック(4MHz/4)をカウントし、
// PS 1/4、8bitMode 1/256で、1.024mSごとにインタラプトする。
// ------------------------------------------------------
T0CON = 0b11010001; // プリスケラ 1/4
TMR0IP = 1; // 高優先割り込み
TMR0IE = 1; // 割り込みを使用する
// UART関連レジスタは以下のように設定される
RCSTA = 0b10000000; // 非同期 8bit、
TXSTA = 0b00100100; // パリティ無
BAUDCON = 0b00001000; // 9600bps
SPBRGH = 0;
SPBRG = 103;
// ----------- 割込み初期化 -----------------------------
RCONbits.IPEN=1; // 2段階の割込に設定
INTCONbits.GIEH=1; // 高優先割込を許可
INTCONbits.GIEL=0; // 低優先割込は禁止
while(CWclean); // Markの終了を待つ
while(1){
switch(state){
case waitMkStart: // ****** Mark 入力を待つ ******
if(CWclean){ // Markを受信したなら
LED=1; // LED ON
state=waitMkEnd; // Markの終了を待つ
}
break;
case waitMkEnd: // ****** Markの終了を待つ ******
if(!CWclean){ // Spaceを受信したなら
LED=0; // LED消灯
MarkLen = Period; // Markの長さを保存
state=waitSpEnd; // Spaceの終了を待つ
}
break;
case waitSpEnd: // ****** Spaceの終了を待つ ******
// Markを受信したなら ------------------------------
if(CWclean){
// 受信済みのMarkとSpaceの継続時間からcodeを解析
// Space継続時間が一文字終了を示すなら
// 文字を送信する
LED=1; // LED ON
mChar = sigToASC(MarkLen,Period); // codeを解析
if(mChar) TXREG = mChar; // 文字完了なら送信
state=waitMkEnd; // Markの終了を待つ
// Space時間が単語閾値を超えているなら -----------------
}else if(Time_mS >= thWord){
// 受信済みのMarkとSpaceの継続時間からcodeを解析
// 受信文字と単語区切り(スペース)を送信する
mChar = sigToASC(MarkLen,Time_mS);
if(mChar) TXREG = mChar; // 文字を送信
TXREG = ' '; // 単語区切りを送信
state=waitMkStart; // 次の文字の開始を待つ
}
break;
default: // ****** 予想外のstateなら ******
if(!CWclean) state=waitMkStart; // Spaceまで待ちリスタート
break;
}
}
}
//****** sigToASC ***************************************
// 以下の共通変数を使用する
// tmpCode ; tmpLen;
// 文字の途中は 戻り値:0
// 文字の終りで 戻り値:ASCIIcode を返す。
//
//***************************************************************
char sigToASC(int M_Len,int S_Len){
unsigned int CWcode;
char i;
if(M_Len < thBar){ // Dot-Barの閾値より短ければ
tmpCode |= (0x8000 >> tmpLen); // Dotの「1」をセット
}
tmpLen++;
if(S_Len < thChar)return 0; // Mark-文字の閾値より短ければ
// 文字の受信途中なので復帰
// -------- 一文字受信完了 ----------------------------------------
CWcode = tmpCode + tmpLen; // 長ければ、CWcodeを組み立てる
tmpCode = 0; // 共通変数を次のために初期化
tmpLen = 0;
//****** codeToASC ****** CWcodeに対応するASCIIを探す
for(i = 0; i < 0x40; i++){ // CWcodeに一致すれば
if(CWcode == m_code[i])break; // ループを途中で抜ける
} // ASCIIはループ定数(i) + 0x20
if(i < 0x40) return i + 0x20; // 対応文字 あり
return 0; // 対応文字 なし
}