В качестве первого проекта я планирую сделать teensyduino рассеянный свет с различными режимами освещения, которые проверяются в большом заявлении о переключении — теперь я хочу переключаться из одного режима в другой, нажимая кнопку.
поиск в Google привел меня к использованию прерываний, но есть один момент, который не ясен — если я нажимаю кнопку во время дорогой функции, которая занимает много времени и использует много переменных, что произойдет, если я вызову основной цикл из прерывания, оставшееся состояние остается в оперативной памяти и приводит к переполнению стека, если я переключаюсь слишком много раз, или он очищается.
Вот некоторый код:
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 ++. Пожалуйста, измените это, если это не подходит.
В конечном итоге вы переполнили бы стек, если бы вам пришлось многократно рекурсивно вводить 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;
}
Теперь, чтобы определить новый режим освещения, вам просто нужен новый массив цветов и времен, а не писать новый код. Добавление таких вещей, как цветовые схемы и другие подобные эффекты, остается для читателя в качестве упражнения.
Других решений пока нет …