モールス信号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 になることを確認します。
- Control Mode : Roll over pulse
- Start/Reset Option : Softwear control
- Clock Souce : FOSC/4
- Prescaler : 1:32
- Timer Period : 1 ms
- Enable Timer Interrupt にレ点を入れる
5 EUSART の設定
Device Resources ウインド内の Libraries list にある EUSART icon を展開して EUSART icon をダブルクリックします。
表示される画面を見ながら、マウスでクリックして設定します。
- Baud Rate : 115200 を選択
- printf 関数を使用できるように、Redirect STDIO にレ点を入れる
6 TMR2 の設定
Device Resources ウインド内の Libraries list にある Timer icon を展開して TMR2 icon をダブルクリックします。モジュールがロードされるとTMR2設定画面が現れます。
表示される画面を見ながら、マウスでクリックして設定します。下記の4項目以外はデフォルトでOKでした。
- Clock Source は Fosc/4 を選択
- Postscaler は 1:5
- Timer Period は 62.5us を入力
- Enable Timer Interrupt に レ点を入れる
参考:Postscaler を 1:4 に設定すると、正弦波周波数を 1kHz に変更することができます。
7 PWM3 の設定
Device Resources ウインド内の Libraries list にある PWM icon を展開して PWM3 icon をダブルクリックします。モジュールがロードされると PWM3 設定画面が現れます。
- Duty Cycle : 100%
- 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 を入力すると出力フォーマットを変更することができます。