PASのブログ

PASの中の人が書く雑記ブログです。

アナマル.11 第2部スタート。PRGモードを追加編

小休止を取りまして、マニュアルモードだけではなくて、プログラムモードを追加してほしいという声がありましたので、第2部を本日よりスタート致します。

アナマル.1から10までで理解できていない部分があれば、復習をおすすめします。

全体の構造

プログラムモードを追加するということは、プログラムが出来るモードも追加するということになるのでイメージとしてはこんな感じです。

f:id:pas_fx:20200617203431p:plain
モード移行のイメージ

構造を事前に考えて各所を作っていくやり方が望ましいので、流れとしては、前回までのスケッチを改造しますので、

マニュアルモードとプログラムモードを切り替える部分を作る。

プログラムモードを作る。

モードを切り替えるのをスイッチで行う。

記憶させる機能を作る。

プログラムセットモードを作る。

ここまでやって、LEDだけの表示では足りないようならば先日ご紹介した1602というディスプレイを追加します。

モードを移行する方法を検討する。

モードとは言え、いろんなやり方があるのですが、例えば、

○基本的にはメインループしながら、そのモードならではの命令が来たときに一瞬そのモードに飛んで、すぐメインループに戻る。

○一旦メインループからそのモードのループに飛んだら、戻れと命令するまでメインループに戻らずにモードの中でループする。

この2つがわかりやすいと思います。

今回の場合、マニュアルモードとプログラムモードは、例えばフットスイッチの入力処理などは共通化出来ます。 それに対して、プログラムセットモードでは、前回決めたように、

Aボタン→平時はマニュアル/プログラムの切り替え。

Bボタン→平時は長押しでセットモードに。

セットモード

Aボタン→セットするchを選択

Bボタン→短押しでセット。長押しで通常モードに。

このように、ボタン(スイッチ)の使い方が共通ではないのもありますので、プログラムセットモードの中でループする方法を採りたいと思います。

マニュアルモードとプログラムモードを切り替える部分を作る。

//SW4

const int SW[4] = {A0, A1, A2, A3};//chのスイッチ
const int SW4 = A4;//man/prg切り替え
const int SW5 = A5;
const int LED[4] = {7, 6, 5, 4};
int state[4] = {0}; //ONOFFの状態を保存
int val[4] = {0}; //スイッチの状態を保存
int old_val[4] = {0}; //前回の状態を保存
int a;
int count = 0; // 1周ループしたら+1
int mode = 1; // 0でmanual,1でPRG


void setup() {
  Serial.begin(9600);
  pinMode(LED[0], OUTPUT);
  pinMode(LED[1], OUTPUT);
  pinMode(LED[2], OUTPUT);
  pinMode(LED[3], OUTPUT);
  pinMode(SW[0], INPUT_PULLUP);
  pinMode(SW[1], INPUT_PULLUP);
  pinMode(SW[2], INPUT_PULLUP);
  pinMode(SW[3], INPUT_PULLUP);


}

void loop() {

  a = count % 4;
  sw(a);
  count = count + 1;

}

void sw(int num) {
  val[num] = digitalRead(SW[num]); //スイッチの状態をval[num]に入れる

 if ((val[num] == 1) && (old_val[num] == 0))
  {
    delay(10);// 10ms待つ。
    if (mode == 1)
    {
      prg_process(num);
    }
    state[num] = 1 - state[num]; // state[num]をひっくり返す。

  }

  if (mode == 0)
  {
    if (state[num] == 1)
    {
      digitalWrite(LED[num], HIGH);   // LEDへの信号をHIGH(点灯)
    }
    else
    {
      digitalWrite(LED[num], LOW);    // LEDへの信号をLOW(消灯)
    }
  }

  old_val[num] = val[num];  // 処理が済んだので前回の状態にval[num]をコピーする。
}

void prg_process(int num)// PGMモード時動作
{
 
}

ここで新たに、プリプロセッサとして、
int mode = 1; // 0でmanual,1でPRG
という変数を作りました。
この変数を後々スイッチで切り替えることで、マニュアルモードと、プログラムモードの切り替えに利用できます。

次に、

if (mode == 1)
    {
      prg_process(num);
    }

をどこかに挟み、それとは別に

void prg_process(int num)// PGMモード時動作
{
 中身は後ほど作成
}

という関数を作ることで、モード変更の仕組みが完成です。 ではどこに挟むか、ということですが、前回のスケッチの命令の順番を少し変えて、
「スイッチから手を離した瞬間に判定」することにします。

if ((val[num] == 1) && (old_val[num] == 0))
  {
    delay(10);// 10ms待つ。
    if (mode == 1)
    {
      prg_process(num);
    }
    state[num] = 1 - state[num]; // state[num]をひっくり返す。

  }

stateの情報は必要ないので、この位置が良さそうです。

現在mode = 0;にしていますので、この状態で前回と同じようにマニュアルモードで動作するはずです。

ここで、数分間チェックをしていたら有ることに気づきました。
数分後に正常に動作しなくなります。
先にこの部分をデバッグしましょう。

問題はcountにあった。

時間が立つと条件が変わると変数はこのスケッチではcountですので、これをシリアルモニタで見ていきます。

void loop() {

  a = count % 4;
  sw(a);
  count = count + 1;
  Serial.println(count, DEC);

}

そうすると、オーバーフロー(数値が満タンになりあふれること)するときにこうなりました。

f:id:pas_fx:20200618100933p:plain
オーバーフローしたらマイナスになった

考えてみれば当然です。intは0を中心としてマイナス領域もカバーするからです。

ちなみにこのときのaの数値はこうなります。 f:id:pas_fx:20200618101345p:plain

対策としては、count のintを、符号なしunsigned intに変更します。

f:id:pas_fx:20200618105313p:plain
オーバーフローしたら0から再スタート

この後数十分チェックしましたが問題は解決したようです。

modeについて

今回作成したスケッチでは、mode == 1の時と、mode == 0の時をそれぞれ記述しています。
本来でしたら片方は省略しても構いませんが、デバッグの際にわかりやすいので、このようにどちらの場合も記述したほうが良いかと思います。