Помогите советом. Прерывание цикла.

m3duatop
Offline
Зарегистрирован: 30.01.2017

Доброго времени суток. Помогите, пожалуйста, советом по организации прерывания. 

Суть такая. Пытаюсь соорудить что-то типа LED контроллера. 

Схема отладки:

1. UNO

2. Тактовая кнопка, притянута к земле, подключена на второй pin.

3. RGB светодиоды:

redled = 3;

greenled = 5;

blueled = 6;



Что хотелось бы увидеть. Диоды мигают по какому-то своему алгоритму, при нажатии кнопки происходит переключение программы "мигалки". 

Например. Мигает красный, нажал кнопку - замигал зеленый, еще раз нажал - замигал синий, еще раз нажал - стали мигать RGB по очереди. 

Программист я неопытный. Гуглил, читал, в итоге пришел к одному варианту, который больше всего напоминает то, что я хочу, но и с ним немного что-то не так.



По порядку

int switchPin = 2;
int redled = 3;
int greenled = 5;
int blueled = 6;
volatile int LEDstate = 0;

char colors[3] = {redled, greenled, blueled}; //Засунул в массив для удобства обращения через циклы

void setup()
{
  pinMode(blueled, OUTPUT);
  pinMode(redled, OUTPUT); 
  pinMode(greenled, OUTPUT);
  pinMode(switchPin, INPUT);
  attachInterrupt(0, LEDstateSwitch, LOW);
}
//Мигаем три раза цветом по выбору
void anyblink(int x){
  for(int i=0;i<3;i++){
    digitalWrite(colors[x], HIGH);
    delay(1000);
    digitalWrite(colors[x], LOW);
    delay(1000);
  }
}
//Мигаем бесконечно цветом по выбору
void anyblink(int x){
  int tmp_LEDstate = LEDstate;
  while(tmp_LEDstate == LEDstate){
    for(int i=0;i<3;i++){
      digitalWrite(colors[x], HIGH);
      delay(1000);
      digitalWrite(colors[x], LOW);
      delay(1000);
    }
  }
}

//Три красных, три зеленых, три синих
void anyblink_all(){
  for(int i=0;i<3;i++){
    anyblink(i);
  }
}
//В лупе - код, который запускает определенную функцию, в зависимости от значения LEDstate
//LEDstate принимает значения от [0-3], а каждое нажатие кнопки увеличивает значение на 1
//Если значение LED state становится больше 3, то присваиваем значение "0"
//Таким образом, переключаем программы мигалок по кругу
void loop() {
  if (LEDstate > 3) //Проверяем, не превышено ли максимальное значение
  {
    LEDstate = 0;
  }
  else
  {
      if (LEDstate == 0) //Три красных свистка
      {
        anyblink(0);
      }
      else if (LEDstate == 1) //Три зеленых
      {
       anyblink(1);
      }
      else if (LEDstate == 2) //Три синих
      {
       anyblink(2);
       }
      else if (LEDstate == 3)//Три красных, три зеленых, три синих.
      {
        anyblink_all();
      }
   }
}
//Обработка прерывания с дебонсом и увеличением значения LEDstate на 1
void LEDstateSwitch()
{
  static unsigned long millis_prev;
  if(millis()-100 > millis_prev) LEDstate++;
  millis_prev = millis();
}

Получается на выходе следующее. Если берем функцию с тремя вспышками, то прерывается только та вспышка, с которой совпало нажатие кнопки, оставшиеся срабатывают, и только после этого происходит переключение программы. Если не совсем понятно, то на примере:

Мигает красный, который должен мигнуть три раза по условиям функции. Если нажимаем во время первой вспышки из трех, прерывается только первая вспышка, диод моргает еще два раза красным и после этого включается зеленая мигалка.

Если берем функцию anyblink_all(), которая, напоминаю, должна мигнуть три раза красным, три зеленым, и три синим, то попытавшись ее прервать на красном, увидим, как она домаргивает зеленым и синим и только потом начинается выполнение следующей функции.

В случае с infiniteblink(), бесконечным циклом, переключение вообще не происходило, если не добавлять условие

int tmp_LEDstate = LEDstate;
  while(tmp_LEDstate == LEDstate)

Но это не помогает с остальными, к сожалению.

Буду признателен за содействие и соучастие :)

P.S. Почему-то, после добавления прерывания длительность всех задержек стала меньше. Диоды мигают не по секунде, а по 0,5 по ощущениям.

 

 

 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

после обработки прерывания, программа продолжается с прерванного места, т.е после прерывания функция продолжится до конца. 

надо взводить флаг и постоянно проверять его в цикле, если он взведен, делать break.  А прерывание может происходить в _любом_ месте цикла

m3duatop
Offline
Зарегистрирован: 30.01.2017

Не уверен, что правильно Вас понял, но спасибо :)

Добвил в начало каждой функции

int tmp_LEDstate = LEDstate;

Дабы зафиксировать текущее состояние режима. А в конце каждой итерации цикла включил проверку

if(tmp_LEDstate != LEDstate) break;

Получилось вот так:

void infiniteblink(int x){
  int tmp_LEDstate = LEDstate;
  while(tmp_LEDstate == LEDstate){
    digitalWrite(colors[x], HIGH);
    delay(1000);
    digitalWrite(colors[x], LOW);
    delay(1000);
  }
}

//------------------------------------------

void anyblink(int x){
  for(int i=0;i<3;i++){
    int tmp_LEDstate = LEDstate;
    digitalWrite(colors[x], HIGH);
    delay(1000);
    digitalWrite(colors[x], LOW);
    delay(1000);
    if(tmp_LEDstate != LEDstate) break;
  }
}

//------------------------------------------

void anyblink_all(){
  int tmp_LEDstate = LEDstate;
  for(int i=0;i<3;i++){
    anyblink(i);
    if(tmp_LEDstate != LEDstate) break;
  }
}

Только все еще не пойму, почему при выставлении триггера прерываний в LOW, все задержки половинятся. Похоже, что в функции delay() что-то скипается, раз кнопка поднята.

В общем, вроде работает основная часть. Если есть более изящный способ воплотить мою идею, буду рад увидеть :)

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Код написан канешно не очень но я его поправил. Попробуйте

#define switchPin 2
#define redled  3
#define greenled  5
#define blueled 6
volatile int LEDstate = 0;
volatile int tmp_LEDstate = 0;

char colors[3] = {redled, greenled, blueled}; //Засунул в массив для удобства обращения через циклы

void setup()
{
  pinMode(blueled, OUTPUT);
  pinMode(redled, OUTPUT);
  pinMode(greenled, OUTPUT);
  pinMode(switchPin, INPUT);
  attachInterrupt(0, LEDstateSwitch, FALLING);
}

//Обработка прерывания с дебонсом и увеличением значения LEDstate на 1
void LEDstateSwitch()
{
  LEDstate++;
}

//Мигаем бесконечно цветом по выбору
void anyblink(int x) {
  for (int i = 0; i < 3; i++) {
    digitalWrite(colors[x], HIGH);
    delay(1000);
    digitalWrite(colors[x], LOW);
    delay(1000);
    if (tmp_LEDstate != LEDstate) {
      tmp_LEDstate = LEDstate;
      break;
    }
  }
}

//Три красных, три зеленых, три синих
void anyblink_all() {
  for (int i = 0; i < 3; i++) {
    anyblink(i);
    if (tmp_LEDstate != LEDstate) {
      tmp_LEDstate = LEDstate;
      break;
    }
  }
}
//В лупе - код, который запускает определенную функцию, в зависимости от значения LEDstate
//LEDstate принимает значения от [0-3], а каждое нажатие кнопки увеличивает значение на 1
//Если значение LED state становится больше 3, то присваиваем значение "0"
//Таким образом, переключаем программы мигалок по кругу
void loop() {
  if (LEDstate > 3) //Проверяем, не превышено ли максимальное значение
  {
    LEDstate = 0;
  }
  else
  {
    if (LEDstate == 0) //Три красных свистка
    {
      anyblink(0);
    }
    else if (LEDstate == 1) //Три зеленых
    {
      anyblink(1);
    }
    else if (LEDstate == 2) //Три синих
    {
      anyblink(2);
    }
    else if (LEDstate == 3)//Три красных, три зеленых, три синих.
    {
      anyblink_all();
    }
  }
}

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

1 ошибка это delay();

2 ошибка это останавливать delay() прерыванием.

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Ну делай - это само собой. А если без далай то тогда так

#define switchPin 2
#define redled  3
#define greenled  5
#define blueled 6
volatile int LEDstate = 0;
volatile int tmp_LEDstate = 0;
unsigned long millis_prev;
int kol = 0;
int volume = 0;
int kolor = 0;

char colors[3] = {redled, greenled, blueled}; //Засунул в массив для удобства обращения через циклы

void setup()
{
  pinMode(blueled, OUTPUT);
  pinMode(redled, OUTPUT);
  pinMode(greenled, OUTPUT);
  pinMode(switchPin, INPUT);
  attachInterrupt(0, LEDstateSwitch, FALLING);
}

//Обработка прерывания с дебонсом и увеличением значения LEDstate на 1
void LEDstateSwitch()
{
  LEDstate++;
}

//Мигаем бесконечно цветом по выбору
void anyblink(int x) {
  digitalWrite(colors[x], !digitalRead(colors[x]));
}


void loop() {
  if (tmp_LEDstate != LEDstate) {
    kol = 0;
    kolor = 0;
    volume = 0;
    tmp_LEDstate = LEDstate;
    for (int i = 0; i < 3; i++) {
      digitalWrite(colors[i], LOW);
    }
  }
  if (LEDstate > 3) //Проверяем, не превышено ли максимальное значение
  {
    LEDstate = 0;
  }

  if (LEDstate < 3 && kol < 6) //
  {
    if (millis() - millis_prev >= 1000) {
      anyblink(LEDstate);
      millis_prev = millis();
      kol++;
    }
  }

  else if (LEDstate == 3 && kol < 6)//Три красных, три зеленых, три синих.
  {
    if (millis() - millis_prev >= 1000) {
      if (volume == 6)kolor++, volume = 0;
      if (kolor > 2)kolor = 0;
      anyblink(kolor);
      volume++;
      millis_prev = millis();
    }
  }
}

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Я сделал так Полный скетч здесь https://yadi.sk/d/rbJvpZqd3Bvw4h

А это головной файл 

/*running_lights.ino
 * https://yadi.sk/d/rbJvpZqd3Bvw4h
  #1 светодиоды RGB ->2,3,4
  #2 кнопка выбора ->5
  принцип кода: каждое нажатие кнопки новый режим мигания.
*/
//#1 светодиоды RGB
#include "Cl_lights.h"
const byte R_pin = 2;
const byte G_pin = 3;
const byte B_pin = 4;
Cl_led Lights; // создать
//#2 кнопка выбора
#include "Cl_do_btn.h"
const byte btn1_pin = 5;
Cl_do_btn SELECT_btn; // создать
void Select() {
  static byte sel = 0;
  Lights.OFF();
  sel++;
  if (sel > 6) sel = 0;
  switch (sel) {
    case 0:
      Lights.R_blink();//мигает красный
      break;
    case 1:
      Lights.G_blink();//мигает зеленый
      break;
    case 2:
      Lights.B_blink();//мигает синий
      break;
    case 3:
      Lights.RGB_blink();//мигают все
      break;
    case 4:
      Lights.R_G_B();//мигают по очереди
      break;
    case 5:
      Lights.R2_G2_B2();//мигают 2 раза по очереди
      break;
    case 6:
      Lights.R3_G3_B3();//мигают 3 раза по очереди
      break;
  }
}
void setup() {
  //#1 светодиоды RGB
  Lights.T_blink = 500;
  Lights.setup(R_pin, G_pin, B_pin);
  Lights.R_blink();
  //#2 кнопка выбора
  SELECT_btn.setup(btn1_pin);
}
void loop() {
  //#1 светодиоды RGB
  Lights.loop();
  //#2 кнопка выбора
  SELECT_btn.loop(& Select );

}

 

m3duatop
Offline
Зарегистрирован: 30.01.2017

Благодарю за внимание, но некорректно работает без этого

void LEDstateSwitch()
{
  static unsigned long millis_prev;
  if(millis()-50 > millis_prev) LEDstate++;
  millis_prev = millis();
}

Да и, не понимаю пока почему, но при всех типах прерывания, кроме LOW, смена LEDstate происходит при как при нажатии, так и при отпускании кнопки. Т.е. за один цикл нажатия, происходит +2 к LEDstate. 

 
DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Пропробывай 

void LEDstateSwitch()
{
  cli();
  static unsigned long millis_prev;
  if(millis()-50 > millis_prev) LEDstate++;
  millis_prev = millis();
  sei();
}

может, в millis() опять неявно прерывания разрешаются?

m3duatop
Offline
Зарегистрирован: 30.01.2017

vosara пишет:

Ну делай - это само собой. А если без далай то тогда так

qwone пишет:

Я сделал так Полный скетч здесь https://yadi.sk/d/rbJvpZqd3Bvw4h

А это головной файл

 

Спасибо, это уже круто. Необходимо время для осмысления :) Совсем другой уровень.

DetSimen пишет:

Пропробывай 

void LEDstateSwitch()
{
  cli();
  static unsigned long millis_prev;
  if(millis()-50 > millis_prev) LEDstate++;
  millis_prev = millis();
  sei();
}

может, в millis() опять неявно прерывания разрешаются?

Вечером попробываю, спсибо!

Клапауций 823
Клапауций 823 аватар
Offline
Зарегистрирован: 13.01.2017

DetSimen пишет:

Пропробывай 

void LEDstateSwitch()
{
  cli();
  static unsigned long millis_prev;
  if(millis()-50 > millis_prev) LEDstate++;
  millis_prev = millis();
  sei();
}

может, в millis() опять неявно прерывания разрешаются?

пробую: millis_prev = 5; if(6 - 50 > 5) O_O;

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017
конечно же лучше так
 

if((millis()-millis_prev)>50) LEDstate++; else return;

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

m3duatop пишет:

Благодарю за внимание, но некорректно работает без этого

void LEDstateSwitch()
{
  static unsigned long millis_prev;
  if(millis()-50 > millis_prev) LEDstate++;
  millis_prev = millis();
}

Да и, не понимаю пока почему, но при всех типах прерывания, кроме LOW, смена LEDstate происходит при как при нажатии, так и при отпускании кнопки. Т.е. за один цикл нажатия, происходит +2 к LEDstate. 

 

Это у Вас идет дребезг контактов а с millis дребезг устраняется. Так что отавляйте это полезно

m3duatop
Offline
Зарегистрирован: 30.01.2017

Ну, да. я это и имел ввиду. Там закомменчено в коде, что тут дебонс идет.

m3duatop
Offline
Зарегистрирован: 30.01.2017

Вот. тут хотел уточнить. Какова роль переменных kol и volume?

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

kol-перебор цвета(красн. син.зелен)  volume-количество морганий

m3duatop
Offline
Зарегистрирован: 30.01.2017

vosara пишет:

kol-перебор цвета(красн. син.зелен)  volume-количество морганий

А я так понял. что перебор цвета, это kolor, а у kol ОДЗ [0:5]

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Да Вы правы vulume выполняла тоже что и kol можно обойтись без нее. Поэтому я применил ее в другое где она нужнее

#define switchPin 2
#define redled  3
#define greenled  5
#define blueled 6
volatile int LEDstate = 0;
volatile int tmp_LEDstate = 0;
unsigned long millis_prev;
int kol = 0;
bool volume = 0;
int kolor = 0;

char colors[3] = {redled, greenled, blueled}; //Засунул в массив для удобства обращения через циклы

void setup()
{
  pinMode(blueled, OUTPUT);
  pinMode(redled, OUTPUT);
  pinMode(greenled, OUTPUT);
  pinMode(switchPin, INPUT);
  attachInterrupt(0, LEDstateSwitch, FALLING);
}

//Обработка прерывания с дебонсом и увеличением значения LEDstate на 1
void LEDstateSwitch()
{
  LEDstate++;
}

//Мигаем бесконечно цветом по выбору
void anyblink(int x) {
  volume = !volume;
  digitalWrite(colors[x], volume);
}


void loop() {
  if (tmp_LEDstate != LEDstate) {
    kol = 0;
    kolor = 0;
    volume = 0;
    tmp_LEDstate = LEDstate;
    for (int i = 0; i < 3; i++) {
      digitalWrite(colors[i], LOW);
    }
  }
  if (LEDstate > 3) //Проверяем, не превышено ли максимальное значение
  {
    LEDstate = 0;
  }

  if (LEDstate < 3 && kol < 6) //
  {
    if (millis() - millis_prev >= 1000) {
      anyblink(LEDstate);
      millis_prev = millis();
      kol++;
    }
  }

  else if (LEDstate == 3 && kol <= 6)//Три красных, три зеленых, три синих.
  {
    if (millis() - millis_prev >= 1000) {
      if (kol == 6)kolor++, kol = 0;
      if (kolor > 2)kolor = 0;
      anyblink(kolor);
      kol++;
      millis_prev = millis();
    }
  }
}

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

vosara пишет:

kol-перебор цвета(красн. син.зелен)  volume-количество морганий

Мда... "Найдите 10 отличий" (с)

 color - цвет, value - значение/величина.