2025年3月28日金曜日

CW ABC

CWから遠ざかっているので、忘れた感覚を取り戻す為にCW練習機を作った。練習モードは、ABC連続、ABC数字連続、ABC数字ランダムおよびカナ連続、カナランダムから選択できる。LCD0802を使って、5文字表示と打鍵速度表示を行った。マイコンはATiny1614を使って、BCDスイッチ(モード切り替えSW)とRepeat SWは割込み処理にした。









回路図












スケッチ

fabcとfkanaは、ANKコード表による。但しコード表にない文字 ヰは#と表示、ヱはェ
(小さいエ)と表示することにした。ANKコードは下表。
























モード設定はBCD SW行い、速度はボリュームで設定する。
連続モード(sequenceモード)時は、REP_SW操作でリピート開始/リピート中止ができる。

////////////////////////////////////////////////  
//  CW Trainer Ver.1.0
//                                2025/3/31
//                                JA2GQP
////////////////////////////////////////////////

#include <LiquidCrystal.h>

/////////////////////////////
//  Micro define
/////////////////////////////  
#define SP_DO   4                   // SP
#define SPEED   8                   // SPEED
#define freq    700                 // tone freq(Hz)
#define msw_1   7                   // mode SW BIT0  
#define msw_2   6                   //            1
#define msw_4   5                   //            2
#define REP_SW  11

const int rs = 10 , en = 9, d4 = 3, d5 = 2, d6 = 1, d7 = 0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

/////////////////////////////
//  Morse Code & font table
/////////////////////////////
byte acode[36] =
{0x06,0x11,0x15,0x09,0x02,0x14,0x0b,0x10,0x04,0x1e,  // A B C D E F G H I J
  0x0d,0x12,0x07,0x05,0x0f,0x16,0x1b,0x0a,0x08,0x03,  // K L M N O P Q R S T
  0x0c,0x18,0x0e,0x19,0x1d,0x13,0x3f,0x3e,0x3c,0x38,  // U V W X Y Z 0 1 2 3
  0x30,0x20,0x21,0x23,0x27,0x2f};                     // 4 5 6 7 8 9

byte fabc[36]  =
{0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,  // A B C D E F G H I J
  0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,  // K L M N O P Q R S T
  0x55,0x56,0x57,0x58,0x59,0x5a,0x30,0x31,0x32,0x33,  // U V W X Y Z 0 1 2 3
  0x34,0x35,0x36,0x37,0x38,0x39};                     // 4 5 6 7 8 9

byte kcode[50] =
{0x3b,0x06,0x0c,0x3d,0x22,0x12,0x25,0x18,0x1d,0x1f,  // ア イ ウ エ オ カ キ ク ケ コ  
  0x35,0x2b,0x37,0x2e,0x17,0x05,0x14,0x16,0x3a,0x24,  // サ シ ス セ ソ タ チ ツ テ ト
  0x0a,0x15,0x10,0x1b,0x1c,0x11,0x33,0x13,0x02,0x09,  // ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ
  0x19,0x34,0x03,0x31,0x29,0x0e,0x39,0x07,0x08,0x0b,  // マ ミ ム メ モ ヤ ユ ヨ ラ リ
  0x2d,0x0f,0x1a,0x0d,0x2a,0x04,0x2c,0x29,0x26,0x1e}; // ル レ ロ ワ ン " ゜㋼ エ ヲ

byte fkana[50] =
{0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,  // ア イ ウ エ オ カ キ ク ケ コ
  0xbb,0xbc,0xbd,0xbe,0xbf,0xc0,0xc1,0xc2,0xc3,0xc4,  // サ シ ス セ ソ タ チ ツ テ ト
  0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,  // ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ
  0xcf,0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,  // マ ミ ム メ モ ヤ ユ ヨ ラ リ
  0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,0x23,0xaa,0xa6}; // ル レ ロ ワ ン " ゜㋼ エ ヲ

/////////////////////////////
// memory define
/////////////////////////////
int dottime;
int dashtime;
int interval1;
int interval2;
byte mode_val = 0;
byte mode_i = 0;                    // mode IRQ flag
byte rep_i = 0;                     // repeat IRQ flag

//-------- set up --------------------------------------------------//

void setup(){
  pinMode(msw_1, INPUT_PULLUP);
  pinMode(msw_2, INPUT_PULLUP);
  pinMode(msw_4, INPUT_PULLUP);
  pinMode(REP_SW, INPUT_PULLUP);

  attachInterrupt(msw_1,mode_irq,CHANGE);
  attachInterrupt(REP_SW,rep_irq,FALLING);

  randomSeed(100);
  lcd.begin(8, 2);
  lcd.print("CW ABC");
  lcd.setCursor(0, 1);
  lcd.print("ver1.0");
  delay (1000);
  lcd.clear();
  mode_SW();
};

//-------- main ----------------------------------------------------//

void loop(){
  mode_SW();
  lcd.clear();
  lcd.setCursor(0 ,0);

  switch(mode_val){
    case 0:                           // ABC to number sequence
      lcd.print("Seq A09");
      delay(1000);
      HR_BT();
      WPM();
      lcd.setCursor(0, 1);
      seqChar(36);
      break;

    case 1:                           // random ABC
      lcd.print("Ran ABC");
      delay(1000);
      HR_BT();
      WPM();
      lcd.setCursor(0, 1);
      rndChar(26);
      break;

    case 2:                           // ABC to number ramdom
      lcd.print("Ran A09");
      delay(1000);
      HR_BT();
      WPM();
      lcd.setCursor(0, 1);
      rndChar(36);
      break;

    case 3:                           // kana sequence
      lcd.print("Seq kana");
      delay(1000);
      HR_HORE();
      WPM();
      lcd.setCursor(0, 1);
      seqChark(50);
      break;

    case 4:                           // kana ramdom
      lcd.print("Ran kana");
      delay(1000);
      HR_HORE();
      WPM();
      lcd.setCursor(0, 1);
      rndChark(50);
      break;

    case 5:
    case 6:
    case 7:
      break;
    default:
      break;
  }
}

//--------  ABC 0-9 sequence output --------------------------------//

void seqChar(int n){
int cnt=0;                            // counter clear

  while (cnt <n){
    morseTone(acode[cnt]);            // morsecode out
    delay(interval1);                 // wait
    lcd.write(fabc[cnt]);             // A to Z LCD out

    if (( (cnt+1) % 5) ==0){          // 5 dijit check
      delay(300);
      lcd.clear();
      WPM();
      lcd.setCursor(0, 1);

      if(rep_i == 1)
        cnt = cnt - 5;
    }

    cnt++;
    if(mode_i == 1)                   // IRQ check
      break;                          // exit loop
  }
  AR();
  lcd.clear();
}

//-------- random ABC output ---------------------------------------//

void rndChar(int n){
int cnt=0;                              // counter clear

int x;
  while (cnt <80){
    x = random(n);                      // randum code
    morseTone(acode[x]);                // morsecode out
    delay(interval1);                   // wait
    lcd.write(fabc[x]);                 // A to Z LCD out

    if (( (cnt+1) % 5) ==0){            // 5 dijit check
      delay(500);
      lcd.clear() ;
      WPM();
      lcd.setCursor(0, 1);
    }
    cnt++;
    if(mode_i == 1)                     // IRQ check
      break;                            // exit loop
  }
  AR();
  lcd.clear();
}

//--------  kana sequence output -----------------------------------//

void seqChark(int n){
int cnt=0;                              // counter clear

  while (cnt <n){
    morseTone(kcode[cnt]);              // morsecode out
    delay(interval1);                   // wait
    lcd.write(fkana[cnt]);              // kana LCD out

    if (( (cnt+1) % 5) ==0){            // 5 dijit check
      delay(300);
      lcd.clear() ;
      WPM();
      lcd.setCursor(0, 1);

      if(rep_i == 1)
        cnt = cnt - 5;
    }
    cnt++;
    if(mode_i == 1)                     // IRQ check
      break;                            // exit loop
  }
  SN();
  lcd.clear();
}

//-------- kana random output --------//

void rndChark(int n){
int cnt=0;                              // counter clear
int x;
  while (cnt <80){
    x = random(n);                      // randum code
    morseTone(kcode[x]);                // morsecode out
    delay(interval1);                   // wait
    lcd.write(fkana[x]);                // kana LCD out

    if (( (cnt+1) % 5) ==0){            // 5 dijit check
      delay(500);
      lcd.clear() ;
      WPM();
      lcd.setCursor(0, 1);
    }
    cnt++;
    if(mode_i == 1)                     // IRQ check
      break;                            // exit loop
  }
  SN();
  lcd.clear();
}

//-------- mode SW -------------------------------------------------//

void mode_SW(){
  mode_val = 0;
  if(digitalRead(msw_1) == LOW)
    mode_val = mode_val + 1;
  if(digitalRead(msw_2) == LOW)
    mode_val = mode_val + 2;
  if(digitalRead(msw_4) == LOW)
    mode_val = mode_val + 4;
  mode_i = 0;
}

//-------- mode IRQ ------------------------------------------------//

void mode_irq(){
  mode_i = 1;
}

//-------- ESC IRQ ------------------------------------------------//

void rep_irq(){
  delay(50);
  rep_i = rep_i ^ 1;
}

//-------- HR HR BT code output ------------------------------------//

void HR_BT(){
  lcd.clear();
  WPM();
  lcd.setCursor(0 ,1);

  HR();
  HR();
  lcd.print("BT");
  morseTone(0x11);                    // B code output
  morseTone(0x03);                    // T code output
  delay(interval2 *2);
  lcd.clear();
}

//-------- HR code output ------------------------------------------//

void HR(){
  lcd.print("HR ");
  morseTone(0x10);                    // H code output
  delay(interval1);
  morseTone(0x0a);                    // R code output
  delay(interval1);
}

//-------- AR code output ------------------------------------------//

void AR(){
  morseTone(0x2a);
  delay(interval1);
}

//-------- ラタ code output ------------------------------------------//

void SN(){
  morseTone(0x28);
  delay(interval1);
}

//-------- code speed ----------------------------------------------//

void code_speed(){
  dottime = analogRead(SPEED) / 5;
  dashtime = dottime * 3;
  interval1 = dottime * 2;
  interval2 = dottime * 4;
}

//-------- code tone output ----------------------------------------//

void morseTone(int m){
  while (m != 1){
    if ((m & 1) == 0){
      tone(SP_DO,freq,dottime);         // dot
      delay(interval1);
    }
    else{
      tone(SP_DO,freq,dashtime);        // dash
      delay(interval2);
    }
    m = m >> 1;                         // 1bit shift right
  }
}

//-------- WPM -----------------------------------------------------//

void WPM(){
  code_speed();
  lcd.setCursor(0, 0);
  lcd.print("WPM");
  lcd.print(2400 / interval1);
  lcd.setCursor(7, 0);
  lcd.print(mode_val);
}

//-------- HR HR ホレ code output ------------------------------------//

void HR_HORE(){
  lcd.clear();
  WPM();
  lcd.setCursor(0 ,1);

  HR();
  HR();
  morseTone(0x09);                      // ホ code output
  morseTone(0x0f);                      // レ code output
  lcd.write(0xce);
  lcd.write(0xda);
  delay(interval2 *2);
  lcd.clear();
}

ダウンロード
スケッチ、回路図はJA2GQP's Download siteCWフォルダーからダウンロードできる。