お久しぶりです。
SAIです。

 

4月の週末は、自動車の整備をしたりしつつ、

また4月頭に会社で部署移動なんぞありまして、

少し放置しておりました。

 

また、

このブログを再開した理由ですが、

 

今後職場の教育用になればなぁと書き始めていたわけです

が、

ぽしゃりまして、モチベーションが落ちてるというのもあります。

 

 

そんなわけで、

ゲームサイドばかり更新していたのでした。

 

 

それでも、

完全に忘れたわけではないですよ。

 

需要があれば・・・

 

そう、

 

必要なのは需要ですよ。

 

見る人が増えると思ってたのがぽしゃり、

モチベーションが下がってしまったのをどうにかせねば。

 

 

さてさて、どこまでやったかな。

 

思いだそう。

 

確か、7セグクラスは4桁表示まで実現させてたのです。

ただ、4桁を使うにはシーケンシャルに表示する処理を埋め込む必要があります。

つまり、7セグを表示するには定期割り込みを実装する必要があります。

 

 

SAIはサクッと作ってしまいましたが、

何気にこのコールバック関数、またはOnxxx関数の実装って

C言語の作り方とC++言語の作り方は異なります。

 

そもそも、コールバック関数とは何ぞやというところから説明せねばです。

 

 

やっぱり、いったんC言語でIF整合した関数実装のところから

説明をする必要があるんだろうなというところで、

停滞してたはず。。。


 

 

 

気が向いたら記事にします。

 

 

 

【電子工作C++編】A-4.7Segもクラスにしてしまおう

 

さてさて、

今度は、7Seg制御をC++のクラスにしていこう!

 

まず、

7セグをクラスとして作成するにあたり、

必要になるポートは、、

 

 * //      A
 * //     ----
 * //  F | G  |B
 * //     ----
 * //  E |      |C
 * //     ----    . Dot
 * //      D

 

8Pinですね。 (ドットを使わなければ7Pin)

 

Arduinoには14Pin(Nanoは13Pin)ありますので、

 

使うPinはだいたい決まってきます。

 

なので、これらのPin配置は、

クラス内で決め打ちでもいいのですが。。

場合によっては、ほかの用途で先に使ってしまうこともあります。

 

(Arduinoの場合、そこまで頻繁に衝突はしないですが、

 通常のマイコンだと、PPGやIRQ、ADポートとよく衝突します。)

 

そのたびに、コードを置き換えるのは面倒ですよね。

 

前回、LEDを制御するクラスで行ったように、

コンストラクタで、設定情報を受け渡すようにすれば

汎用が効きますね!

 

 

 

ところが、

 

先ほど書いた通り、

7Segをコントロールするには、

8Pinも情報が必要になります。

 

さらにさらに、今後2桁とか4桁のセグを制御しようとすると、

もっと大量に引数が必要になります。

 

// コンストラクタ //

SegControl(int aApin,int aBpin, ・・・・・・ int aDodPin);

みたいなことをやってもいいですが、

 

ダサい。。。

あと、うっかり順番を間違えそうですよね。

 

 

では、どうするか?

 

SAIは構造体クラス(Tクラスって呼んでいます)を使って、

 

以下のようにパッケージングして引数にしています。

 

// コンストラクタ //

SegControl(const T7SegSetter& aPort);

 

引数のT7SegSetterはどんなものかというと、

7セグの情報、PinをLOWでONするか、HIGHでONするかの設定を

パッケージにしています。

↓↓↓↓↓↓

class T7SegSetter
{
 public:
  // LED=ONがLOWかHIGHかを設定する/
  int iLED_ON;
  int iLED_OFF;
  
  // 各ポートの番号を設定する/
  int iASeg;
  int iBSeg;
  int iCSeg;
  int iDSeg;
  int iESeg;
  int iFSeg;
  int iGSeg;
  int iDotSeg;
  };

(本当はこの構造体クラスにもコンストラクタを付けるのがいいですよ)

 

最近試した

この記事で、”最近のコンパイラだと”うまいこと設定できることが分かったしね。

 

 

もし、何か他にも引き渡したい情報が必要になったら、

コンストラクタの引数を修正するのではなく、

クラスにメンバーを追加することで

対応できますね!

 

 

そのほかに、

7セグを制御するためには・・・

 

 

 

↑の日記で書いている通り、

 

int の数字から、 7SEGの各数字に変換する関数を

外部からは見えない、privateな関数にしておくとよいですね。

 

 

というわけで、

こんなクラスを作成します。

-----------------------------------------

/*
 * @file    CSegControlClass
 * @brief   Lesson A_4 7Segを制御するクラスを作成してみよう。
 * @author  SAI
 * @date    2021/04/04
 * @note    7Segを簡単に操作できるようにするよ
 */

class CSegControlClass
{
 public:

  /* costructor */
  CSegControlClass(const T7SegSetter& aPort);
  /* destructor */
  ~CSegControlClass();
  /* 出力設定 0-9まで */
  void SetOutput(int aOutput);

private:

  //Display function 0
  void zero();
  //Display function 1
  void one(); 
  //Display function 2
  void two();
  //Display function 3
  void three();
  //Display function 4
  void four();
  //Display function 5
  void five();
  //Display function 6
  void six();
  //Display function 7
  void seven();
  //Display function 8
  void eight();
  //Display function 9
  void nine();
private:
  T7SegSetter iPort;
};

-----------------------------------------

 

こんなクラスを作ってしまえば、

 

今度からは表示したい数字の関数を呼ぶのではなく、

表示したい数値を引数で渡してやるだけで済みますね。

 

↓こんな感じで、intの引数を入れたら表示できるので、

 呼び出し元の処理がめちゃくちゃ簡単になりますよ!

 

-----------------------------------------

  CSegControlClass  lSegCtrlClass(C_7SegSetter);

 

  /* 3つを同時に点灯 */
  for(int i=0 ; i < 10;i++)
  {
    lSegCtrlClass.SetOutput( i );
    delay(1000);
  }

-----------------------------------------

 

簡単に10まで表示できますね!

 

 

 

ところで、

コンストラクタの引数の設定が面倒じゃんと思ったあなた。

 

その通り。

セッタークラスの設定面倒くさそうに見えますよね。

 

C言語ソースに書くと、こんな感じになりますか。

------------------------------

  T7SegSetter l7SegSetter;

  // ON/OFF情報を設定
  l7SegSetter.iLED_ON = LOW;
  l7SegSetter.iLED_OFF = HIGH;
 
// 各ポートの番号を設定する/
  l7SegSetter.iASeg = ASeg;
  l7SegSetter.iBSeg = BSeg;
  l7SegSetter.iCSeg = CSeg;
  l7SegSetter.iDSeg = DSeg;
  l7SegSetter.iESeg = ESeg;
  l7SegSetter.iFSeg = FSeg;
  l7SegSetter.iGSeg = GSeg;
  l7SegSetter.iDotSeg = DotSeg;

 

  CSegControlClass  lSegCtrlClass(l7SegSetter);

------------------------------

 

 

さすがにSAIも、

処理の途中で、こんなべたべたなことを書くのは嫌いです。

 

じゃ、どうするか?

 

SAIだと、T7SegSetter型の定数を定義しておきますよ!

 

↓こんな感じの方法で、セグ設定を確保しておくんです。

------------------------------

const T7SegSetter C_7SegSetter = 
{

  // ON/OFF情報を設定
  LOW
  ,HIGH
  
 
// 各ポートの番号を設定する/
  ,ASeg
  ,BSeg
  ,CSeg
  ,DSeg
  ,ESeg
  ,FSeg
  ,GSeg
  ,DotSeg
};

------------------------------

 

処理の途中に書くわけではないので、

可読性も落ちませんよね!

 

 

それでは、

動かしてみましょう!

 

※ソースコードは文末にあるので

 コピペして試してみてね。

 


 

どうでしたか?

 

7Segが0~9まで順番に表示されましたか?

 

 

呼び出し元が簡単になったはずですので、

呼び出し方を色々工夫してみて、

遊んでみてください。

 

それではSAIでした。

 

晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ

雑談

 

既に

タイマークラスを2タイプ作成しているのですが、

どっちが使いやすいか考えています。

 

組み込み系をやっている人ならわかると思いますが、

10msとか100msとかで周期的にメイン関数を呼び出してもらい、

中でカウントするタイプがC言語には多いと思います。

 

つまり、原始的に、各機能の専用の関数を、順番に呼び出す。

機能を追加するたびに、周期処理を作っている人に追加してもらう。

というもの。

 

 

ところが、

WindowやVC++など、アプリケーションを作る人にとっては、

10秒後にコールバックしてもらう。(または、Onxx()が呼び出される)

 

というような作りの方が

うれしいはずなんですよね。


 

機能面で見ると、

メイン処理部、orタイマ呼び出し部の方にお願いして、

外部インターフェースの追加になってしまうのです。

 

本職ではやりたくないですよね。

 

SAIがC言語で作るなら、

コールバック関数を、関数ポインタで渡してもらう。

or

関数ポインタ配列をぐるぐる回すだけにしておいて、

外部IF整合時には、定型としておき、関数を登録するだけにする。

 

 

static int callback(int a); // ←こんなコールバック関数を

 

SetObserver(&callback);  // こんな感じで関数のポインタを渡してもらい

 

必要なタイミングで、

int  (*icallbackPtr)(a); 

 

みたいな・・・

 

この時点で、話は全然途中なのですが、

文章だと、意味不明な人が増えてきたと思います。

 

C言語でコールバック関数の設定、呼び出しを

理解している人には説明が楽なのですが、

 

この先、純粋仮想関数の説明をするにあたり、

悩んでます。

 

一旦中抜きで記事だけ書いて、後で捕捉する形にしようかな。。

じゃないと先に進まない。

 

というわけで、日記に悩んでるSAIでした。

晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ晴れ

 

またねっ!

 

 

 

宇宙人しっぽ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人あたま

★メインのinoファイル

/*
 * @file    CPP_L_A_3_TestCLASS
 * @brief   Lesson A_3 Classをcppファイルに分離みよう。
 * @author  SAI
 * @date    2021/04/03
 * @note    Classをcppファイルに分離みよう。
 */


/* *** Include List *** */
#ifndef __SegControlClass_
#include "SegControlClass.h"
#endif // __SegControlClass_

/* *** Define 定義 *** */
const int ASeg = 13;
const int BSeg = 12;
const int CSeg = 11;
const int DSeg = 10;
const int ESeg = 9;
const int FSeg = 8;
const int GSeg = 7;
const int DotSeg = 6;

const int Keta1Seg = 5;
const int Keta2Seg = 4;
const int Keta3Seg = 3;
const int Keta4Seg = 2;


const T7SegSetter C_7SegSetter = 
{
  // ON/OFF情報を設定
  LOW
  ,HIGH
  
  // 各ポートの番号を設定する/
  ,ASeg
  ,BSeg
  ,CSeg
  ,DSeg
  ,ESeg
  ,FSeg
  ,GSeg
  ,DotSeg
};

  
/*
 * @note  Arduinoで最初に呼ばれる関数
 */
void setup() 
{
//今回はなし
  pinMode(Keta1Seg, OUTPUT);
  pinMode(Keta2Seg, OUTPUT);
  pinMode(Keta3Seg, OUTPUT);
  pinMode(Keta4Seg, OUTPUT);
  digitalWrite(Keta1Seg, HIGH);
  digitalWrite(Keta2Seg, HIGH);
  digitalWrite(Keta3Seg, HIGH);
  digitalWrite(Keta4Seg, HIGH);
}

/*
 * @note  ぐるぐるループ
 */
void loop() 
{

// ---------------  こんな方法で設定してもいいけど --------- //
  /* スタック上にクラスを生成 */
//  T7SegSetter l7SegSetter;
//
  // ON/OFF情報を設定
//  l7SegSetter.iLED_ON = LOW;
//  l7SegSetter.iLED_OFF = HIGH;
  // 各ポートの番号を設定する/
//  l7SegSetter.iASeg = ASeg;
//  l7SegSetter.iBSeg = BSeg;
//  l7SegSetter.iCSeg = CSeg;
//  l7SegSetter.iDSeg = DSeg;
//  l7SegSetter.iESeg = ESeg;
//  l7SegSetter.iFSeg = FSeg;
//  l7SegSetter.iGSeg = GSeg;
//  l7SegSetter.iDotSeg = DotSeg;
//  CSegControlClass  lSegCtrlClass(l7SegSetter);

// --------  ↓のようにconst定数を作って設定したほうが楽だよね --------- //

  CSegControlClass  lSegCtrlClass(C_7SegSetter);

 
  /* 3つを同時に点灯 */
  for(int i=0 ; i < 100;i++)
  {
    lSegCtrlClass.SetOutput(i);
    delay(1000);
  }
  
  /* 関数終了時にスタックが解放されて、デストラクタが走る
    同時に、デストラクタでLEDを消灯する*/  
}

 

宇宙人しっぽ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人あたま

セグコントロールクラスのヘッダ

// 多重Lncludeの防止 //
#ifndef __SegControlClass_
#define __SegControlClass_

/*
 * @file    T7SegSetter
 * @brief   Lesson A_4 Class用。
 * @author  SAI
 * @date    2021/04/04
 * @note    7SegのPin設定用のクラス。コンストラクタはないので、自身で設定してね。
 * //      A
 * //     ----
 * //  F | G  |B
 * //     ----
 * //  E |    |C
 * //     ----    . Dot
 * //      D
 */
class T7SegSetter
{
 public:
  // LED=ONがLOWかHIGHかを設定する/
  int iLED_ON;
  int iLED_OFF;
  
  // 各ポートの番号を設定する/
  int iASeg;
  int iBSeg;
  int iCSeg;
  int iDSeg;
  int iESeg;
  int iFSeg;
  int iGSeg;
  int iDotSeg;
  
//  int iKeta1Seg;
//  int iKeta2Seg;
//  int iKeta3Seg;
//  int iKeta4Seg;
};

/*
 * @file    CSegControlClass
 * @brief   Lesson A_4 7Segを制御するクラスを作成してみよう。
 * @author  SAI
 * @date    2021/04/04
 * @note    7Segを簡単に操作できるようにするよ
 */
class CSegControlClass
{
 public:
  /* costructor */
  CSegControlClass(const T7SegSetter& aPort);
  /* destructor */
  ~CSegControlClass();
  /* 出力設定 0-9まで */
  void SetOutput(int aOutput);

private:
  //Display function 0
  void zero();
  //Display function 1
  void one(); 
  //Display function 2
  void two();
  //Display function 3
  void three();
  //Display function 4
  void four();
  //Display function 5
  void five();
  //Display function 6
  void six();
  //Display function 7
  void seven();
  //Display function 8
  void eight();
  //Display function 9
  void nine();
private:
  T7SegSetter iPort;
};

#endif // __SegControlClass_

 

 

宇宙人しっぽ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人あたま

 

セグコントロールクラスの関数

/*
 * @file    CSegControlClass
 * @brief   Lesson A_4 7Segを制御するクラスを作成してみよう。
 * @author  SAI
 * @date    2021/04/04
 * @note    7Segを簡単に操作できるようにするよ
 */

#include "Arduino.h"

#ifndef __SegControlClass_
#include "SegControlClass.h"
#endif // __SegControlClass_


 /*
 * @fn    初期設定
 * @brief コンストラクタ
 * @param aPort 7SegLEDを制御するポート番号
 */
CSegControlClass::CSegControlClass(const T7SegSetter& aPort)
:iPort(aPort)
{
  //出力設定/
  pinMode(iPort.iASeg, OUTPUT);
  pinMode(iPort.iBSeg, OUTPUT);
  pinMode(iPort.iCSeg, OUTPUT);
  pinMode(iPort.iDSeg, OUTPUT);
  pinMode(iPort.iESeg, OUTPUT);
  pinMode(iPort.iFSeg, OUTPUT);
  pinMode(iPort.iGSeg, OUTPUT);
  pinMode(iPort.iDotSeg, OUTPUT);
  
//  pinMode(iPort.iKeta1Seg, OUTPUT);
//  pinMode(iPort.iKeta2Seg, OUTPUT);
//  pinMode(iPort.iKeta3Seg, OUTPUT);
//  pinMode(iPort.iKeta4Seg, OUTPUT);
}

/*
 * @fn    終了設定
 * @brief デストラクタ
 * @note  終了時には必ずリソースを終了状態にする。
 *        生成したヒープ、つないだセッションなど開放する
 */
CSegControlClass::~CSegControlClass()
{
  digitalWrite(iPort.iASeg, iPort.iLED_OFF);
  digitalWrite(iPort.iBSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iCSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iDSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iGSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iDotSeg, iPort.iLED_OFF);
}


/* 出力設定 */
void CSegControlClass::SetOutput(int aOutput)
{
  int lDigit = aOutput % 10;
  switch(lDigit)
  {
  case 1:
    one();
    break;
  case 2:
    two();
    break;
  case 3:
    three();
    break;
  case 4:
    four();
    break;
  case 5:
    five();
    break;
  case 6:
    six();
    break;
  case 7:
    seven();
    break;
  case 8:
    eight();
    break;
  case 9:
    nine();
    break;
  case 0:
  default:
    zero();
    break;
  }
}

//Display function 0
void CSegControlClass::zero() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_ON);
  digitalWrite(iPort.iFSeg, iPort.iLED_ON);
  digitalWrite(iPort.iGSeg, iPort.iLED_OFF);
}

//Display function 1
void CSegControlClass::one() { 
  digitalWrite(iPort.iASeg, iPort.iLED_OFF);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iGSeg, iPort.iLED_OFF);
}

//Display function 2
void CSegControlClass::two() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_ON);
  digitalWrite(iPort.iFSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

//Display function 3
void CSegControlClass::three() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

//Display function 4
void CSegControlClass::four() {
  digitalWrite(iPort.iASeg, iPort.iLED_OFF);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_ON);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

//Display function 5
void CSegControlClass::five() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_ON);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

//Display function 6
void CSegControlClass::six() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_ON);
  digitalWrite(iPort.iFSeg, iPort.iLED_ON);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

//Display function 7
void CSegControlClass::seven() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_OFF);
  digitalWrite(iPort.iGSeg, iPort.iLED_OFF);
}

//Display function 8
void CSegControlClass::eight() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_ON);
  digitalWrite(iPort.iFSeg, iPort.iLED_ON);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

//Display function 9
void CSegControlClass::nine() {
  digitalWrite(iPort.iASeg, iPort.iLED_ON);
  digitalWrite(iPort.iBSeg, iPort.iLED_ON);
  digitalWrite(iPort.iCSeg, iPort.iLED_ON);
  digitalWrite(iPort.iDSeg, iPort.iLED_ON);
  digitalWrite(iPort.iESeg, iPort.iLED_OFF);
  digitalWrite(iPort.iFSeg, iPort.iLED_ON);
  digitalWrite(iPort.iGSeg, iPort.iLED_ON);
}

 

【電子工作C++編】A-3.Arduinoでクラスを別ファイルに分離してみよう

 

 

少し日が開いてしまいましたが、

前回は、ArduinoでC++のクラスを作ってみました。

 

クラスを流用するには、

コード部分だけを移植することも可能ですが、

できれば

cppだけを別ファイルにして、

ファイルを追加するだけで移植したいですよね。

 

 

以前C言語の場合の複数ファイルに分ける方法を紹介しました。

 

 

 

今回も同様に、CPPファイルとして、複数ファイルに分けたいですね。

 

 

というわけで、

 

分けてみましょう。

 

◆ファイル分割① ファイルを追加

 

前回と同じ、↓これですね。

 

新規タブ と書いていますが、これがファイル追加を意味しています。

 

このボタンを押すと。。

 

画面を大きくしているとわかりにくいんですが、

以下のような、ファイル名を記入する画面が表示されます。

 

 

毎回思いますが、なんで画面の下やねん。。。

 

画面をでっかくしていたら、気づきませんよね。

 

時を戻そう。

 

まずは、ヘッダフォイルを追加します。

 

今回は、LedClassを追加したいので、

そのまんま”LedClass.h”としました。

 

別にどんなファイル名でもいいんですが、

後で見た時に、何のファイルかがわかるファイル名が良いですよ!

 

 

 

◆ファイル分割② コードの移動

 

続いて、

新しく作成した"LedClass.h"に、移動させたいコード部分を全部移動させましょう。

 

移動させるコードですが、

 

CPPファイルはC言語と違い、

ちゃんとプロトタイプ宣言をしないといけないようです。

 

というわけで、Class 宣言部分をすべて移動させます。

 

 ※後述 ”LedClass.h” を参照

 

 

◆ファイル分割③ includeの追加

 

メインの関数に、新ファイルをincludeしましょう。

 

 

ただのIncludeではないですね。

何をしているかというと、”多重Includeの防止”をしています。

 

検索をかけていただければと思いますが、ヘッダで”__LedClass_”を定義して

1度でも通っていたら、2回目は呼び出さないようにする処理です。

Cソース側でifndef でチェックを行う理由ですが、

ファイル呼び出しすらしないためです。

 

※HDDが遅かった時代、コンパイルする時間以上に

 ファイルを読み出す処理に時間がかかっていました

 HDDリードがフルパワーなので、FEP(文字の変換)ができず

 ビルド中は何もできなかったですね。

 

 

ほかにも色々理由はあるのですが、

多重インクルード防止の癖をつけておくとよいと思います。

 

この段階で、一回ビルドしてみてください。

 

 

うまく移動できていたら、

ビルドが成功するはずです!

 

◆ファイル分割④ ソース用のcppファイル追加

 

続けて、ソースコード部分を移植します。

Arduinoで追加するCppのソースファイルは”cpp”である必要があります。

拡張子を指定して”*.cpp”になるようです。

 

 

◆ファイル分割⑤ コードの移動

 

続いて、

新しく作成した"LedClass.cpp"に、

移動させたいコード部分(作った関数のことです)を全部移動させましょう。

 

C言語と違い、ヘッダファイルにプロトタイプ宣言をする必要があります。

そうそう、

ということは、Arduino.hをIncludeしないと、

Arduinoの関数が使えません。       ---- ①

 

②でちゃんとclassのプロトタイプ宣言をしましょうね。

 

また、プロトタイプ宣言が必要ということは、

コードの最初で、プロトタイプ宣言をしたヘッダファイルをIncludeしましょうね。

今回だと、LedClass.h が必要です。 ----②

 

 

どうでしょうか?

 

※サンプルソースを最下部に載せておきます。

 

お試しください。

 

宇宙人しっぽ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人あたま

★メインのinoファイル

 

/*
 * @file    CPP_L_A_3_TestCLASS
 * @brief   Lesson A_3 Classをcppファイルに分離みよう。
 * @author  SAI
 * @date    2021/04/03
 * @note    Classをcppファイルに分離みよう。
 */


/* *** Include List *** */
// LED Control Class/
#ifndef __LedClass_
#include "LedClass.h"
#endif // __LedClass_

/* *** Define 定義 *** */
const int CPort1 = 11;
const int CPort2 = 12;
const int CPort3 = 13;


/*
 * @note  Arduinoで最初に呼ばれる関数
 */
void setup() 
{
//今回はなし
}

/*
 * @note  ぐるぐるループ
 */
void loop() 
{
  delay(5000);
  /* スタック上にクラスを生成 */
  LedClass  lStackLedClass[3]={CPort1,CPort2,CPort3};

  delay(500);
  /* 3つを同時に点灯 */
  for(int i=0 ; i < 3;i++)
  {
    lStackLedClass[i].SetOutput(true);
  }
  
  delay(500);

  /* 3つを順に消灯 */
  for(int i=0 ; i < 3;i++)
  {
    lStackLedClass[i].SetOutput(false);
    delay(500);
  }
  
  /* 3つを順に点灯 */
  for(int i=0 ; i < 3;i++)
  {
    lStackLedClass[i].SetOutput(true);
    delay(500);
  }
  
  /* 関数終了時にスタックが解放されて、デストラクタが走る
    同時に、デストラクタでLEDを消灯する*/  
}

 

 

宇宙人しっぽ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人あたま

 

// LedClass.h ファイル

// 多重Lncludeの防止 //
#ifndef __LedClass_
#define __LedClass_

/*
 * @file    LedClass
 * @brief   Lesson A_3 Classを別ファイルにしてみよう。
 * @author  SAI
 * @date    2021/04/03
 * @note    LedClassを流用できるように別ファイルにぶんりするよ
 */

 
 /*
 * @note    LedClassクラスのプロトタイプ
 */
class LedClass
{
 public:
  /* costructor */
  LedClass(int aPort);
  /* destructor */
  ~LedClass();
  /* 出力設定 */
  void SetOutput(bool aOutput);
  bool GetStatus()const;
 private:
  int iPort;
  bool iStatus;
};

#endif // __LedClass_
 

宇宙人しっぽ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人からだ宇宙人あたま

// LedClass用のcppファイル

/*
 * @file    LedClass
 * @brief   Lesson A_3 Classを別ファイルにしてみよう。
 * @author  SAI
 * @date    2021/04/03
 * @note    LedClassを流用できるように別ファイルにぶんりするよ
 */

#include "Arduino.h"

#ifndef __LedClass_
#include "LedClass.h"
#endif // __LedClass_

 /*
 * @fn    初期設定
 * @brief コンストラクタ
 * @param aPort LEDを制御するポート番号
 */
LedClass::LedClass(int aPort)
:iPort(aPort),iStatus(false)
{
  /* コンストラクタで指定されたポートを出力設定 */
  pinMode(iPort, OUTPUT);
}
/*
 * @fn    終了設定
 * @brief デストラクタ
 * @note  終了時には必ずリソースを終了状態にする。
 *        生成したヒープ、つないだセッションなど開放する
 */
LedClass::~LedClass()
{
  digitalWrite(iPort, LOW);
}
/*
 * @fn    出力設定
 * @brief 出力の設定を切り替える
 * @param aOutput ON/OFFを設定する true=点灯 false=消灯
 */
void LedClass::SetOutput(bool aOutput)
{
  digitalWrite(iPort, aOutput);
  iStatus = aOutput;
}

/*
 * @fn    出力状態取得
 * @brief 出力の設定を切り替える
 * @param aOutput ON/OFFを設定する true=点灯 false=消灯
 */
bool LedClass::GetStatus()const
{
//  SetOutput(false);
  return iStatus;
}