Arduinoで作っちゃいました ~ CW練習機(パート1) ~

2025年3月4日火曜日

t f B! P L

 

Arduinoで作っちゃいました  CW練習機(パート1)

 1.   はじめに

 20248月 船橋市アマチュア無線クラブのArduinoの講習会参加して、生まれて始めてArduinoに接しプログラミングが非常に楽にできることを知りました。
それ以来、Arduinoにハマってハマって抜けられなくなってしまいました。
(講習会と機材の準備してくださいました講師のFQA松本さん、ありがとうございました)

これからArduinoで製作したものをご紹介していきたいと思います。

2.   最初に作るものは、やっぱりCW練習機

まずは、CWの練習機から。パート1として、音を鳴らすだけの基本機能を作ります。 (Lチカや、Tone()関数、digitalRead(), digitalWrite()などの説明は省略します)

 プログラムの骨子は、

void loop() {
  int key;
  // 電鍵(Key)の状態が変化したら、Toneをon/offする
  key = check_Key_status();
  if (key == BtnOnEdge) {  // Key On Edge
    tone(SPK_PIN, Pitch);  // 音を出す
  }
  if (key == BtnOffEdge) {  // Key Off Edge
    noTone(SPK_PIN);        // 音を止める
  }
}

です。

ここで、check_Key_status()は電鍵のOn/Offの状態変化を見張っています。この中身を確認していきます。

3. 接点(ボタン/Key)の状態監視の方法 

ところで、電鍵やボタン、スイッチの接点にはチャタリングがありますので、対策が必要です。
お勧めは、アナログCR積分回路を模したデジタル積分方式です。 

(アナログCR積分によるチャタリング対策)





(デジタル積分によるチャタリング対策)





デジタル積分によるチャタリング対策のプログラム例

このプログラムは、Btnの状態(OnかOffか)を返します。
(ボタン番号1が、GPIO Btn1_PINに接続されている前提)

/// ボタン1(Btn1) ///
static int Btn1Cnt = 0;
static int Btn1Status = Off;  // normally off

int check_Btn1_status() {
  if (digitalRead(BTN1_PIN) == HIGH) {  // switch off
    Btn1Cnt--;
    if (Btn1Cnt < 0) {
      Btn1Cnt = 0;
      Btn1Status = Off;
    }
  } else {  // switch on
    Btn1Cnt++;
    if (Btn1Cnt > threshold) {
      Btn1Cnt = threshold;
      Btn1Status = On;
    }
  }
}

ヒステリシス特性を決めるThresholdの値は、接点の特性や反応時間、ループの速さによって調整します。(3-100の間くらい)
この方式のメリットは、チャタリング終了判定までの間タイマーで(10msecなど)待つ必要がなく、ループで回っている途中でチェックに入れば良い(他の仕事ができる)という点です。

 

 これを踏まえて、電鍵(Key)On/Offの状態変化を監視するcheck_Key_status()を作成します。

/// 電鍵(KEY) ///
static int KeyCnt = 0;
static int KeyStatus = Off;  // normally off

int check_Key_status() {
  if (digitalRead(KEY_PIN) == HIGH) {  // switch off
    KeyCnt--;
    if (KeyCnt < 0) {
      KeyCnt = 0;
      if (KeyStatus == On) {
        KeyStatus = Off;
        return (BtnOffEdge);  // Key Off Edge
      } else {
        return (NoChange);  // no change
      }
    }
  } else {  // switch on
    KeyCnt++;
    if (KeyCnt > threshold) {
      KeyCnt = threshold;
      if (KeyStatus == Off) {
        KeyStatus = On;
        return (BtnOnEdge);  // Key On Edge
      } else {
        return (NoChange);  // no change
      }
    }
  }
}

さらに、モールス信号の音程(pitch)を変えるボタン(updown)をloop()に追加してみました。

void loop() {
  int key;
  // 電鍵(Key)の状態が変化したら、Toneをon/offする
  key = check_Key_status();
  if (key == BtnOnEdge) {  // Key On Edge
    tone(SPK_PIN, Pitch);  // 音を出す
  }
  if (key == BtnOffEdge) {  // Key Off Edge
    noTone(SPK_PIN);        // 音を止める
  }
//// 以下を追加 ////
// ボタン1 (音程up)
  if (check_Btn1_status() == BtnOnEdge) {  // Btn On Edge
    setTone(+50);                          // 音程(Pitch)を上げる
  }
  // ボタン2 (音程down)
  if (check_Btn2_status() == BtnOnEdge) {  // Btn On Edge
    setTone(-50);                          // 音程(Pitch)を下げる
  }
}

()上の例では、音程up/downのボタンによる動作(Action)をボタンを押したタイミング(Btn On Edge)としていますが、長押し判定などをする場合は、ボタンを離すタイミング(Btn Off Edge)を使用します。

 4. まとめ

以上をまとめると、全体のプログラムは以下になります。

/////////////////////////////
// Blogger CW練習機 (その1) //
//  20250305 by JL1BDL     //
/////////////////////////////

/// ハードウェアの接続定義 ///
#define KEY_PIN 4  // 電鍵(Key)を接続
#define BTN1_PIN 9  // ボタン1
#define BTN2_PIN 12  // ボタン2
#define SPK_PIN 8  // スピーカー

#define Off 0
#define On 1

// KEYやボタンの状態変化 //
#define BtnOnEdge 1
#define BtnOffEdge 2
#define NoChange 0

/// 共通変数 ///
int Pitch = 600;  // 音程(Pitch)のデフォルト値 600Hz

void setup() {
  // 各ピンの設定
  pinMode(KEY_PIN, INPUT_PULLUP);  // straight key
  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);
}


void loop() {
  int key;
  // 電鍵(Key)の状態が変化したら、Toneをon/offする
  key = check_Key_status();
  if (key == BtnOnEdge) {  // Key On Edge
    tone(SPK_PIN, Pitch);  // 音を出す
  }
  if (key == BtnOffEdge) {  // Key Off Edge
    noTone(SPK_PIN);        // 音を止める
  }
  // ボタン1 (音程up)
  if (check_Btn1_status() == BtnOnEdge) {  // Btn On Edge
    setTone(+50);                          // 音程(Pitch)を上げる
  }
  // ボタン2 (音程down)
  if (check_Btn2_status() == BtnOnEdge) {  // Btn On Edge
    setTone(-50);                          // 音程(Pitch)を下げる
  }
}

// 音程変更 (300-1200Hz)
void setTone(int n) {
  Pitch = Pitch + n;
  if (Pitch < 300) Pitch = 300;
  if (Pitch > 1200) Pitch = 1200;
  tone(SPK_PIN, Pitch, 100);  // 音程を変更したら、ピッと鳴らす
}

///////////////////////////////////////////////////////
// 以下は、電鍵(Key)、ボタン1,、ボタン2の入力チェック   //
//  On/Offではなく、on/offの切り替え(Edge)を返す       //
//      BtnOnEdge =1, BtnOffEdge= 2, NoChange = 0    //
///////////////////////////////////////////////////////

int threshold = 10;  // thresholdは、全ボタン共通とした

/// 電鍵(KEY) ///
static int KeyCnt = 0;
static int KeyStatus = Off;  // normally off

int check_Key_status() {
  if (digitalRead(KEY_PIN) == HIGH) {  // switch off
    KeyCnt--;
    if (KeyCnt < 0) {
      KeyCnt = 0;
      if (KeyStatus == On) {
        KeyStatus = Off;
        return (BtnOffEdge);  // Key Off Edge
      } else {
        return (NoChange);  // no change
      }
    }
  } else {  // switch on
    KeyCnt++;
    if (KeyCnt > threshold) {
      KeyCnt = threshold;
      if (KeyStatus == Off) {
        KeyStatus = On;
        return (BtnOnEdge);  // Key On Edge
      } else {
        return (NoChange);  // no change
      }
    }
  }
}

/// ボタン1(Btn1) ///
static int Btn1Cnt = 0;
static int Btn1Status = Off;  // normally off

int check_Btn1_status() {
  if (digitalRead(BTN1_PIN) == HIGH) {  // switch off
    Btn1Cnt--;
    if (Btn1Cnt < 0) {
      Btn1Cnt = 0;
      if (Btn1Status == On) {
        Btn1Status = Off;
        return (BtnOffEdge);  // Btn1 Off Edge
      } else {
        return (NoChange);  // no change
      }
    }
  } else {  // switch on
    Btn1Cnt++;
    if (Btn1Cnt > threshold) {
      Btn1Cnt = threshold;
      if (Btn1Status == Off) {
        Btn1Status = On;
        return (BtnOnEdge);  // Btn1 On Edge
      } else {
        return (NoChange);  // no change
      }
    }
  }
}

/// ボタン2(Btn2) ///
static int Btn2Cnt = 0;
static int Btn2Status = Off;  // normally off

int check_Btn2_status() {
  if (digitalRead(BTN2_PIN) == HIGH) {  // switch off
    Btn2Cnt--;
    if (Btn2Cnt < 0) {
      Btn2Cnt = 0;
      if (Btn2Status == On) {
        Btn2Status = Off;
        return (BtnOffEdge);  // Btn2 Off Edge
      } else {
        return (NoChange);  // no change
      }
    }
  } else {  // switch on
    Btn2Cnt++;
    if (Btn2Cnt > threshold) {
      Btn2Cnt = threshold;
      if (Btn2Status == Off) {
        Btn2Status = On;
        return (BtnOnEdge);  // Btn2 On Edge
      } else {
        return (NoChange);  // no change
      }
    }
  }
}

/////////// EOF ////////////////////

キモとなる電鍵とボタン2個の状態監視部分が長々と(3回)続いていますが、それを除けば、「CW練習機(パート1)音を鳴らす」は、非常に簡単なプログラムで完成しました。

私は、今までは主にSTM32を使ってきました。これはこれで、ベアメタルに近いプログラミングが楽しめるのですが、Arduinoは、最初のハードルが低く、導入しやすい豊富なライブラリが揃っている、また開発環境が機種間で(低速MCUから高速MCUまで)互換性が高いところが、人気の秘密だと思いました。

 

次回は、電鍵から入力されたコードを読み取って(解読して) LCD表示機に表示させる部分を製作します。

このブログを検索

ブログ アーカイブ

CW Decoderの製作講習会を開催しました

 先日、「マイコンで作る 本格的CW デコーダ」を掲載しましたが、これを、船橋市アマチュア無線クラブ(FARC: JJ1YNA)で発表したところ、大画面化したものが欲しいとのご要望をいただきました。 そこで、OLEDをタッチパネル付きのTFT液晶に変更して大画面化、操作性を改善し...

QooQ