---
---
モールス信号発信器は、マイコンなどからシリアル通信で文字を受け取り、その文字に対応するモールス信号を圧電スピーカーから発生させるものです。 受け取ったASCII文字は、プログラムメモリにあるm_Code一覧でモールス信号の断続音を表すmCodeに変換しています。
| 文字 | モールス | mCode |
|---|---|---|
| --- 長短点 --- 長さ | ||
| A | ・- | 1000 0000 0000 0010 |
| B | -・・・ | 0111 0000 0000 0100 |
| C | -・-・ | 0101 0000 0000 0100 |
<mCodeとは>
このプログラムのために開発したモールス信号固有のコードで、16bitで構成しています。
16bitのうちの
上位12bitが、長点[0]、短点[1の区別]を表し、
下位 4bitが、符号の長さを表します。
シリアルデータの受信は割込みで処理され、一時的にTempBufに保存されます。プログラムのメインループでは、TempBuf にデータが保存されているかを確認し、保存されていれば、RxBufにそのデータを移動した後に、一文字ずつ処理していきます。 まず、受信文字は、mCode変換ルーチンで英文小文字を英文大文字にしてから対応するmCodeに変換します。mCodeの左端のビットから順にモールス信号の短点、長点を処理しモールス音を発生さます。以下に、処理のフローチャートを示します。プログラムのソースリストと見比べてください。
<プログラム>
//--------------------------------------------------------------------
// <モールス信号発信> 20130328 xM03_genStateSq リングバッファ
// UART 9600bps で 通信文を受領 小文字は大文字に自動変換される。
// 通信速度は、短点 100mS固定となっているが、DotLen の 値を
// 変更することで、任意に設定できる。
// PWMで 1KHz、duty 50%の方形波を出力する。
//
// Notes: 4MhzM内部クロック
// RB4 SW1入力(弱プルアップ)未使用
// RB5 UART入力
// RB6 SW2入力(弱プルアップ)連続Key
// RC0 signalOut ロジックレベル
// RC1 signalOut 負ロジックレベル
// RC5 AudioOut P1A 音声出力
// Language: MPLAB xc8 Target: PIC18F14K50
//--------------------------------------------------------------------
// コメント修正
//--------------------------------------------------------
#include <xc.h>
//------------- 通信速度 ---------------------------------------------
// 時間間隔 mSec単位で指定する
#define DotLen 100 // 短点の長さ
#define BarLen 300 // 長点の長さ
#define markSp 100 // マーク間の長さ
#define charSp 300 // 文字間の長さ
#define wordSp 700 // 単語間の長さ
//--------------------------------------------------------------------
#define maxBuf 80 // シリアル受信バッファ数
#define portAudio TRISCbits.TRISC5
#define portRC0 LATCbits.LATC0
#define portRC1 LATCbits.LATC1
#define SW1 PORTBbits.RB4
#define SW2 PORTBbits.RB6
#define TimeUp TMR0IF
//-------------- 制御ステート --------------------------------
#define waitCommand 0
#define markOut 1
#define waitMarkEnd 2
#define waitSpEnd 3
#define waitNextChar 4
//-------------- コンフィグレーション --------------------------------
#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
//-------- プロトタイプ ----------------------------------------------
void delaySet(unsigned int);
void MarkOn(void);
void MarkOff(void);
unsigned int Ch_mCode(char);
char sChar(void);
// -------------- 共通変数 変数 --------------------------------------
char RxBuf[maxBuf]; // Rx受信バッファ
char ptrRx; // Rx書込みポインタ
char ptrIn; // Rx読込みポインタ
char state; // モールス発信ステート
//---------------- m_Code定数 ----------------------------------------
// 16bitの上位12bitが、長[0]、短[1]を表し、
// 下位 4bitが、符号の長さを表す。
// 並び順は、ASCIIコードと同じ
//--------------------------------------------------------------------
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, 0,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, 0, 0, 0, //XYZ[\]^_
};
//-----------------シリアルデータ文字取得---------------------------
// RxBufに文字がある場合は、その文字を返す
// ない場合は、「0」を返す。
// 使用している共通変数 ptrIn ptrRx RxBuf[]
// 使用している定義 maxBuf
//--------------------------------------------------------------------
char gChar(void){
char ch;
RCIE = 0; // 受信割込み不許可
if(ptrIn != ptrRx){ // 受信データがあるなら
ch = RxBuf[ptrIn]; // 戻り値のデータを取得
if(++ptrIn >= maxBuf) ptrIn = 0; // ポインタを+1
}else{ // バッファが空なら
ch = 0; // 戻り値を「0」にする
}
RCIE = 1; // 受信割込み許可
return ch;
}
//-----------------低優先割込みの処理---------------------------------
// 低優先割込みはUARTに使用
//--------------------------------------------------------------------
void interrupt low_priority Lo_Isr(void){
// -------------- UART からの受信 --------------------------------
char RxData;
char tpPtr;
if(RCIF){
RxData = RCREG;
if(OERR){ // Overrunエラーなら
CREN=0; // エラーをクリア
CREN=1; //
}else{ // 通常受信なら
tpPtr = ptrRx; // ポインタを保存
if(++ptrRx >= maxBuf) ptrRx = 0; // ポインタを+1
if(ptrRx != ptrIn){ // バッファに空きがあるなら
RxBuf[tpPtr] = RxData; // データを保存
}else{ // バッファフルなら
ptrRx = tpPtr; // ポインタを戻し
} // データを破棄
}
} // UART RxIF}
}
//----------------------メインプログラム------------------------------
void main(void){
unsigned int mCode; // モールス発信用コード
char mLength; // 該当コードのマーク数
char mChar; // 送信する文字コード
OSCCON = 0b01010010; // 内部クロック4Mhz
LATC = 0b00000010; // PortC RC2,3,4,5出力
TRISC = 0b11000000; // PortC RC2,3,4,5出力
ANSEL = 0b00000000; // デジタル
ANSELH = 0b00000000; // デジタル
WPUB4 = 1; // RB4を弱プルアップ
WPUB6 = 1; // RB6を弱プルアップ
RABPU = 0; // 弱プルアップを有効化
// --------------- Timer0 の 設定 ------------------------------------
// システムクロック(4Mhe/4)をカウントする
// Timer0のPreSに16がセットされるので16uSecのカウント
// フルカウントで 1.1 Sec
// -------------------------------------------------------------------
T0CON = 0b10000011;
// ----------- CCP PWM の 設定 (1KHz Duty 50% 方形波発生で使用)------
// システムクロック(4Mhe/4)をTimer2でカウントする。PSが1/4
// PR2に249がセットされるので1KHzの繰返しとなる。
// CCPおよびTimer2関連レジスタは以下のように設定される
//--------------------------------------------------------------------
CCP1CON = 0b00001100; // PWM_MODE_1、SINGLE_OUT
PSTRCON = 0b00001111; // PWM出力はP1A,B,C,Dピン
CCPR1L = 125; // PWMにDutyをセット
T2CON = 0b00000101; // プリスケラ 1/4,ポストスケラ 1/1
PR2 = 249; // プリセット値 249
TMR2IE = 0; // 割込みOFF
TMR2IP = 1; // 割込みを高優先に設定
TMR2IF = 0; // 割込みフラッグをクリア
// ----------- UART 初期化 -----------
RCSTA = 0b10010000; // UART送受信を有効
TXSTA = 0b00000100; // 8Bit非同期受信のみ
BAUDCON = 0b00001000; // HI-16Bitボーレート
SPBRGH = 0; // 103
SPBRG = 103; // 9600ボー@4MHz
RCIE = 1; // 受信割込み許可
RCIP = 0; // 受信低優先割込
// ----------- 変数 初期化 -----------
ptrRx = 0; // Rx暫定書込みポインタ
ptrIn = 0; // Rx受信文字ポインタ
state = waitCommand; // モールス発信ステート
// ----------- 割込み初期化 -----------------------------
IPEN=1; // 2段階の割込みに設定
GIEH=1; // 高優先割込みを許可
GIEL=1; // 低優先割込みを許可
// --------------- 開始の合図信号 ------------------------------------
portAudio = 0; // AudioPortを出力に設定 (Mark)
delaySet(DotLen); // 短点の長さをセット
while(!TimeUp);
portAudio = 1; // PortをHi-Impedance (Space)
// --------------- 繰返し動作 ----------------------------------------
while(1){
switch(state){
case waitCommand: // ---- 処理指示を待つ ---------------------
if(SW2 == 0){ // SW2 Pushで
MarkOn(); // マークの開始
}else{ // 離すと
MarkOff(); // マークの終了
}
mChar = gChar(); // 一文字取得する
if(mChar == 0) break; // 空なら繰返し待つ
mCode = Ch_mCode(mChar); // 文字をmCodeに変換
if(mCode){ // 有効文字なら
mLength = (char)(mCode & 0xF); // mLengthを分離
state = markOut; // codeの処理をする
}else{ // スペースか無効文字なら
delaySet(wordSp); // 単語間 Space 送出
state = waitNextChar; // 次の文字を処理する
}
break;
case markOut: // ----- Markの処理 -------------------------------
if(mCode & 0x8000){ // Markを確認、短点なら
delaySet(DotLen); // 短点の長さをセット
}else{ // 長点なら
delaySet(BarLen); // 長点の長さをセット
}
MarkOn();
mCode <<= 1; // 次のMarkの準備のため
mLength--; // mCodeをシフト
state = waitMarkEnd; // Markの終了待ち
break;
case waitMarkEnd: // ----- Markの終了を待つ ----------------------
if(TimeUp){ // Timer終了を待ち
MarkOff(); // MarkをOFFにし
if(mLength){ // code処理の途中なら
delaySet(markSp); // Mark間のSpaceセット
state = waitSpEnd; // 次のMarkの処理
}else{ // code処理終了したなら
delaySet(charSp); // 文字間の
state = waitNextChar; // 次の文字の処理
}
}
break;
case waitSpEnd: // ----- Mark間の終了を待つ ----------------------
if(TimeUp) state = markOut; // Timer終了を待ち
break; // 次のmarkの処理
case waitNextChar: // ----- 文字間の終了を待つ -----------------------
if(TimeUp) state = waitCommand; // Timer終了を待ち
break; // 次の文字の処理
default: // ----- 予想外のstateなら ------------------------
state = waitCommand; // コマンド待
break;
} // end Switch
} // end while
} // end main
//------ モールス出力ポート制御 --------------------------------------
// Audio、ロジック出力のOn、Off
//--------------------------------------------------------------------
void MarkOn(void){
portAudio = 0; // 音声 ON (Mark)
portRC0 = 1; // 出力Hi (正論理)
portRC1 = 0; // 出力Low(負論理)
}
void MarkOff(void){
portAudio = 1; // 音声OFF(Space)
portRC0 = 0; // 出力Low(正論理)
portRC1 = 1; // 出力Hi (負論理)
}
//-------- ASCII文字を mCodeに変換 -----------------------------------
// 小文字は大文字に変換してから、m_code 一覧表を参照
// BS CRは、特殊文字として別途変換し、mCodeを戻り値とする
// 戻り値0 は、非該当文字またはスペース
//--------------------------------------------------------------------
unsigned int Ch_mCode(char ch){
if(ch==0x08) return 0xFF08; // BS は、訂正符号
if(ch==0x0D) return 0xAC06; // CR は、段落符号
if((ch>=0x20) && (ch<0x60)) // 数字および英文字は
return m_Code[ch - 0x20]; // 一覧表で変換
if((ch>=0x60) && (ch<0x7B)) // 英小文字は大文字にし
return m_Code[ch - 0x40]; // 一覧表で変換
return 0; // 対象外は「0」
}
// --------------- delaySet --------------------
// mS単位の遅延を行う
// Timer0の1カウント16uSecでフルカウントまでの時間が、
// 指定時間になるよう計算し設定している
// TimeUp(TMR0IF)は、メインのルーチン内でモニタしている
// ---------------------------------------------
void delaySet(unsigned int num){
num = 0x10000 - (long)num * 1000 / 16; // 設定カウントを計算
TMR0H = num >> 8; // Timer 0 にセット
TMR0L = num & 0xFF; // Timer 0 にセット
TimeUp = 0; // Timer 0 のフラグをクリア
}