PIC18F14K50の基本動作から応用プログラムまでを学びます。

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
モールス解析器

モールス信号解析器は、アマチュア無線での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;                            // 対応文字 なし
}