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

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
USB接続モールス練習機
2021-05-01

アマチュア無線のCW モールス信号は、なかなか正確な符号を送信するのが難しいものです。自分で送っている信号を耳で聞くだけでは、どこがどう不正確なタイミングなのか客観的に評価することができません。以前、PIC16F1619を使い同様のプログラムを作成しました。この時は、市販のUSBシリアル変換モジュールを利用しPCとUSB通信を行っています。今回は、PIC16F1459内臓のUSBモジュールを使用するためごく少ない部品でPIC16F1619での機能を実現できました。

制作タイミングの関係で下の写真には本来必要のない部品の写っています。

プログラム概要

<回路図>

今回のプロジェクトは、[ USB - CDC basic MCC ]をベースに、PIC16F1619 [ モールス練習機 ]で開発したプログラムを移植したものです。

内臓USBモジュール以外には、システムクロックをカウントし、1m Sec 毎に低優先割込みをかけて、電鍵のチャタリング防止や ON/OFF 時間の測定を行歌めにタイマー2モジューを使用しています。

作成したプロジェクトは以下からダウンロードすることができます。

モールス練習機プロジェクト B11_cdc_CW.zip

動作の確認

  1. PCとPICをUSBケーブルで接続します。
    回路を初めてPCのUSBに接続すると、自動的にUSBのドライバーがインストールされますが、インストールには、この機器の定義ファイルが必要です。
    Microchip社のCDCソフトウエアをそのまま使用しているので、Microchip社の定義ファイルを使用してください。
  2. 専用ソフト, Dynamic CDC Demo
    PCに、プロジェクトフォルダに同梱されているdynamic_cdc_demo.exeをダブルクリックで実行します。
    右図のウインドが開きます。
    ファイルは、以下のディレクトリに保存されています。
     B02_CDC_basicRev01
     └ ¥utilities
      └ ¥bin
       └ dynamic_cdc_demo.exe
  3. 動作
    PICのRB5に接続された電鍵を打つと圧電スピーカからサイドドーンが出ると共に。PC上に電鍵操作の状況が ms単位で表示されます。mark が電鍵を押している時間、space が離れている時間です。2秒以上離れていると文章が終了したと判断して「 - - - 」を表示します。

専用ソフト, Dynamic CDC Demo 以外のCoolTermなどの通信ソフトでも出力を受け取ることができます。
通信ソフトの設定画面で

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

モールス送信した信号のパルス幅がms単位の一覧形式で表示されます。

プログラム

プロジェクトのプログラムのうち main.c だけを掲載します。他のプログラムは、ダウンロードしたファイルでご確認ください。

<main.c>

/** USB CDC モールス練習機
     Device            :  PIC16F1459
     XC8 Compiler      :  Version 2.10
     MCC               :  Version 4.0.2
     MPLAB X IDE       :  Version 5.45
 *   CDC Serial number strings : 10
*/

#include "mcc_generated_files/mcc.h"
#include <stdio.h>
#include <string.h>

#define keyIn     RB5                   // 電鍵入力ポート
#define sp_LAT    LATCbits.LATC5        // スピーカー出力ポート
#define tone_ON() TRISCbits.TRISC5=0    // スピーカー出力 ON
#define toneOFF() TRISCbits.TRISC5=1    // スピーカー出力 OFF
#define MonCnt     20                   // 電鍵チャタリング防止の監視回数

static uint8_t readBuffer[64];
static uint8_t writeBuffer[64];

uint16_t keyTimer = 0;    // Keying継続時間
uint16_t MK_time  = 0;    // Mark継続時間
uint16_t SP_time  = 0;    // Space継続時間
uint8_t  keyClean = 1;    // クリーンkey信号
uint8_t  numMark  = 1;    // Markの番号
bool     cgToMark = 0;    // 「押した」フラグ
bool     cgToSpace = 0;   // 「離した」フラグ
bool     gapDetected = 0; // Gapの検出フラグ
bool     onGoing = 0;     // 継続中フラグ
bool     modeDsp = 1;     // 表示モード

// ユーザー関数のプロトタイプ
void MCC_USB_CDC_DemoTasks(void);
void MyTMR2_ISR(void);

// ユーザーの割込み関数 ########################################
//   TMR2 から 1ms 毎に割り込みがかかる
// #########################################################
void MyTMR2_ISR(void){
    static uint8_t keyMonCnt = 0; // チャタリング監視回数

    // Key時間計測タイマー処理 ---------------------
    if(onGoing)keyTimer++;      // 電鍵操作中ならタイマーを進める
    if(keyTimer > 2000){        // 2秒以上なら
        gapDetected = 1;        //   ギャップ操作終了なので
        keyTimer = 0;           // 電鍵操作中ならタイマーを進める
    }
    // チャタリング防止 Key-up で keyClean=1 ---------------------
    if(keyMonCnt == 0){             // keyが安定状態時
        if(keyClean != keyIn)           // keyの状態変更で
            keyMonCnt = MonCnt;         // 監視カウントをセット
    }else{                          // チャタリング監視中に
        if(keyClean != keyIn){          // key状態が変化のままなら
            keyMonCnt--;                //   変化継続を20回確認
            if(keyMonCnt == 0){         // 監視期間終了で
                keyClean = keyIn;       // 新しい状態を反映
                if(keyClean){           // upなら
                    cgToSpace = 1;      //  「離した」を[1]
                    MK_time = keyTimer; //   Mark時間を記録
                }else{                  // downなら
                    cgToMark = 1;       //  「押した」を[1]
                    SP_time = keyTimer; //   Space時間を記録
                }
                keyTimer = 0;           // Timerリセット
            }
        }else{                          // keyが元に戻ったら
            keyMonCnt = 0;              //   keyが安定状態を維持
        }
    }
    // サイドトーン -------------------------
    sp_LAT ^= 1;                // 1ms毎に反転 = 500Hz
    if(keyIn)toneOFF();         // key を叩くと
        else tone_ON();         //     サイドトーンが出る
    // サイドトーン END ---------------------
}

/*  Main application ****************** */
void main(void)
{
    SYSTEM_Initialize();

    // ユーザーの割り込み関数を指定する
    TMR2_SetInterruptHandler(MyTMR2_ISR);

    // USB割り込み モードをMCCでの設定時に選択しているため
    // 割り込みを有効にします
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
        MCC_USB_CDC_DemoTasks();
    }
}

// ユーザーの関数 ############################################
//   TMR2 から 1ms 毎に割り込みがかかる
// #########################################################
void MCC_USB_CDC_DemoTasks(void)
{
    // USBデバイスがまだ構成されていない場合、
    // 通信するホストがないため、何もせずにwhileループに戻ります。
    if( USBGetDeviceState() < CONFIGURED_STATE ) return;

    // 現在中断(suspended)の場合は、リモートウェイクアップを発行する必要があるか
    // 確認する必要があります。また、現在ホストと通信していないため、どのような
    // キーボードコマンドも処理できません。whileループに戻ります。
    if( USBIsDeviceSuspended()== true ) return;

    // 電鍵関連の処理
    if( USBUSARTIsTxTrfReady() == true){
        if(cgToMark){                   // Key 押されたなら
            cgToMark = 0;                   // 押されたイベントをOFF
            if(onGoing){                    // 最初は時間の出力なし
                if(modeDsp){
                    sprintf(writeBuffer," %5d\r\n",SP_time);
                    putUSBUSART(writeBuffer,8);
                }else{
                    sprintf(writeBuffer,"S %5d\r\n",SP_time);
                    putUSBUSART(writeBuffer,9);
                }
            }else{                          // 初回はタイトル表示
                if(modeDsp){                // 組み合わせモードのみ
                    sprintf(writeBuffer,"    Mark  Space (mS)\r\n");
                    putUSBUSART(writeBuffer,22);
                }
            }
            onGoing = 1;                    // 継続中フラッグセット
        }
        else if(cgToSpace){             // Key 離れたなら
            cgToSpace = 0;                  // 離れたイベントをOFF
            if(modeDsp){
                sprintf(writeBuffer,"%2d %5d ",numMark++,MK_time);
                putUSBUSART(writeBuffer,9);
            }else{
                sprintf(writeBuffer,"M %5d\r\n",MK_time);
                putUSBUSART(writeBuffer,9);
            }
        }
        else if(gapDetected){           // Key操作 中断なら
            gapDetected = 0;                // GAPイベントをOFF
            onGoing = 0;                    // 継続フラグリセット
            numMark = 1;                    // 符号番号リセット
            if(modeDsp){
                sprintf(writeBuffer,"   ---\r\n\r\n");
                putUSBUSART(writeBuffer,10);
            }else{
                sprintf(writeBuffer,"G   ---\r\n");
                putUSBUSART(writeBuffer,9);
            }
        }
    }
    CDCTxService();

    // CDCドライバーの送信の準備ができていることを確認
    if( USBUSARTIsTxTrfReady() == true)
    {
        uint8_t i;
        uint8_t numBytesRead;

        numBytesRead = getsUSBUSART(readBuffer, sizeof(readBuffer));

        // 送信バッファをクリア後、すべての受信バイトに対し以下を繰り返す
        writeBuffer[0] = '\0';
        for(i=0; i<numBytesRead; i++){
            switch(readBuffer[i]) {
                // Vコマンドを受け取った場合は、Versionコードを返送
                case 'v':
                case 'V':
                    sprintf(writeBuffer,"%sV iwa2104\r\n",writeBuffer);
                    break;
                // 4,5 コマンドを受け取った場合は、表示モードを変更
                case '4':
                    modeDsp = 0;
                    sprintf(writeBuffer,"%s4 Separated format\r\n",writeBuffer);
                    break;
                case '5':
                    modeDsp = 1;
                    sprintf(writeBuffer,"%s5 Combined format\r\n",writeBuffer);
                    break;
            }
        }
        // 受信したすべてのデータを処理した後、返送データを送信する
        if(numBytesRead > 0) {
            putUSBUSART(writeBuffer,strlen(writeBuffer));
        }
    }

    // デバイスからホストへのトランザクションを処理します。
    // CDCTxService()を定期的に呼び出さないと、
    // データがUSBホストに送信されません
    CDCTxService();
}

/**
 End of File
*/