---
---
アマチュア無線のCW モールス信号は、なかなか正確な符号を送信するのが難しいものです。自分で送っている信号を耳で聞くだけでは、どこがどう不正確なタイミングなのか客観的に評価することができません。以前、PIC16F1619を使い同様のプログラムを作成しました。この時は、市販のUSBシリアル変換モジュールを利用しPCとUSB通信を行っています。今回は、PIC16F1459内臓のUSBモジュールを使用するためごく少ない部品でPIC16F1619での機能を実現できました。
制作タイミングの関係で下の写真には本来必要のない部品の写っています。
<回路図>
今回のプロジェクトは、[ USB - CDC basic MCC ]をベースに、PIC16F1619 [ モールス練習機 ]で開発したプログラムを移植したものです。
内臓USBモジュール以外には、システムクロックをカウントし、1m Sec 毎に低優先割込みをかけて、電鍵のチャタリング防止や ON/OFF 時間の測定を行歌めにタイマー2モジューを使用しています。
作成したプロジェクトは以下からダウンロードすることができます。
モールス練習機プロジェクト B11_cdc_CW.zip |
専用ソフト, 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
*/