質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
M5Stack

M5Stackは、小型のマイコンモジュールです。拡張モジュールが豊富に用意されており、センサと組み合わせることで測定機能を自由に追加することができます。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

3回答

2967閲覧

Arduino(M5StickC-Plus)タイマー割込み処理中のdelay処理を行う方法はありますか?

Yasu0421

総合スコア37

M5Stack

M5Stackは、小型のマイコンモジュールです。拡張モジュールが豊富に用意されており、センサと組み合わせることで測定機能を自由に追加することができます。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2022/05/29 08:54

動作環境・問題点

ESP32picoを搭載したArduino-IDEで動作するM5StickC-Plusを使用しています。
M5StickC-Plus表面にある「ボタンA」を押下した時点で
①sec毎にタスクが実行されるタイマー割込みを起動
②そのタスクにおいてGPIO25ピンから割込み開始時から50mSecだけHighを出力
③同時にそのタスクにおいてLEDの出力を反転させる処理を実行
するようなプログラムを作成しております。上記②の処理で「delay(50)」関数を用いて
50mSec後にGPIO25ピンを反転させる処理をおこなっているのですが、実行してくれず困っております。
素人のご質問で大変恐縮ではございますが、ご教示いただけますと幸いです。

実現したいこと

実現したいことは
①ボタンAを押下すると、1秒毎のタイマー割込みの動作が開始されます
②タイマー割込み処理では以下の処理を実施したいです。
・タイマー割込み処理毎にLED(GPIO10)の出力を反転させる処理を行う
・タイマー割込み処理開始時に50mSec間だけGPIO25ピンからHighを出力させる
タイミングを図示すると以下の様になります。
イメージ説明
③ボタンBを押下すると、①の動作を停止する

です。

発生している問題

下記の「該当のソースコード」を実行し、シリアルモニターで動作を確認しましたところ

HOME_BTN was pressed...... 16:54:06.344 -> start_time =6036 16:54:06.344 -> delay_time =6036 16:54:06.377 -> end_time =6036

と、タスク内で実行した時刻が全て同じに表示されており、GPIO25からの出力もHighのまま変化してくれません

該当のソースコード

#include <M5StickCPlus.h> hw_timer_t * timer1 = NULL; int onemicro; unsigned long exe_time; void IRAM_ATTR onTimer() { exe_time = millis(); Serial.print("start_time ="); Serial.println(exe_time); digitalWrite(GPIO_NUM_10, !digitalRead(GPIO_NUM_10)); digitalWrite(25, 1);//OE on delay(100); exe_time = millis(); Serial.print("delay_time ="); Serial.println(exe_time); digitalWrite(25,0); //OE off exe_time = millis(); Serial.print("end_time ="); Serial.println(exe_time); Serial.println("-------"); } void setup() { M5.begin(); Serial.begin(9600); pinMode(25, OUTPUT); pinMode(GPIO_NUM_10, OUTPUT); onemicro =getApbFrequency()/1000000; timer1 = timerBegin(0, onemicro, true); timerAttachInterrupt(timer1, &onTimer, true); } void loop() { M5.update(); // Buttonの状態更新のため必要 if(M5.BtnA.wasPressed()){ Serial.println("HOME_BTN was pressed......"); timerAlarmWrite(timer1, 1000000, true); //1000msec毎に実施 timerAlarmEnable(timer1); } if(M5.BtnB.wasPressed()){ Serial.println("B-BTN was pressed......"); timerAlarmDisable(timer1); } }

試したこと

もう1つタイマーを設けて並列処理を考えてみましたが、やはり50mSec後に変化させる必要があるため、上記と同様の問題に直面しました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

自分のやりたいことを見失わないようにしましょう。やりたいのは1秒毎に50ms幅のパルスを出すことであって、「タイマー割込み処理中のdelay処理を行う」ことでは無いはずです。割り込み中にdelay()を行う方法を考え始めてしまうと、本来手段であったはずのことが目的化してしまったりしますので。

とりあえず「割り込み中にはdelay()は使えない」は原則と考えてください(使えないし、使わない)。割込み禁止を解除してやる、なんていう方法はあるでしょうが、その弊害とか一般性とかを考えるなら、決して他に優先して考える方法ではないでしょう。

で。ざっと考えるなら考えられる方法はいくつか。

  1. 50ms毎にタイマー割り込みをかけて、割り込み発生回数をカウントする。0回目でGPIO10を反転し、GPIO25を立てる。1回目でGPIO25を降ろす。20回目でGPIO10を反転し、GPIO25を立てる。ついでにカウンタをクリアして、1回目でGPIO25を降ろす。以下繰り返し。

  2. 50ms毎にタイマー割り込みを設定し、GPIO10を反転し、GPIO25を立てる。割り込み発生でGPIO25を降ろして、タイマーを950msに設定する。割り込みでGPIO10を反転し、GPIO25を立てて、タイマーを50msに設定する。

  3. 通常コンテキストのloop()が軽くて、1sや50msのパルス幅の精度要求が緩いなら、1000msの割り込みではフラグを一つ立てるだけ。通常コンテキストで割り込み受付のフラグが立っていたら、GPIO10を反転し、GPIO25を立てる。delay(50)の後、GPIO25を降ろす。

話は逸れますが、そもそもとして「割り込みハンドラ中では他の割り込みは禁止になる」のであって、使えないのはdelay()だけではありません。millis()も多分カウントを停止するはずだし、Serialなんかも割り込みを使う(ことがある)のでダメです。というか、そもそも割り込み中で50msとか滞留しようなんていうことは考えるだけでもダメです。割り込み中でSerialなど時間のかかる(かもしれない)入出力もイケマセン。割り込みからは速やかに復帰すること、これが原則(他の割り込みを阻害するから、というのが理由ですが)。

ということで、2.の場合のプログラム例など。手持ちがM5Stick-Cなので一部書き換えています。

Arduino

1#if 1 2#include <M5StickC.h> 3const int pulse1s = GPIO_NUM_32; 4const int pulse50ms = GPIO_NUM_26; 5#else 6#include <M5StickCPlus.h> 7const int pulse1s = GPIO_NUM_10; 8const int pulse50ms = GPIO_NUM_25; 9#endif 10hw_timer_t* timer1 = NULL; 11int onemicro; 12volatile unsigned long exe_time; 13const char * volatile msg=""; 14volatile int cnt = 0; 15 16const char* MESSAGE[]={"Start time","end time"}; 17 18void IRAM_ATTR onTimer() { 19 if (cnt == 0) { 20 timerAlarmWrite(timer1, 50 * 1000, true);//本当は第3パラメータはfalseにしたいのだけれどタイマーを再キックする方法がわからん 21 digitalWrite(pulse1s, !digitalRead(pulse1s)); 22 digitalWrite(pulse50ms, true); // OE on 23 } else { 24 timerAlarmWrite(timer1, 950 * 1000, true); 25 digitalWrite(pulse50ms, false); 26 } 27 exe_time = millis(); 28 msg = MESSAGE[cnt]; 29 cnt ^= 1; 30} 31void setup() { 32 M5.begin(); 33 Serial.begin(115200); 34 pinMode(pulse50ms, OUTPUT); 35 pinMode(pulse1s, OUTPUT); 36 37 onemicro = getApbFrequency() / (1000 * 1000); 38 timer1 = timerBegin(0, onemicro, true); 39 timerAttachInterrupt(timer1, &onTimer, true); 40} 41void loop() { 42 M5.update(); // Buttonの状態更新のため必要 43 if (M5.BtnA.wasPressed()) { 44 Serial.println("HOME_BTN was pressed......"); 45 cnt=0; 46 onTimer(); 47 timerAlarmEnable(timer1); 48 } 49 if (M5.BtnB.wasPressed()) { 50 Serial.println("B-BTN was pressed......"); 51 timerAlarmDisable(timer1); 52 } 53 if (msg) { 54 Serial.println(msg); 55 Serial.println(exe_time); 56 msg=nullptr; 57 } 58}

投稿2022/05/29 12:35

thkana

総合スコア7610

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yasu0421

2022/05/30 06:34

丁寧な回答いただきありがとうございます。 目的のシーンにおける実現手段の3パターンの考え方 またスケッチも検討いただき、大変助かります。 勉強になります。 参考にさせていただきます。
guest

0

ベストアンサー

millis関数は、タイマ割り込みを使って実装しています。
そして、delay関数も、こいつを使って実装されているため、ユーザ側でタイマ割り込みを使ってしまうとこれらの関数は動きません。(あるいは動作がおかしくなる)

delayMicroseconds関数は、単なる空ループで時間待ちをしているため、タイマには影響されません。

投稿2022/05/29 11:53

y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2022/05/29 12:14

んで、タイマ割り込み中で時間待ちをする、という実装はよろしくありません。 1秒ごとに、50ms処理が止まるとなってしまったら、他の処理ができなくなります。 シリアル通信などをさせる場合、通信の取りこぼしをする可能性も出てきます。 #まあ、動作がこれだけ、というなら、意図通りに動作するんでこれはこれでもいいんですが。 こういう動作をさせる場合は、(例えば)1msごとのタイマ割り込みを実装し、タイマ割り込み中でカウントさせ、50カウントと、1000カウントとでポート操作やらなんやらの処理させればすみます
Yasu0421

2022/05/29 12:26

貴重なコメントありがとうございます。 ご指摘の点、ごもっともです。(実はこの点も気にしておりました) カウント単位での処理・・・理解しました。 こちらで実装してみたいと思います。 とても、参考になりました。 ありがとうございました。Orz
guest

0

delay(50)の代わりに

delayMicroseconds(50000);

とすると、

19:48:54.613 -> start_time =20849 19:48:54.661 -> delay_time =20899 19:48:54.661 -> end_time =20899

と希望通りの動作を得ることができるようになりました。
理由はわかりませんが・・・

投稿2022/05/29 10:51

Yasu0421

総合スコア37

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thkana

2022/05/29 12:37

delayMicroseconds()がスピンロックだということではないですか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問