モールス信号ON/OFFのパルス幅をPICで測定し、USB経由でPCに送信するプログラムを以前作成ました。

前回までは、PIC16F1619に搭載されているSMTやCLCという周辺機器をなるたけ利用してCPUの負担を軽減したプログラムでした。

今回は、型式の少し古いPICでも実現できるプログラムを作成します。使用部品やPICのピン配置などは前回と同じですが、サイドトーンの周波数や出力フォーマッをPCから変更できるように機能を追加します。

 

 

 

 

 

1 プログラム概要

このプログラムでは以下の周辺モジュールを使用しています。

TMR4
システムクロックをカウントして、1 mSec 毎に割り込みをかけています。この割り込み処理の中で、電鍵のチャタリング防止や ON/OFF 時間の測定を行います。
ESUART
SMTで測定した電鍵信号の On/Off を、mS 単位に変換した文字列をUSBシリアル変換モジュールに送信します。比較的高速の115200 baud で送信します。割り込み処理は使用しません。
TMR2
PWM3と共に機能し、サイドトーンのベースとなります。Fosc (8MHz)を 1/100 し、80kHz のPWM周期で、その5回に一度(16kHz)、波形データを更新します。
 
PWM3
波形データは、正弦波を20分割した値を使用するので、出力波形は、800Hz (16kHz/20)です。
PCからのコマンドで、TMR2 postscaler が、1:4 、1:5 、1:6 と変化するので、出力される周波数数も、666、800、1000Hzから選ぶことができます。

 

2 MPLAB X プロジェクトの作成

この例では、「New Project...」から、CWanalizer という名称のプロジェクトを作成しました。

  • Device: PIC16F1619
  • MPLAB X; v5.45
  • MPLAB XC8; v2.20
  • MCC; v3.95.0

3 システムクロックの設定

MCCの設定から8MHz_HF を選択し、PLL Enabled にレ点を付けます。
Current System Clock が、32 MHz になることを確認します。

 

 

4 TMR4 の設定

Device Resources ウインド内の Libraries list にある Timer icon 展開して TMR4 icon をダブルクリックします。

表示される画面を見ながら、マウスでクリックして設定します。赤で示した設定以外はデフォルトでOKでした。設定後に、Actual Period が 1 ms になることを確認します。

  1. Control Mode : Roll over pulse
  2. Start/Reset Option : Softwear control
  3. Clock Souce : FOSC/4
  4. Prescaler : 1:32
  5. Timer Period : 1 ms
  6. Enable Timer Interrupt にレ点を入れる

 

 

5 EUSART の設定

Device Resources ウインド内の Libraries list にある EUSART icon を展開して EUSART icon をダブルクリックします。

表示される画面を見ながら、マウスでクリックして設定します。

  1. Baud Rate : 115200 を選択
  2. printf 関数を使用できるように、Redirect STDIO にレ点を入れる

6 TMR2 の設定

Device Resources ウインド内の Libraries list にある Timer icon を展開して TMR2 icon をダブルクリックします。モジュールがロードされるとTMR2設定画面が現れます。

表示される画面を見ながら、マウスでクリックして設定します。下記の4項目以外はデフォルトでOKでした。

  1. Clock Source は Fosc/4 を選択
  2. Postscaler は 1:5
  3. Timer Period は 62.5us を入力
  4. Enable Timer Interrupt に レ点を入れる

参考:Postscaler を 1:4 に設定すると、正弦波周波数を 1kHz に変更することができます。

 

 

7 PWM3 の設定

Device Resources ウインド内の Libraries list にある PWM icon を展開して PWM3 icon をダブルクリックします。モジュールがロードされると PWM3 設定画面が現れます。

  1. Duty Cycle  : 100% 
  2. PWM Polarity : active_lo 

これらは、電鍵がOFFになりサイドトーンを止めた時、その出力が Lowレベルになるよう通常とは異なり、反転出力を利用する処置です。

最後に、画面で連携タイマーがにTimer2 が選択されていて、PWM Frequency が 80KHz と表示されいるのを確認します。

 

 

8 入出力の指定

各module がロードされると、Pin Manager に、利用できる 入出力pinが表示されます。以下のように設定します。

  • EUSART RX : RB5
  • EUSART TX : RB7
  • PWM3 PWM3OUT : RC5
  • GPIO input : RC4
  • GPIO input : RC7
  • GPIO output : RA1
  • GPIO output : RA2
  • GPIO output : RA5

 

Pin Module画面で、RC7のWPU(弱プルアップ)にレ点を入れます。

 

 

さらに、Registersタブをクリックし OPTION_REG の nWPUEN を enabled に変更します。

 

9 Code 生成

Generate ボタンをクリックします。

 

10 処理ルーチンを main.c に記載

自動生成された main.c ファイルを開き、内容を下のリストの様に全面変更します。

なお、MPLAB X プロジェクトを下のリンクからダウンロードですることができます。また、プロジェクトをコンパイルした HEXファイル(CWanalizer.hex)も同梱しました。その HEXファイルを MPLAB IPE を使用しCuriosityボードに書き込んでも良いでしょう。

---> CWanalizer.zip

 

/******************************************
 * CW Analizer 
 * 電鍵のOnOff時間を、mSec単位で測定し、PCに送信
 *    115200 Baud 8bits 
 *      Device            :  PIC16F1619
 *      Driver Version    :  2.00
 * 2021-02-15 
********************************************/

#include "mcc_generated_files/mcc.h"


#define verMsg   "v1 2021-02-15"
#define keyToDown LATA5     // Keyが押された時に点灯
#define keyToUp   LATA1     // Keyが離れた時に点灯
#define keyGap    LATA2     // Key操作が中断時に点灯
#define keyIn     RC7       // 電鍵接続ポート
#define cgCount   10        // チャタリング防止する時間
#define LED_on_ms 50        // LED点灯時間

uint8_t keying   = 0;       // 電鍵操作中
uint8_t keyCount = 0;       // 防止時間カウント
uint8_t keyClean = 1;       // チャタリング除去済み整形波形
uint8_t cgToMark  = 0;      // Markへの遷移
uint8_t cgToSpace = 0;      // Spaceへの遷移
uint8_t gapDetected = 0;    // ギャプ開始
uint8_t LEDtimer = 0;       // 点灯時間
uint16_t MK_time = 0;       // Mark継続時間
uint16_t SP_time = 0;       // Space継続時間

uint16_t keyTimer = 0;      // 継続時間測定タイマー
uint8_t modeSep = 0;        // 分離出力モード

// Sin定数 20分割 MAX 99
uint8_t wave[] = {
     97,90,79,65,50,34,20,9,2,0,2,9,20,34,50,65,79,90,97,99
};

uint8_t p_wave;         // wave pointer

void MyTMR2_ISR(void);      // 割り込み処理関数プロトタイプ
void MyTMR4_ISR(void);      // 割り込み処理関数プロトタイプ
void RxOptions(void);

/**********   Main application  **********/
void main(void)
{
    uint8_t nFirstMk = 0;   // 電鍵操作開始
    uint8_t num = 0;        // 符号番号

    SYSTEM_Initialize();                    // デバイス初期化
    TMR2_SetInterruptHandler(MyTMR2_ISR);   // 割り込み処理関数設定
    TMR4_SetInterruptHandler(MyTMR4_ISR);   // 割り込み処理関数設定

    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
    
    printf("CW Analizer option commands:\r\n");
    printf(" 1 : Tone frequency 1000 Hz\r\n");
    printf(" 2 : Tone frequency  800 Hz\r\n");
    printf(" 3 : Tone frequency  667 Hz\r\n");
    printf(" 4 : Output format -> Separate\r\n");
    printf(" 5 : Output format -> Combine\r\n");
    if(modeSep)                     // 開始メッセージ出力
        printf("\r\n");
    else
        printf("\r\n    Mark  Space (mS)\r\n");

    while (1)
    {
        RxOptions();            // Option Command 受信の確認
        if(cgToMark){           // Key 押されたなら
            cgToMark = 0;           // 押されたイベントをOFF
            keyToDown = 1;          // LED 押された を ON
            LEDtimer = LED_on_ms;   // LED点灯時間セット
            if(nFirstMk){           // 最初は出力なし
                if(modeSep)
                    printf("S %5d\r\n",SP_time);
                else
                    printf(" %5d\r\n",SP_time);
            }
            nFirstMk = 1;

        }
        else if(cgToSpace){     // Key 離れたなら
            cgToSpace = 0;          // 離れたイベントをOFF
            keyToUp = 1;            // LED 離れた を ON
            LEDtimer = LED_on_ms;   // LED点灯時間セット
            if(modeSep)
                printf("M %5d\r\n",MK_time);
            else
                printf("%2d %5d ",num++,MK_time);

        }
        else if(gapDetected){   // Key操作 中断なら
            gapDetected = 0;        // GAPイベントをOFF
            keyGap = 1;             // LED GAP を ON
            LEDtimer = LED_on_ms;   // LED点灯時間セット
            nFirstMk = 0;           // 継続フラグリセット
            num = 0;                // 符号番号リセット
            if(modeSep)
                printf("G   ---\r\n");
            else{
                printf("   ---\r\n");
                printf("\r\n");
                printf("    Mark  Space (mS)\r\n");
            }
        }
    }
}

// 割り込み処理関数設定 ---------------------------
// 正弦波を派生させるために定期的に実行される
void MyTMR2_ISR(void){
    PWM3DCH = wave[p_wave];         // 波形定数を更新
    if(p_wave >= 19){               // 零点なら
        if(!RC7) p_wave = 0;        //  SW ON で継続
    }else{                          // 零点以外なら
        p_wave ++;                  //  波形定数を進める
    }
}

// MyTMR4_ISR は、1msごとに実行される
void MyTMR4_ISR(void){
    if(keying)keyTimer++;       // 電鍵操作中ならタイマーを進める
    if(keyTimer > 2000){        // 2秒以上なら
        gapDetected = 1;        //   ギャップ操作終了なので
        keyTimer = 0;           // 電鍵操作中ならタイマーを進める
        keying = 0;             // 電鍵操作の終了
//        return;
    }
    
    if(keyClean){           
        // ######## 整形出力「1」(電鍵がUP)の時 ########
        if(keyCount){   // keyCount 0 以外  Mark確認中なら
            keyCount--;             //   確認時間を進める
            if(!keyCount){          //   確認時間終了なら
                if(!keyIn){         //   入力を再度確認し
                    // -------------     以下の変異処置を実施
                    keyClean = 0;           // チャタリング整形出力を「0」
                    SP_time = keyTimer;  // 経過時間をSP_timeに
                    keyTimer = 0;           // 経過時間をリセット
                    cgToMark = 1;         // 押されたイベントをON
                    keying = 1;             // 電鍵操作の開始
                }
            }
        } else {        // keyCount == 0 電鍵監視中に
            if(!keyIn)              //   電鍵がONになったら
                keyCount = cgCount; //   Mark確認中に移行
        }
        
    }else{  // ######## 整形出力「0」(電鍵がDOWN)の時 ########
        if(keyIn){      // keyIn == 1  電鍵がOFF離れれば
            keyCount--;             //   確認カウントを進め
            if(!keyCount){          //   確認カウント終了で
                // -------------         以下の変異処置を実施
                keyClean = 1;           // チャタリング整形出力を「1」
                MK_time = keyTimer;   // 経過時間をSP_timeに
                keyTimer = 0;           // 経過時間をリセット
                cgToSpace = 1;        // 離れたイベントをON   
            }
        }else{          // keyIn == 0  電鍵がON 押しているなら
            keyCount = cgCount;     //   確認カウントをリセット
        }
    }
    if(LEDtimer){           // LED を一定時間(10mS)後に
        LEDtimer--;         // 消灯させる
        if(!LEDtimer){
            keyToDown = 0;
            keyToUp = 0;
            keyGap = 0;
        }
    }
}

void RxOptions(void)
{
    if(EUSART_is_rx_ready()){   
    // UARTで受信していれば以下を実行する ---------------------
    uint8_t cmd = EUSART_Read();    // 受信したバイトを受け取る
    EUSART_Write(cmd);              // 受信コードをエコーバック
    switch(cmd){                    // コマンドをチェック 
        case '0':                   // 1000Hz 周波数変更
            printf(verMsg);   
            printf("\r\n");   
            break;
        case '1':                   // 1000Hz 周波数変更
            T2CON = 0x83;
            printf(" -> 1000 Hz !!\r\n");   
            break;
        case '2':                   // 800Hz 周波数変更
            T2CON = 0x84;
            printf(" -> 800 Hz !!\r\n");   
            break;
        case '3':                   // 667Hz 周波数変更
            T2CON = 0x85;
            printf(" -> 667 Hz !!\r\n");   
            break;
        case '4':                   // 分離出力フォーマット
            modeSep = 1;
            printf(" -> Separate mode !!\r\n");   
            break;
        case '5':                   // 統合出力フォーマット
            modeSep = 0;    // 
            printf(" -> Combine mode !!\r\n");   
            break;
        default:                    // 以外は、「Fail」送信
               printf(" -> Fail Command!! \r\n");            
               break;
        }
    }
}

/** End of File **/

13 プロジェクトのコンパイルと書き込み

Program アイコンをクリックし、PICにプログラムを書き込みます。

 

 

14 動作の確認

書き込み終了後に、USBシリアル変換器とPCをUSBケーブルで接続し、CoolTermなどの通信ソフトを立ち上げます。通信ソフトの設定画面で

  • 115200 baud, 
  • 8 bit data, 
  • none parity, 
  • 1 stopbit 

に設定し接続を有効にします。

PICをリセットすると、利用可能なコマンドを表示し信号待ちの状態になります。

電鍵のキーを押すとサイドトーンが聞こえ、CoolTerm画面にパルス幅が表示されます。 PCから半角数字の1、2、または、3 を入力すると、トーン周波数数を、1000、800、667Hzから選ぶことができます。4、または、5 を入力すると出力フォーマットを変更することができます。