こんにちは、「メカのりまき」です。

この記事では、タイマ割り込みについて説明した後、「8分タイマ」の制作について説明したいと思います。

この記事では、関数と割り込み処理についての基礎知識が必要になります。

関数と割り込み処理が良く分かっていないという方は、以下のリンク先の記事で説明をしていますので、先にご覧になることをお勧めします。

本記事で使用するもの

記事前半のタイマ割り込みの説明では以下のものを使用しています。

  • 「Arduino IDE2.0.4」をインストールしたPC(Windows11)
  • USBケーブル
  • Arduino Uno R3(ELEGOO UNO R3でも可)

また、後半の「8分タイマ」の制作では、上記のものに加え、以下のものを使用しています。

  • ブレッドボード
  • LED
  • 抵抗器
  • パッシブブザー
  • タクトスイッチ
  • ジャンパーワイヤー

「Arduino Uno R3」の互換ボードであれば、「ELEGOO UNO R3」以外のボードでも、本記事と同様な手順で動作可能だと思われますが、動作検証はしていないので、その点はご了承ください。

以下のリンクのスターターキットを購入した場合、上記で述べた使用するものは、PC以外すべて入手することができます。

Arduinoをまだ購入していないという方やArduinoで動かすことができるたくさんのパーツが欲しいという方は、購入を検討してみてください。

タイマ割り込みの説明

「タイマ割り込み」は、指定した時間が過ぎるごとに、現在の処理を中断して、指定した関数を実行するという機能です。

タイマ割り込みの概要

時間の経過は、「クロック」というものと「タイマ」というものを利用して行います。

「クロック」とは処理のタイミングを合わせるために利用されるパルス信号であり、一定の周期で「HIGH」になったり「LOW」になったりを繰り返します。

クロック信号の図

そして、「タイマ」はそのクロックのパルス信号に応じて、カウントをしています。

カウントの仕方はマイコンや設定により異なっていて、例えばクロックの立ち上がりが起きるたびに、カウント上げるような設定だとすると、以下のようにカウントが上がっていくということになります。

クロック信号とタイマのカウント

ここで、クロックは一定の周期で、「HIGH」と「LOW」を繰り返しているため、「タイマ」のカウント数から時間が分かるということになります。

時間が分かる仕組み

ただし、「タイマ」のカウントには上限があり、計れる時間には限りがあります。

よって、カウントが上限に達したときは、カウントが「0」に戻るような処理やカウントを下げていくような処理が行われます。

カウントの上限に達したとき

また、上限に達する前に「0」に戻るようにすることも設定によっては可能になります。

指定した値で0に戻る

ここで、改めてカウンタの波形を見てみると、設定が常に同じ場合、一定の波形を繰り返しているのが分かると思います。

「タイマ割り込み」は、その性質を利用し、カウント数が上限に達したときや指定した値になったとき、割り込みを発生させ、関数の実行を行っています。

割り込み処理とタイマのカウント

Arduinoとタイマ割り込み

「Arduino Uno R3」には「Timer0」、「Timer1」、「Timer2」の3つのタイマがあり、これらを設定することにより、「タイマ割り込み」を行うことができます。

しかし、「Arduino IDE」では、「タイマ」の設定を行うための関数は用意されておらず、「タイマ割り込み」を行えるようにするには、「レジスタ」というものを操作する記述をする必要があります。

以下のスケッチは「レジスタ」の操作で「タイマ」を設定し、4番ピンの「HIGH」と「LOW」を切り替えるものです。

「レジスタ」で設定する方法を理解していれば、自由自在に割り込み処理の設定を行うことができるので、一番良いのですが、「レジスタ」の対応はマイコンの種類により異なるため、扱う際はマイコンについてよく調べる必要があります。

さらに、処理をするための文を書くために、「ビット演算」というものを理解する必要があるため初心者にとっては少しハードルが高いです。

そこで、今回の記事では、上記のように「レジスタ」の操作をして、タイマ割り込みを利用する方法ではなく、ライブラリを利用することで、簡単に「タイマ割り込み」が行える方法について、説明したいと思います。

MsTimer2の説明

タイマ割り込みが行えるライブラリはいくつかあるのですが、今回は「MsTimer2」というライブラリについて説明したいと思います。

「MsTimer2」ライブラリの「MsTimer2::set()」という関数は、ms単位の時間と割り込みで実行したい関数を引数にしており、指定した時間になったら、指定した関数を実行するというタイマ割り込みが行えるように設定をしてくれます。

MsTimer2の概要

ライブラリの中身については分からなくても、タイマ割り込みを行えるのですが、まったく分からないまま使用するのは嫌だという方がいらっしゃるかもしれないので、「MsTimer2::set()」の仕組みについて、もう少しだけ詳しく説明したいと思います。

まず、「MsTimer2::set()」という関数を使うと、レジスタを操作する処理を行い、1[ms]ごとに割り込み処理を行うように、「Timer2」の設定が行われます。

MsTimer2とカウント

そして、割り込み処理が行われるたびに、1[ms]を表すためのカウント(タイマで使用しているのと別物)を増やしていき、ユーザが指定した時間([ms]単位)になったら、指定した関数を実行して、その後、再スタートするという仕組みになっています。

MsTimer2の仕組み

よって、純粋な「タイマ割り込み」とは少し異なりますが、指定した時間ごとに割り込みを行うという点は同じになるということになります。

MsTimer2のインストール

「MsTimer2」は、「Arduino IDE」に初めから付属しているライブラリではないため、利用するためには、インストールを行う必要があります。

インストールする方法は以下の通りです。

STEP1

ライブラリマネージャを開く。

以下の画像は「Arduino IDE2.0.4」のものです。

ライブラリマネージャを開く

古いバージョンを使用している場合は、「ツール」タブをクリック後、「ライブラリの管理」をクリックしてください。

STEP2

「MsTimer2」で検索をして、「MsTimer2」を探す。

MsTimer2で検索

STEP3

「インストール」をクリックする。

インストールをクリック

MsTimer2の使用例

では、実際に「MsTimer2」を利用する方法について、説明をしたいと思います。

以下のスケッチを書き込んでシリアルモニタを確認してみてください。

すると、始めに「(-_-)」と表示された後、ゆっくりと「z」が追加され最終的に「(-_-)zzZZ」の状態で表示が止まるのが確認できます。

(-_-)zzZZと表示される

最初にシリアルモニタを開いていない場合、「z」だけ出力されたり、何も表示されなかったりすることがあります。

その場合は、シリアルモニタを開いた状態で「Arduino Uno R3」のリセットボタンを押すと、再度スケッチの初めから処理が実行されるので、正しく表示されます。

Arduino Unoのリセットボタンの写真

それでも、表示が上手くいかない場合は、スケッチやシリアルモニタの設定を確認をしてみてください。

スケッチの解説

まずは、「MsTimer2」に関係する処理について説明していきたいと思います。

まず、スケッチの2行目で、以下のような文が記述されています。

上記の文では「MsTime2」ライブラリを読み込み、ライブラリに登録されている関数を利用できるようにしています。

次に、22行目では以下の文が記述されています。

上記の文では、1000[ms](1秒)ごとに、「Count_UP」という関数が実行されるように、タイマ割り込みの設定を行っています。

ここで指定する関数は、外部割り込みのときと同様、値を返さない「void型」で、さらに引数を持たないことが条件になります。

実行したい関数の条件

関数の中身についての注意点も外部割り込みのときと同様で、割り込み処理を利用した関数が利用できなかったり、「void loop()」と「割り込み処理」で同時にシリアルモニタへ表示させる処理が行われると表示がおかしくなったり、関数の中と外で共通の変数に対して「volatile」を付けないと意図しない処理になったりします。

タイマ割り込みを行う仕組みについては、前の節で説明した通りです。

次に、25行目では以下の文が記述されています。

実は、「MsTimer2::set()」は「Timer2」の設定を行っているだけであり、タイマ割り込みは開始されていません。

上記の「MsTimer2::start()」では、カウントを初期化し、タイマ割り込みの許可をすることで、タイマ割り込みを開始しています。

最後に63行目では以下の文が記述されています。

上記の「MsTimer2::stop()」は、タイマ割り込みを終了させる文です。

これにより、「Timer2」のタイマ割り込みが禁止され、1[ms]ごとのカウントが止まります。

「MsTimer2」に関係する処理についての説明は以上になります。

スケッチ全体の処理をまとめると以下の図のようになります。

(-_-)zzZZと表示させるスケッチの流れ

ここで、19行目では「Serial.print(“\n(-_-)”);」と書いているのに、「\n」が表示されないのは、この部分が改行コードを表しているからです。

これにより、改行してから、「(-_-)」の表示を行っています。

MsTimer2の注意点

「MsTimer2」は「Timer2」を利用して割り込み処理行っているため、同じ「Timer2」を使うような関数を同時に実行しようとすると、動作がおかしくなったり、どちらかが動作しなくなったりします。

例えば、「Timer2」を利用したものとして、「tone()関数」や「Arduino Uno R3」の3番ピンと11番ピンの「PWM制御(analogWrite())」などがあります。

これらの関数を扱う際、「Timer2」はそれぞれの関数に合わせて設定されるため、同時に扱うことはできなくなるということになります。

ただし、同時に使用しないように、うまく実行させれば、同じスケッチ内で同じ「タイマ」を使用した関数を複数使用することが可能な場合があります。

例えば、以下で説明する「8分タイマ」は、「MsTimer2」と「tone()関数」を同じスケッチ内で使用しています。

タイマ割り込みについての説明は以上となります。

8分タイマの制作

ここからは「8分タイマ」の制作について説明したいと思います。

回路は前回、前々回の記事で制作した「右往左往ゲーム」の回路をそのまま使用します。

右往左往ゲームの回路

「右往左往ゲーム」の詳しい説明については、以下のリンク先の記事をご覧になってください。

「8分タイマ」の制作では、「右往左往ゲーム」を作った際の知識も利用するため、先に「右往左往ゲーム」についての記事をご覧になることをお勧めします。

8分タイマの概要

「右往左往ゲーム」の回路をそのまま再利用して、8分までの時間を計測できる「8分タイマ」を制作したいと思います。

「8分タイマ」の内容は、左のスイッチを押すことで時間の設定、右のスイッチを押すことで計測の開始をし、時間が経過したらアラームを鳴らすというものです。

ここで、アラームはスイッチを押すまで、鳴り続けるものとします。

また、時間の設定や時間の経過は、LEDの点灯により行います。

8分タイマの概要

時間の計測が開始された後に、計測をやめたいとき、「Arduino Uno R3」のリセットボタンを押してリセットすることも可能ですが、今回は回路上のスイッチを押すことにより、リセットが行えるようにしたいと思います。

スケッチの構造を考える

ここでは、上記の「8分タイマ」を作成するために、スケッチをどのような手順で考えたのかを説明したいと思います。

まず、「8分タイマ」の大まかな処理としては、「時間の設定」、「時間を計る」、「アラームを鳴らす」の3つに分けることができます。

そこで、今回はそれぞれを「mode=1」、「mode=2」、「mode=3」とし、スイッチが押されたときや時間が経過したときに「mode」を切り替え、それぞれの処理を行うようにしたいと考えました。

大まかな処理の流れは以下のようになります。

8分タイマの大まかな処理

大まかな流れが決まったので、次に、それぞれの「mode」で行いたいことや「mode」の切り替わる条件をまとめました。

まとめた結果が以下のようになります。

8分タイマで行いたいことのまとめ

最後に、上記で決めたことをするために、具体的にどのような処理をすればいいのかをまとめました。

まとめた結果が以下のようになります。

8分タイマの処理のまとめ

これで、スケッチの構造は決まったので、後はそれをスケッチにまとめていくだけです。

1分後にアラームを鳴らす

まずは、全体の構造を作りつつ、1分間だけカウントできるようなスケッチを作成します。

今回作成するのは、以下の図の赤枠で囲った部分になります。

1分間を測定するための流れ

以下のスケッチを書き込んでください。

上記のスケッチを書き込むと、以下の動画のように、1分間だけ時間の計測が行えることが確認できます。

スケッチの解説

まずは、「void loop()」の部分から説明したいと思います。

「void loop()」では、各「mode」に応じた処理を行うために、if文を使い、各ブロックへ分岐をさせています。

大まかな処理の流れは以下のようになります。

modeに合わせて処理を変える仕組み

そして、分岐したブロックの中で、左右のスイッチが押されているかどうかを、if文で判別し、さらに処理を分岐させる構造となっています。

modeとスイッチの状況で処理を変える仕組み

スイッチが押されているかの判別は、変数「Pushed_Flag_R」、「Pushed_Flag_L」が「true」になっているかによって行われています。

上記の変数「Pushed_Flag_R」、「Pushed_Flag_L」は、スイッチが押されたときの外部割り込みにより「true」になり、実行させたい処理が終わった後に「false」に戻ります。

ここで、スケッチ全体の構図を見ると以下のようになっています。

スケッチ全体の構造

次に、スイッチが押されたとき実行する関数やタイマ割り込みで実行される関数について説明したいと思います。

まず、時間の計測を開始する関数「Start_Count」です。

この関数の処理の流れは以下のようになります。

Start_Countの処理の流れ

これにより、タイマ割り込みが開始され、「mode」は「2」に切り替わります。

最後の「delay」は、チャタリングにより、もう一度「Pushed_Flag_R」が「true」になってしまうことを防ぐために入れています。

チャタリング対策の説明

次に、タイマ割り込みで実行する関数であり、時間の測定をする「Count_Up」についてです。

この関数の処理の流れは以下のようになります。

Count_Upの処理の流れ

この関数は、タイマ割り込みにより、1秒ごとに実行されるため、1秒ごとに「count」が「1」増えていくということになります。

よって、「count」が「60」になったときが、1分の経過をあらわしているため、「count」が「60」以上になったときに、「count_minute」に対応したLEDを消灯させ、「count_minute」を減らしています。

ここで、さらに、「count_minute」が「0」になったときは、設定した時間が経過したことをあらわしているため、タイマ割り込みを終了させ、「mode」を「3」にしています。

また、「count」が「60」以上でないときは、1秒ごとにLEDの点灯、消灯を切り替えて時間の計測途中であることを知らせるようにしています。

if文の条件である「count%2==0」は「count」が偶数のとき「true」、奇数のとき「false」になります。

よって、「count」が偶数のときLEDが点灯、奇数のときLEDが消灯するということになります。

次に、アラームを鳴らす関数である「Alarm」についてです。

この関数の処理の流れは以下のようになります。

Alarmの処理の流れ

この関数はブザーを鳴らして、待機をするだけです。

割り込み処理によって、「tone()関数」の波形が乱れたとしても、音の変化を感じることはないとおもいますが、念のため、関数の最初と最後で外部割り込みの禁止を行っています。

この関数は、「mode」が切り替わるまで、「void loop()」により、繰り返されるため、スイッチが押されるまで、ブザーを鳴らし続けます。

最後に、アラームを止める関数である「Stop_Alarm」についてです。

この関数の処理の流れは以下のようになります。

Stop_Alarmの処理の流れ

これにより、LEDの状態は最初の状態に戻されて、「mode」が「1」になるため、アラームが止まり、最初の状態に戻ります。

時間の設定を行えるようにする

上記のスケッチが上手く動作していれば、後は関数を追加していくだけです。

まず、時間の設定を行う関数を追加します。

以下のスケッチを書き込んでください。

黄色でマークされている部分が変更または追加した部分です。

上記のスケッチを書き込むと、以下の動画のように、左のスイッチで時間の設定が行えるようになるのが確認できます。

スケッチの解説

「mode=1」で、左のスイッチが押されたとき、新しく作成した「Count_Plus_Minute」という関数を実行させるように変更を加えています。

新しく作成した「Count_Plus_Minute」の処理の流れは以下のようになります。

Count_Plus_Minuteの処理の流れ

これにより、左のスイッチが押されると、「count_minute」の値が増え、ブザー音と共に「count_minute」に対応したLEDが点灯します。

また、if文により、「count_minute」が「8」よりも大きい場合は、「count_minute」を「1」に戻すような処理も行っています。

リセットを行えるようにする

次は、時間のリセットを行う関数を追加します。

以下のスケッチを書き込んでください。

黄色でマークされている部分が変更または追加した部分です。

上記のスケッチを書き込むと、以下の動画のように、時間を計測している途中でリセットが行えるようになるのが確認できます。

スケッチの解説

「mode=2」で、左右どちらかのスイッチが押されたとき、新しく作成した「Reset_Count」という関数を実行させるように変更を加えています。

新しく作成した「Reset_Count」の処理の流れは以下のようになります。

Reset_Countの処理の流れ

これにより、スイッチが押されると、ブザー音と共に、LEDが最初の状態にリセットされます。

アラームを自分なりにアレンジする

上記のスケッチで「8分タイマ」のシステムは完成ですが、アラームが少し寂しいので、最後にアラームを鳴らす演出を自分で考えて変更を加えてみてください。

以下のスケッチは、LEDを点灯させながら、音を鳴らす例です。

LEDを点灯させる場合は、最後に全て消灯させるように気を付けてください。

黄色でマークされている部分が変更または追加した部分です。

上記のスケッチを書き込んだ場合、以下の動画のように動作します。

「8分タイマ」の制作についての説明は以上です。

終わりに

今回は前回の記事の外部割り込みに引き続き、タイマ割り込みの使い方について説明しました。

割り込み処理はとても便利で使いこなせるようになると、作れるものの幅が広がります。

扱いが難しいため、スケッチのミスに気付かず、思ったように動作しないことも、多々あるとは思いますが、なぜ思ったように動作しないのかを、よく考えて直すことで、スケッチを書く技術が上がってくると思います。

「右往左往ゲーム」と「8分タイマ」を参考にたくさん挑戦してみてください。

次回は、「LCDモジュール(1602A)」に文字を出力させる方法について、説明したいと思います。

本記事はここまでです。ご清覧ありがとうございました。

広告

楽天モーションウィジェットとGoogleアドセンス広告です。

気になる商品がございましたら、チェックをしてみてください。