みなさんこんにちわ、

SAIです。

 

前回、関数ポインタ呼び出しを学習しました。

 

ただ、前回の使い方だと、

周期呼び出しを関数ポインタにする程度。

 

この程度、別に関数羅列でも実現できますし、

あまり有益ではないですよね。

 

 

実は、関数ポインタが本当に活躍するのは、

非同期コールバックとか、状態遷移処理だと考えています。

 

 

今日は非同期コールバックを勉強しましょう。

 

 

非同期コールバックって何でしょうか?

 

その前に、同期処理 と 非同期処理 を理解しましょう。

 

 

同期処理とは、その名の通り 関数内で同期で処理を完結させるものです。

 

通常の関数呼び出しだと考えてください。

 

 

例えば、

0.5秒ごとにLEDを点滅する処理を作る場合どうしますか?

 

皆さん使ったことがありますよ。

  delay(500);

500ms待ちですね。

 

絵にすると、こんな感じです。

 

 

ただ、この処理には問題があります。

 

0.5秒間、他の処理ができません。

 

あせる現実世界でいえば、

 カップヌードルにお湯を入れたとしましょう。

 お湯を入れてから、3分経過するまでずっと時計を見続ける必要があります。

 

合格マイコンで例えば、

 4桁7セグLEDを使った場合、

 この時間待ち中も表示を続けなければなりません。

 

それではほかの処理ができません。困ります。

 

 

では、どうするか?

ほかの同期処理の方法として

メイン側の関数内で、0.5秒経過したかを毎回チェックする必要がありますよね。

 

もし、0.5秒待ちを同期処理すると

絵にするとこんな感じ。

 

 

毎周期、0.5秒たったかをチェックしなければならず、

処理がもったいないですよね。

 

あせる現実世界でいえば、

 カップヌードルにお湯を入れたとしましょう。

 お湯を入れてから、3分経過するまでちらちらと時計を見続ける必要があります。

 

 集中して計算なんかはできませんね。

 



では、
非同期処理はどのようなものでしょうか?


処理が終わったときに、処理が終わったことを通知をしてもらうものです。

 

 

 

 

あせる現実世界でいえば、

 カップヌードルにお湯を入れたとしましょう。

 お湯を入れた直後に、キッチンタイマーに3分をセットします。

 

 3分経過したら、タイマーアラームが鳴りますよね

 アラームが鳴るので、それまで計算などをしていても大丈夫ですね!

 

 

パソコン イヒ ラーメン         風鈴    時計 

 

 

 

ほかの非同期打とうとしては

タイマーとか、SD読み出しとか、モータをX回転回すとか、

処理が終わった際に、特定の関数を呼び出して、教えてあげるよ!

 

というものです。

 

 

そうすれば、その間別のことに集中できますね。

 

 

これが、関数ポインタだとどう役に立つのかというと・・・・

特にサードパーティソフトとしてソフト提供するようなときです。

 

 

非同期処理を複数実装する際に、どの呼び出し元からの応答か区別をつけるため

複数の関数に応答を作ってあげることになると思います。

 

自分が作るコードとか、同じ職場の人が作るコードなら、

Aの機能の応答は、FuncA_CallBack()、FuncB_CallBack()、で実装お願い!って言えますよね。

 

あせる現実世界でいえば、

 A君のカップラーメンを入れてから5分、

 B君のカップラーメンを入れてから3分、

 というように、開始タイミングも、終了までの時間も違うようなとき、

 1つのタイマーだと同時に処理できません。

 

 仮に一つのタイマーで処理をしたとすると、

 完了のアラームが、どっちの処理が完了したか見分けが尽きませんよね。

 複数のタイマーで、FuncA、FuncBのタイマを使用すると思います。

 

  A君 

パソコンイヒラーメン  時計               B君

                ベル時計  ラーメンウシシ パソコン

 

 

これがもし第三者に提供したり、提供してもらう場合どうでしょうか?

C言語の場合、同名の関数は一つしか作れません。

 

 

  A君 

パソコン イヒラーメン  時計          B君

               ラーメン!! 叫び パソコン

 

 

B君、計測できません!

困りますね。


 

こんな時、A君のタイマー用のコールバック関数、B君のタイマー用のコールバック関数を

別に分けることができたらいいと思いませんか?

 

 

 

もし、タイマーの設定をする際に、A君とB君の完了時の音を別にすることができたらどうでしょうか?

 

  A君 

イヒラーメン      風鈴    時計        B君

                  ベル  ラーメンウシシ

 

A君は、3分を設定するときに風鈴の音   ⇒A_CallBack

B君は、5分を設定するときにベルの音       ⇒B_CallBack

 

と設定すれば、カップ麺の完成時間がわかりますね!


 

 

ソフトとしては、

タイマーの要求をする際に、コールバックしてもらう関数を、引数で渡してしまえば

IF整合なんて必要なくなりますね!

イメージはこのような感じです。

 

 

 

 

では、ソフトを作ってみましょう。

 

前回は関数を配列にしましたが、

今回は関数とタイマのなる時間を配列で覚えます。

 

なので、定義がちょっと長いですね。

 

/* 関数ポインタの型定義 */
typedef void (*pFunc)(void);

/* 関数ポインタと時間の配列の構造体定義 */
struct TFuncParam
{
  pFunc  iFunc;           // callback 関数 /
  long   iTimerFireTime;  // callbackの時間 /
};

/* 関数ポインタと時間の配列 */
TFuncParam FunctionList[] =
{
  {NULL ,0}       /* 機能1 */
  ,{NULL ,0}      /* 機能2 */
  ,{NULL ,0}      /* 機能3 */
  ,{NULL ,0}      /* 機能4 */
  ,{NULL ,0}      /* 機能5 */
};

 

あとは、タイマーを改造して、処理を実装してみてくださいね!

 

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

雑談

C++言語でクラスと継承を使えば、めっちゃ楽になります。

 

その説明用にC言語で実装しましたが、

C言語で書くと、かなり面倒くさいですね。

 

あと、お試しで作ったSAIのコードも汚いので、イメージしにくいかも。。。

 

閲覧数が多そうであれば、改めて関数を整理しようかしら。。

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

 

それでは、作成したコードです。

main.ino

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

/*
 * Author: SAI
 */

/* *** Include *** */
#include <TimerOne.h>
/* 注意
   TimerOne.h を使用するためには、
   スケッチ -> ライブラリのインクルード -> ライブラリの管理
   から、TimerOne をインストールしておく必要があります。
*/

// 7Seg LED 表示Function //
#include "DisplayFunction.h"


/* *** Define 定義 *** */
/* 1桁の表示時間 */
const long TIMER_DELAY = (5 * 1000L);
/* 1秒にかかるカウント数 */
const long TIMER_CNT_SECOUND = ((long)1000 * 1000L) /(TIMER_DELAY);

/* RoundUp時間 ms */
const long TIMER_ROUNDUP = 1000 * 1000L;

/* 砂時計用内部カウンター */
long gTimerCounter;
/* 7セグ更新用時間 */
long gDispTime;

/* 関数ポインタの型定義 */
typedef void (*pFunc)(void);

/* 関数ポインタと時間の配列の構造体定義 */
struct TFuncParam
{
  pFunc  iFunc;           // callback 関数 /
  long   iTimerFireTime;  // callbackの時間 /
};

/* 関数ポインタと時間の配列 */
TFuncParam FunctionList[] =
{
  {NULL ,0}       /* 機能1 */
  ,{NULL ,0}      /* 機能2 */
  ,{NULL ,0}      /* 機能3 */
  ,{NULL ,0}      /* 機能4 */
  ,{NULL ,0}      /* 機能5 */
};


long GetMsTime()
{
  return (gTimerCounter *1000 / (TIMER_CNT_SECOUND ) ) ;
}

/* 機能関数の登録関数(ms) */
void AddFunction(void *aFunc, long aDelayTime)
{
  int i=0;
  while(FunctionList[i].iFunc!= NULL )
  {
    i++;
  }
  FunctionList[i].iFunc=aFunc;
  FunctionList[i].iTimerFireTime = aDelayTime + GetMsTime();
}

/* 機能関数の登録関数(ms) */
void TimerRoundup()
{
  gTimerCounter = 0;
  int i=0;
  while(FunctionList[i].iFunc!= NULL )
  {
    FunctionList[i].iTimerFireTime -= TIMER_ROUNDUP;
    i++;
  }
}


/* 機能関数の削除 */
void RemoveFunction(void *aFunc)
{
  int i=0;
  while(FunctionList[i].iFunc!= aFunc )
  {
    i++;
  }

  // sort //
  while(FunctionList[i].iFunc!= NULL )
  {
    FunctionList[i].iFunc       =FunctionList[i+1].iFunc;
    FunctionList[i].iTimerFireTime =FunctionList[i+1].iTimerFireTime;
    
    i++;
  }
}




/* initial setting 初期設定 */
 void setup() {
  initialize();
  /* タイマー用のカウンターを初期化 */
  // Timer setting  タイマー設定
  Timer1.initialize(TIMER_DELAY); //マイクロ秒単位で設定 
  Timer1.attachInterrupt(timerFire); // コールバック関数設定 
  
  gTimerCounter = 0;
  gDispTime = 0;

  // Lチカ用 //
  pinMode(A0, OUTPUT);

  AddFunction(&DisplayDraw,1);
  AddFunction(&DisplayLedChika,500);  

}



/* Mail loop 通常処理(繰り返し) */
void loop() {

  delay(1000);

}



/*
 * タイマー割り込み処理
 *  */
void timerFire() {
  // 時間計測 //
  gTimerCounter++;
  /* 1ms単位の時間に変換 */
  long lTime = GetMsTime();
  if(lTime >= TIMER_ROUNDUP)
  {
    TimerRoundup();
  }

  if(gDispTime != lTime)
  {
    gDispTime = lTime;
    /* 桁上がりした時だけ、1msの処理をする */
    delay(1);
  }

  /* 関数呼び出し */
  int i=0;
  while(FunctionList[i].iFunc!= NULL )
  {
    /* 発動時間を超えていたら、コールバック実施 */
    if(FunctionList[i].iTimerFireTime < lTime)
    {
     (void)(FunctionList[i].iFunc)();
      RemoveFunction(*FunctionList[i].iFunc);
    }
    i++;
  }

}


  
/* LED点灯状態 */
static bool gIsLedOn;

void DisplayLedChika()
{
  /* 次の非同期呼び出しを500ms後に設定 */
  AddFunction(&DisplayLedChika,500);  


  if(gIsLedOn)
  {
    /* LED点灯中なら消灯 */
    analogWrite(A0, 0);
    gIsLedOn = false;
  }
  else
  {
    /* LED点灯中なら点灯 */
    analogWrite(A0, 255);
    gIsLedOn = true;
  }

}

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

DisplayFunctionは、↓この関数だけ変更しました。

 

void DisplayDraw()
{
  /* 次の非同期呼び出しを5ms後に設定 */
  AddFunction(&DisplayDraw,2);  

  
  /* 桁切り分け */
  int DispKeta = gTimerCounter  % 4 ;
  /* 100ms単位の時間に変換 */
  int lTime = gTimerCounter / (TIMER_CNT_SECOUND /10 ) ;

  switch(DispKeta)
  {
    case 0:
     /* 1桁を表示 */
      DigitDisp(lTime);
      digitalWrite(DotSeg, LED_OFF);  
      Keta1();
      break;
    case 1:
     /* 2桁を表示 */
      digitalWrite(DotSeg, LED_ON);  
      DigitDisp(lTime/10);
      Keta2();
      break;
    case 2:
     /* 3桁を表示 */
      digitalWrite(DotSeg, LED_OFF);  
      DigitDisp(lTime/100);
      Keta3();
      break;
    case 3:
    default:
     /* 4桁を表示 */
      digitalWrite(DotSeg, LED_OFF);  
      DigitDisp(lTime/1000);
      Keta4();
      break;
  }
}

 

みなさんこんにちわ、

SAIです。


 

※注意 今回はちょっと小難しいことを書くので、

     ギブアップしそうな方は、最後のほうの音符音符音符音符まで呼び飛ばして、

     コードをまねていただければよいかと思います。

 

昨日、突然「関数ポインタ」なんて言い出しました。

 

関数ポインタ とは、何でしょうか?

 

トランプハートトランプハートトランプハートトランプハートトランプハートトランプハート

 

その名の通り、関数の配置されているポインタのことです。

 

 

Arduinoだと見ることができないのですが、

プログラム環境をビルドすると、

 

定数、関数、などは、マイコンのROM領域に -----①

グローバル変数はマイコンのRAM領域(固定)に -----②

ローカル変数(一時変数)はマイコンのRAM領域(スタック)に -----③

配置されます。

 

 

①と②は、ビルドしている最中に、ROMのどこに配置されるかがきまります。

③は、スタック領域なので関数が呼び出されている間だけスタックに配置されます。

 

Arduinoだと見ることができないのですが、

一般的なマイコンコンパイラだと、Mapファイルが生成され、

↓こんな感じで,配置されたアドレスが見れますね。

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

SECTION=P

FILE=C:\Hew2\TClassTestZ_3\TClassTestZ_3\Debug\TClassTestZ_3.obj
                                  00000826  0000086d        48
  msecwait(int)
                                  00000826        14   func ,g         1
  _main
                                  0000083a        32   func ,g         1

                 ↑アドレス  ↑サイズ

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

SECTION=R
FILE=C:\Hew2\TClassTestZ_3\TClassTestZ_3\Debug\sbrk.obj
                                  0000fb80  0000fb81         2
  _brk
                                  0000fb80         2   data ,l         2

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

 

main()のアドレスは、0000083aに配置されています。

 

変数 blkのアドレスは、0000fb80に配置されています。

 

 

ROM/RAMのアドレスとしては、↓こんな感じ

 

 

 

 

さて、

変数のポインタのおさらいをしましょう。

変数のポインタってどんな仕組みだったでしょうか?

 

変数 blk に”1”を代入すると、

   blk = 1;

 

変数blkは、0000fb80に配置されています。

 

処理としては、0000fb80の値を”1”に変更したことになりますね。

 

これをポインタで操作してみましょう。

blkのポインタを示すには、↓こうするとblkPtrに0000fb80が入ることになりますね。

 

  int * blkPtr = & blk;

            ↑ blkのポインタを取り出し。

  print("blk add = %x",blkPtr);

    とすれば、0000fb80となっていることが見れるはずです。

 

 このポインタを使って、実体を操作するには、

  * blkPtr = 1 ; 

  ↑ blkポインタの 実体

  このように、blkPtrの実体に1を入れることになります。

 

 思い出したでしょうか?

 

 & アドレス取り出し。

 * 実態操作

  

ですね。

 

 

思い出したでしょうか?

 

 

トランプハートトランプハートトランプハートトランプハートトランプハートトランプハート

 

この操作ですが、

 & アドレス取り出し。

 * 実態操作

既にアドレスがきまっている場合、つまり、①や②の場合、関数にも使うことができます。

 

 

先ほどのMAPに戻ります。

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

  _main
                                  0000083a        32   func ,g         1

                 ↑アドレス  ↑サイズ

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

 

 

main()のアドレスは、0000083aに配置されています。

 

これを、アドレスに変えるとどうなるでしょうか?

 

変数の時と同じように

  print("main add = %x", & main );

    とすれば、0000083aとなっていることが見れるはずです。

 

 

 

ここから、main()関数がどのように呼び出されているか、考えてみましょう。

 

main();

 

main()関数は、0000083aに配置されています。

 

処理としては、0000083aのアドレスにスタックポインタを移動させ、

0000083aから続くアドレスに書き込まれている処理を実行している

 

ということになります。

 

逆に言うと、

0000083aのアドレスを関数として扱ってやれば、数字から関数を呼び出すことができるのです!

 

 イメージとしては、こんな感じ!↓

  * mainPtr();

  

 

音符音符音符音符音符音符音符音符音符音符音符

 

これを実現するためには、一つ壁があります。

  mainPtr が、関数であることを定義しなければなりません。

 

どのように定義したらよいでしょうか?

 

 

答えは、typedef で定義するのです。

 

typedef void (*pFunc)(void);

 

初心者では、なかなか見ないtypedef ですね。

 

typedef ・・・型定義

 

void ***** (void ) という関数だよ

 

そして、型の名前をpFuncとあるということを定義しています。

 

引数があれば、引数型を入れて定義すればOKです。


 

 

つまり、

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

            void   main   (void);

             ↓  ↓    ↓

typedef void (*pFunc)(void);

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

 

   *pFunc  が  main()

  (pFunc の実体) が main関数 ということになりますね!

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

  これを逆にすると・・・

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

   pFunc   は 、 & main()

    (pFunc )      は    main関数のポインタ ということになりますね!

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

 

 

ちょっと雑な説明ですが、理解できたでしょうか?

 

 

ここまで理解出来たら、あとは楽です。

 

 

 

pFunc を配列にすれば、関数のポインタを配列にしたことになります。

 

昨日のコードを関数配列にすると、こんな感じですね。

 

// 関数配列 //
typedef void (*pFunc)(void);

pFunc FunctionList[] =
{
  &DisplayDraw       /* 機能1 7Seg表示関数 */
  ,&DisplayLedChika  /* 機能2 LChika表示関数 */
};

 

 

 

トランプハートトランプハートトランプハートトランプハートトランプハートトランプハート

次の問題。

 

どうやって関数の呼び出しをするのでしょうか?

 

答えから先に書くと、

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

(void)      ( * FunctionList[i] )                     ();

 ↓            ↓                 ↓

void 型の、  アドレスFunctionList[]実態 の  引数がvoid で呼び出す

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

 

です。

 

  /* 登録している機能分呼び出し */
  for( int i = 0; i < aFuncNum;i++)
  {
   (void) (*FunctionList[i])();
  }

 

これで、配列に登録されている関数を、ぐるぐる呼び出せることになりますね!

 

 

というわけで、

 

↑このブログで書いたコードのうち、

メインのコードを修正してみましょう。

 

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

雑談

 

関数ポインタ呼び出しって、理解したら便利なのですが、

理解するのが難しいですよね。

 

ポインタの理解ができていたとしても、

関数ポインタの理解は結構難しいんじゃないかな?

 

 

SAIは意外とすんなり理解できたのですが、

 

2000年代前半、携帯電話開発をしていた時に何人か若い子に説明したけど

なかなか理解できる子はいませんでした。

 

最終的に、めったに使わない処理だから、マネしてくれって言った覚えがあります。

 

一つの壁なんでしょうね。

 

ちなみに、C++言語だともう一癖あります。

 

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

 

 

それでは、main.ino のみコードを書きます。

残り2ファイルは、4-1と一緒だよ

 

/*
 * Author: SAI
 */

/* *** Include *** */
#include <TimerOne.h>
/* 注意
   TimerOne.h を使用するためには、
   スケッチ -> ライブラリのインクルード -> ライブラリの管理
   から、TimerOne をインストールしておく必要があります。
*/

// 7Seg LED 表示Function //
#include "DisplayFunction.h"


/* *** Define 定義 *** */
/* 1桁の表示時間 */
const long TIMER_DELAY = (5 * 1000);
/* 1秒にかかるカウント数 */
const long TIMER_CNT_SECOUND = ((long)1000 * 1000) /(TIMER_DELAY);

/* 砂時計用内部カウンター */
long gTimerCounter;

long gDispTime;

/* initial setting 初期設定 */
 void setup() {
  initialize();
  /* タイマー用のカウンターを初期化 */
  // Timer setting  タイマー設定
  Timer1.initialize(TIMER_DELAY); //マイクロ秒単位で設定 
  Timer1.attachInterrupt(timerFire); // コールバック関数設定 
  
  gTimerCounter = 0;
  gDispTime = 0;

  // Lチカ用 //
  pinMode(A0, OUTPUT);
}



/* Mail loop 通常処理(繰り返し) */
void loop() {

  delay(1000);

}


// 関数配列 //

typedef void (*pFunc)(void);

pFunc FunctionList[] =
{
  &DisplayDraw       /* 機能1 7Seg表示関数 */
  ,&DisplayLedChika  /* 機能2 LChika表示関数 */
};


/*
 * タイマー割り込み処理
 * 100ms(=100000us)毎にここが呼び出される */
void timerFire() {
  // 時間計測 //
  gTimerCounter++;
  if(gTimerCounter >= (long)10000*1000)
  {
    gTimerCounter = 0;
  }
  /* 100ms単位の時間に変換 */
  int lTime = gTimerCounter / (TIMER_CNT_SECOUND /10 ) ;

  if(gDispTime != lTime)
  {
    gDispTime = lTime;
    /* 桁上がりした時だけ、1msの処理をする */
    delay(1);
  }

  /* 7Seg表示 */
//  DisplayDraw();

  /* LChika表示 */
//  DisplayLedChika();


  int aFuncNum = sizeof(FunctionList)/sizeof(FunctionList[1]);

  /* 登録している機能分呼び出し */
  for( int i = 0; i < aFuncNum;i++)
  {
   (void) (*FunctionList[i])();
  }


}


void DisplayLedChika()
{
  /* 100ms単位の時間に変換 */
  int lTime = gTimerCounter / (TIMER_CNT_SECOUND /1 ) ;

  if((lTime % 2) != 1)
  {
    analogWrite(A0, 255);
  }
  else
  {
    analogWrite(A0, 0);
  }

}

 

 

みなさんこんにちわ。

 

システムの処理が多くなってくると、

定期的に無条件で呼び出す関数が増えてきますよね。

 

loop()

{

 func1();

 func2();

 func3();

 

機能が増えるたびに、メイン関数に手を加えないといけません。

 

これ。

 

仕事だったら、

機能側から追加の要望が上がり、

メイン側の担当さんが外部IF整合をして、

追加する必要が出てきますね。

 

もし、関数に変更があったら巻き込まれます。

 

 

これをちょっと見やすくしたい。

 

というわけで、

7Segでタイマー表示をしつつ、1秒ごとにLEDチカチカする機能を搭載した

お遊びソフトを関数呼び出しに変えていきます。

 

 

とりあえず、

初期ソフトはこれで行こうと思います。

 

 

※Arduino nano だとPINが足りないので、アナログPinでLEDチカしてます。

 

 

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

雑談

 

とりあえず分かりよいメイン関数でお試しです。

 

実は、

関数ポインタは、こんな定期呼び出しではあまり威力を発揮しません。

 

SAIのおすすめは、

①状態遷移をマトリクスとして呼び出しをする。

②非同期処理のコールバック関数として登録する

 

この辺で威力を発揮すると思います。

 

ただ、関数ポインタの状態遷移なんて

分かりにくいと思うので、まずはイメージしやすい

メイン関数にしてみます。

 

タイマーコールバックの方が良かったかな・・

 

この前振りですが、

最終的にはC++言語のコールバック関数や、

ベースクラスを配列で管理するところへ向かうための

前知識になります。

 

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

 

 

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

★メインのinoファイル

/*
 * Author: SAI
 */

/* *** Include *** */
#include <TimerOne.h>
/* 注意
   TimerOne.h を使用するためには、
   スケッチ -> ライブラリのインクルード -> ライブラリの管理
   から、TimerOne をインストールしておく必要があります。
*/

// 7Seg LED 表示Function //
#include "DisplayFunction.h"

/* *** Define 定義 *** */
/* 1桁の表示時間 */
const long TIMER_DELAY = (5 * 1000);
/* 1秒にかかるカウント数 */
const long TIMER_CNT_SECOUND = ((long)1000 * 1000) /(TIMER_DELAY);

/* 砂時計用内部カウンター */
long gTimerCounter;
long gDispTime;

/* initial setting 初期設定 */
 void setup() {
  initialize();
  /* タイマー用のカウンターを初期化 */
  // Timer setting  タイマー設定
  Timer1.initialize(TIMER_DELAY); //マイクロ秒単位で設定 
  Timer1.attachInterrupt(timerFire); // コールバック関数設定 
  
  gTimerCounter = 0;
  gDispTime = 0;

  // Lチカ用 //
  pinMode(A0, OUTPUT);
}

/* Mail loop 通常処理(繰り返し) */
void loop() {
  delay(1000);
}

/*
 * タイマー割り込み処理
 * 100ms(=100000us)毎にここが呼び出される */
void timerFire() {
  // 時間計測 //
  gTimerCounter++;
  if(gTimerCounter >= (long)10000*1000)
  {
    gTimerCounter = 0;
  }
  /* 100ms単位の時間に変換 */
  int lTime = gTimerCounter / (TIMER_CNT_SECOUND /10 ) ;

  if(gDispTime != lTime)
  {
    gDispTime = lTime;
    /* 桁上がりした時だけ、1msの処理をする */
    delay(1);
  }

  /* 機能1 7Seg表示 */
  DisplayDraw();

 

  /* 機能2 LChika表示 */
  DisplayLedChika();

}
void DisplayLedChika()
{
  /* 100ms単位の時間に変換 */
  int lTime = gTimerCounter / (TIMER_CNT_SECOUND /1 ) ;

  if((lTime % 2) == 1)
  {
    analogWrite(A0, 255);
  }
  else
  {
    analogWrite(A0, 0);
  }
}

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

DisplayFunction用のinoファイル



void initialize() {
  /* IO output setting  IO出力設定 7Seg用端子を出力 */
  pinMode(ASeg, OUTPUT);
  pinMode(BSeg, OUTPUT);
  pinMode(CSeg, OUTPUT);
  pinMode(DSeg, OUTPUT);
  pinMode(ESeg, OUTPUT);
  pinMode(FSeg, OUTPUT);
  pinMode(GSeg, OUTPUT);
  pinMode(DotSeg, OUTPUT); 

  pinMode(Keta1Seg, OUTPUT);
  pinMode(Keta2Seg, OUTPUT);
  pinMode(Keta3Seg, OUTPUT);
  pinMode(Keta4Seg, OUTPUT);

  digitalWrite(Keta1Seg, LED_KETA_OFF);
  digitalWrite(Keta2Seg, LED_KETA_OFF);
  digitalWrite(Keta3Seg, LED_KETA_OFF);
  digitalWrite(Keta4Seg, LED_KETA_OFF);

  gTimerCounter  = 0;
  gDispTime = 0;

}

//Display function 0
void zero() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_ON);
  digitalWrite(FSeg, LED_ON);
  digitalWrite(GSeg, LED_OFF);
}

//Display function 1
void one() { 
  digitalWrite(ASeg, LED_OFF);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_OFF);
  digitalWrite(ESeg, LED_OFF);
  digitalWrite(FSeg, LED_OFF);
  digitalWrite(GSeg, LED_OFF);
}

//Display function 2
void two() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_OFF);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_ON);
  digitalWrite(FSeg, LED_OFF);
  digitalWrite(GSeg, LED_ON);
}

//Display function 3
void three() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_OFF);
  digitalWrite(FSeg, LED_OFF);
  digitalWrite(GSeg, LED_ON);
}

//Display function 4
void four() {
  digitalWrite(ASeg, LED_OFF);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_OFF);
  digitalWrite(ESeg, LED_OFF);
  digitalWrite(FSeg, LED_ON);
  digitalWrite(GSeg, LED_ON);
}

//Display function 5
void five() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_OFF);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_OFF);
  digitalWrite(FSeg, LED_ON);
  digitalWrite(GSeg, LED_ON);
}

//Display function 6
void six() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_OFF);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_ON);
  digitalWrite(FSeg, LED_ON);
  digitalWrite(GSeg, LED_ON);
}

//Display function 7
void seven() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_OFF);
  digitalWrite(ESeg, LED_OFF);
  digitalWrite(FSeg, LED_OFF);
  digitalWrite(GSeg, LED_OFF);
}

//Display function 8
void eight() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_ON);
  digitalWrite(FSeg, LED_ON);
  digitalWrite(GSeg, LED_ON);
}

//Display function 9
void nine() {
  digitalWrite(ASeg, LED_ON);
  digitalWrite(BSeg, LED_ON);
  digitalWrite(CSeg, LED_ON);
  digitalWrite(DSeg, LED_ON);
  digitalWrite(ESeg, LED_OFF);
  digitalWrite(FSeg, LED_ON);
  digitalWrite(GSeg, LED_ON);
}

//Display function Keta1
void Keta1() {
  digitalWrite(Keta1Seg, LED_KETA_ON);
  digitalWrite(Keta2Seg, LED_KETA_OFF);
  digitalWrite(Keta3Seg, LED_KETA_OFF);
  digitalWrite(Keta4Seg, LED_KETA_OFF);
}
//Display function Keta2
void Keta2() {
  digitalWrite(Keta1Seg, LED_KETA_OFF);
  digitalWrite(Keta2Seg, LED_KETA_ON);
  digitalWrite(Keta3Seg, LED_KETA_OFF);
  digitalWrite(Keta4Seg, LED_KETA_OFF);
}
//Display function Keta3
void Keta3() {
  digitalWrite(Keta1Seg, LED_KETA_OFF);
  digitalWrite(Keta2Seg, LED_KETA_OFF);
  digitalWrite(Keta3Seg, LED_KETA_ON);
  digitalWrite(Keta4Seg, LED_KETA_OFF);
}
//Display function Keta4
void Keta4() {
  digitalWrite(Keta1Seg, LED_KETA_OFF);
  digitalWrite(Keta2Seg, LED_KETA_OFF);
  digitalWrite(Keta3Seg, LED_KETA_OFF);
  digitalWrite(Keta4Seg, LED_KETA_ON);
}

// 数字振り分け .
void DigitDisp(int aNum)
{
  int lDigit = aNum % 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;
  }
}



void DisplayDraw()
{
  /* 桁切り分け */
  int DispKeta = gTimerCounter  % 4 ;
  /* 100ms単位の時間に変換 */
  int lTime = gTimerCounter / (TIMER_CNT_SECOUND /10 ) ;

  switch(DispKeta)
  {
    case 0:
     /* 1桁を表示 */
      DigitDisp(lTime);
      digitalWrite(DotSeg, LED_OFF);  
      Keta1();
      break;
    case 1:
     /* 2桁を表示 */
      digitalWrite(DotSeg, LED_ON);  
      DigitDisp(lTime/10);
      Keta2();
      break;
    case 2:
     /* 3桁を表示 */
      digitalWrite(DotSeg, LED_OFF);  
      DigitDisp(lTime/100);
      Keta3();
      break;
    case 3:
    default:
     /* 4桁を表示 */
      digitalWrite(DotSeg, LED_OFF);  
      DigitDisp(lTime/1000);
      Keta4();
      break;
  }
}

 

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

 

// DisplayFunction.hファイル

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

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 int LED_ON = LOW;
const int LED_OFF = HIGH;

const int LED_KETA_ON = HIGH;
const int LED_KETA_OFF = LOW;