2

最初のプロジェクトとして、さまざまなライトモードでteensyduinoアンビエントライトを作成することを計画しています。これは、大きなswitchステートメントでチェックされます。今度は、ボタンを押して、あるモードから別のモードに切り替えたいと思います。

グーグルで割り込みを使用するようになりましたが、明確ではない点が1つあります。時間がかかり、多くの変数が使用されている高価な関数でボタンを押すと、割り込みからメインループを呼び出すとどうなりますか。残りの状態はRAMに残り、何度も切り替えたり、クリアしたりすると、スタックオーバーフローが発生します。

ここにいくつかのコードがあります:

const int speed = 30 //milliseconds
const int modes = 11; //maximum number of modes
const int red   = 15;
const int green = 14;
const int blue  = 12;

volatile int mode  = 0;

void setup() {
    pinMode(red   , OUTPUT);
    pinMode(green , OUTPUT);
    pinMode(blue  , OUTPUT);
    randomSeed(analogRead(0));
    Serial.begin(9600);
    attachInterrupt(0,incMode,CHANGE); // 0 -> digital pin 2
}
void loop()  {
    switch(mode){
        case 0:{
            Serial.println("powerdown");
            setAll(0);
            delay(1000);
            break;
        }
        \\...
        case modes:{
            \\ expensive long function
        }
    }
}
void blinkAll(int times){
    for(int i=1;i <= times;i++){
        setAll(255);
        delay(speed*17);
        setAll(0);
        delay(speed*17);
    }
}

void setAll(int bright){
        analogWrite(red   , bright);
        analogWrite(green , bright);
        analogWrite(blue  , bright);
}
void incMode(){
    delay(speed);
    blinkAll(2); //to indicate mode has changed
    mode = (mode+1) % (modes+1); //switch starts with 0 so use "% modes+1"!
    Serial.println("mode increased");
    //--> loop();
    //--> would resume the main loop but lead to a stackoverflow i presume
}

遅延やスタックの汚染なしに、実行中の機能から抜け出すにはどうすればよいでしょうか。モードを設定して機能が終了するまで待つこともできますが、終了までに数分かかるモードがある場合は、すぐに切り替えられるようにしたいと思います。

PS:私はteensyduinoを使用していますが、arduinoタグを使用します。また、arduinioがタグc /c++を使用する言語がわからないためです。適切でない場合は、これを変更してください。

4

1 に答える 1

1

割り込みハンドラからmainを再帰的に複数回再入力すると、最終的にスタックがオーバーフローします。さらに、ハードウェアに関する限り、割り込みハンドラーにいるので、あらゆる種類の奇妙なことがあります。特に、すでに割り込みが発生している場合、割り込みはブロックされます。つまり、機能しdelay()ません。カウントアップせmillis()ず、手動で割り込みを再度有効にする方法を見つけない限り、他のさまざまなものも同様に壊れます。

これを解決するためのより良い方法は、代わりに「高価な長い関数」を、非常に頻繁に呼び出される安価で短い関数によって駆動されるステートマシンにすることです。割り込みハンドラは、この関数へのエントリ時にチェックされるフラグを設定するだけで、その時点で現在のモード(つまり、現在のステートマシン)が変更されます。

このアプローチにより、新しい照明モードの定義も簡単になります。たとえば、次のように定義できます。

struct phase {
  unsigned char r, g, b, delay;
};

unsigned long t_nextPhase;
volatile struct phase *forceMode = NULL;
struct phase *mode = blinkAll;
int nextPhase = 0;

struct phase blinkAll[] = {
  { 255, 255, 255, 17 },
  { 0, 0, 0, 17 },
  { 0, 0, 0, 255 } // loop sentinel
};

void lighting_kernel() {
    noInterrupts(); // ensure we don't race with interrupts
    if (forceMode) {
        mode = forceMode;
        forceMode = NULL;
        t_nextPhase = millis();
        nextPhase = 0;
    }
    interrupts();

    if (t_nextPhase > millis()) {
        return;
    }

    struct phase *cur_phase;
    do {
        cur_phase = mode[nextPhase++];
        if (cur_phase->delay == 255) {
            nextPhase = 0;
        }
    } while (cur_phase->delay == 255);

    analogWrite(red   , cur_phase->r);
    analogWrite(green , cur_phase->g);
    analogWrite(blue  , cur_phase->b);

    t_nextPhase = millis() + cur_phase->delay;    
}

ここで、新しい照明モードを定義するには、新しいコードを記述するのではなく、色と時間の新しい配列が必要です。カラーランプやその他のそのような効果のようなものを追加することは、読者の練習問題として残されています。

于 2012-10-21T11:26:58.193 に答える