---
---
モールス信号発信器は、マイコンなどからシリアル通信で文字を受け取り、その文字に対応するモールス信号を圧電スピーカーから発生させるものです。 受け取った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 のフラグをクリア }