Обратный отсчет по нажатию кнопки

dbv1991
Offline
Зарегистрирован: 07.11.2016
Добрый день!
Только начинаю изучать Arduino и прошу Вас дать советы по написанию кода.
Программа состоит из двух частей:
1) Необходимо подсчитать количество нажатий на кнопку от 0 до 9 и вывести на 7-сегментный индикатор, использую сдвиговый регистр для экономии выходов (такой пример есть разобранный и по нему вроде все понятно, код приведен ниже)
2) Другая кнопка должна запускать обратный отсчет с количества нажатий до нуля и на каждой секунде издавать короткий сигнал, а при окончании долгий сигнал и должен загореться светодиод.
Подскажите, пожалуйста, какой цикл лучше использовать при обратном отсчете for или do..while? 
И как написать код, чтобы сдвиговый регистр отсчитывал строго до нуля и не уходил в облать отрицательных чисел?
Заранее спасибо.
Если подобное ранее обсуждалось, то прошу дать ссылку. 
 
#define DATA_PIN    13 // пин данных (англ. data)
#define LATCH_PIN   12 // пин строба (англ. latch)
#define CLOCK_PIN   11 // пин такта (англ. clock)
#define BUTTON_PIN  10
 
int clicks = 0;
boolean buttonWasUp = true;
byte segments[10] = {
  0b01111101, 0b00100100, 0b01111010, 0b01110110, 0b00100111, 
  0b01010111, 0b01011111, 0b01100100, 0b01111111, 0b01110111
};
 
void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}
 
void loop()
{
  // считаем клики кнопки, как уже делали это раньше
  if (buttonWasUp && !digitalRead(BUTTON_PIN)) {
    delay(10);
    if (!digitalRead(BUTTON_PIN))
      clicks = (clicks + 1) % 10;
  }
  buttonWasUp = digitalRead(BUTTON_PIN);
  // для записи в 74HC595 нужно притянуть пин строба к земле
  digitalWrite(LATCH_PIN, LOW);
  // задвигаем (англ. shift out) байт-маску бит за битом,
  // начиная с младшего (англ. Least Significant Bit first)
  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, segments[clicks]);
  // чтобы переданный байт отразился на выходах Qx, нужно
  // подать на пин строба высокий сигнал
  digitalWrite(LATCH_PIN, HIGH);
}

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

dbv1991 пишет:

какой цикл лучше использовать при обратном отсчете for или do..while? 

Любой, какой Вам больше нравится.

dbv1991 пишет:

как написать код, чтобы сдвиговый регистр отсчитывал строго до нуля и не уходил в облать отрицательных чисел?

Никак. Сдвиговый регистр не умеет ничего отсчитывать, это не его функция. Отсчитывать будете сами. Просто сравнивайте с нулём и если достигнут, дальше не уменьшайте.

dbv1991 пишет:

#define DATA_PIN    13 // пин данных (англ. data)
#define LATCH_PIN   12 // пин строба (англ. latch)
#define CLOCK_PIN   11 // пин такта (англ. clock)
...
  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, segments[clicks]);

Весьма оригинально!

Вы используете программную реализацию последовательного интерфейса, который в данном контроллере реализован также аппаратно. Обычно программную реализацию используют тогда, когда пины, к которым привязана аппаратная реализация заняты и их невозможно совободить.

Вы же повесили программную реализацию на те же пину, на которых живёт аппаратная. Зачем?

dbv1991
Offline
Зарегистрирован: 07.11.2016

Добрый вечер!

Учел все замечания, написал код, компилирование проходит успешно, но после загрузки не работает, если не трудно, подскажите в чем дело, вероятнее всего ошибка в коде обратного отсчета, так как подсчет количества нажатий работал успешно.

При обратно отсчете необходимо чтобы программа останавливалась и загорался светодиод.

Заранее спасибо!

 

int led = 8; //светодиод, загорающийся при окончании обратного отсчета

//Пин подключен к ST_CP входу 74HC595
int LATCH_PIN = 12;
//Пин подключен к SH_CP входу 74HC595
int  CLOCK_PIN = 11;
//Пин подключен к DS входу 74HC595
int DATA_PIN = 13;

#define BUTTON_PIN  10  //кнопка, с которой происходит подсчет нажатий
#define BUTTON_REV  9  // кнопка запуска обратного отсчета

int clicks = 0;  //число нажатий

boolean BUTT_PIN = true;  //начальное состояние кнопки
boolean BUTT_REV = true;

unsigned long TimerINTERVAL;  // Для интервала / паузы, обратного отсчета таймера, без delay.

byte massiv[10] = {
  
 0b01111101, 0b00100100, 0b01111010, 0b01110110, 0b00100111, 
  0b01010111, 0b01011111, 0b01100100, 0b01111111, 0b01110111 
      
};  //числа от 0 до 9


void setup()
{
 //устанавливаем режим OUTPUT для линий,
 //которые требуются для работы с регистром
 pinMode(LATCH_PIN, OUTPUT);
 pinMode(CLOCK_PIN, OUTPUT);
 pinMode(DATA_PIN, OUTPUT);
 pinMode(BUTTON_PIN, INPUT);
 pinMode(BUTTON_REV, INPUT);
 pinMode(led, OUTPUT);

}



// Код основной программы

void loop()
{
  // считаем клики кнопки
  if (BUTT_PIN>0) 
  {
    delay(10);
    if (!digitalRead(BUTTON_PIN))
      clicks = (clicks + 1) % 10;
  }
  // запись в 74HC595 притянуть пин строба к земле
  digitalWrite(LATCH_PIN, LOW);
  // задвигаем (англ. shift out) байт-маску бит за битом,
  // начиная с младшего (англ. Least Significant Bit first)
  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, massiv[clicks]);
  // чтобы переданный байт отразился на выходах Qx, нужно
  // подать на пин строба высокий сигнал
  digitalWrite(LATCH_PIN, HIGH);


  
  //обратный отсчет по нажатию кнопки BUTTON_REV
  
 if (BUTT_REV>0) 
 {
    delay(10);
 // Обратный перебор массива, начиная с номера элемента равного числу нажатий
 for (clicks = 0; 0 <= 9; clicks++);
    if (TimerINTERVAL == 0)TimerINTERVAL = millis();  
    // пауза для шага вниз таймера, можно увеличивать / уменьшать. 
    if(millis()-TimerINTERVAL > 3000) // если прошло 3000 = 3 секунды.
   //при достижении 0 должен загореться светодиод
    if (clicks == 0)
   { digitalWrite(led, HIGH);}
 }
//вывод цифр при обратном отсчете на индикатор
  digitalWrite(LATCH_PIN, LOW);
  // задвигаем (англ. shift out) байт-маску бит за битом,
  // начиная с младшего (англ. Least Significant Bit first)
  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, massiv[clicks]);
  // чтобы переданный байт отразился на выходах Qx, нужно
  // подать на пин строба высокий сигнал
  digitalWrite(LATCH_PIN, HIGH);

}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

dbv1991,

Вас не смущает тот факт, что переменные BUTT_PIN и BUTT_REV никогда не меняются? Как Вы их сделали true в строка 15 и 16, так они таковыми и остаются пожизненно. Но при этом Вы старательно сравниваете их с 0 в строках 48 и 67. Зачем? 

Я уж не говорю, что переменную типа boolean не стоит сравнивать на >0, т.к. она может быть >0, а может и <0 но всё равно она при этом true.

И ещё, на "языке наших партнёров" существительное butt имеет много значений, но одно из наиболее распространённых - "задница". За что Вы эти переменные так?

 

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015
// все пины лучше сразу через define
#define led 8 			//светодиод, загорающийся при окончании обратного отсчета
#define BUTTON_PIN  10  //кнопка, с которой происходит подсчет нажатий
#define BUTTON_REV  9  	// кнопка запуска обратного отсчета

int clicks = 0;  		//число нажатий
boolean BUTT_PIN=false; 	//нельзя в программе задать состояние кнопки, оно у нас физическое. сделаем из этих переменных флаги как и было задумано // начальное состояние кнопки
boolean BUTT_REV=false;		//

//unsigned long TimerINTERVAL;  // Для интервала / паузы, обратного отсчета таймера, без delay.ПОРВЕМ ШАБЛОН И СДЕЛАЕМ ДЕЛАЙ!
void setup()
{
 Serial.begin(9600);// я убрал сдвиговый регистр и сделал через сириал, так как по регистру вопросов не было
 pinMode(BUTTON_PIN, OUTPUT);
 pinMode(BUTTON_REV, OUTPUT);
 digitalWrite(BUTTON_PIN,1);
 digitalWrite(BUTTON_REV,1);
 pinMode(led, OUTPUT);
  Serial.println("SET THE TIMER BY BOTTON +");
}
// весь луп сделаем из ifов
void loop()
{
  
 // if (BUTT_PIN>0) BUTT_PIN ВСЕГДА БУДЕТ БОЛЬШЕ НУЛЯ! МЫ ЖЕ ЕГО ТРУ ПРОПИСАЛИ!
 // {
 //   delay(10);// што это было? дребезг?
    if (!digitalRead(BUTTON_PIN)&&!BUTT_PIN)
    {
      Serial.print("INCREASE TIMER = ");
      digitalWrite(led,0);// гасим диод, типо в цикле работает вся это бойда
      clicks++;
      Serial.print(clicks);
      Serial.println(" SEC");
      BUTT_PIN=true;// Поднять флаг!
    }
     if (digitalRead(BUTTON_PIN)&&BUTT_PIN)
       BUTT_PIN=false;// СПУСТИТЬ ФЛАГ!
  if (!digitalRead(BUTTON_REV)&&!BUTT_REV&&clicks==0)
  {
    Serial.println("NOT THIS BUTTON IDIOT!!!");
    BUTT_REV=true;
  }
  if(!digitalRead(BUTTON_REV)&&!BUTT_REV) 
 {
    Serial.println("AHTUNG!!!COUNTDOWN IS STARTED!!!");
   for (; clicks >= 0; clicks--)// устал комментить, вот тут вот так...
   {
     delay(1000);
     Serial.println(clicks);
   }
 digitalWrite(led,1);
    Serial.println("BOOOOOM!");
 clicks=0;
   }
  if(digitalRead(BUTTON_REV)&&BUTT_REV)BUTT_REV=false;
}

Я честно пытался сделать кнопки как у Вас, но ничего не получилось, сделал по своему. Убрать сериал и добавить 7 сегментный экран (у меня опять же в эмуляторе не получилось его сделать) решил не тратить время и сделать через сериал.

dbv1991
Offline
Зарегистрирован: 07.11.2016

От души благодарен! Теперь стала понятна структура скетча!

Но по условию задачи все равно надо выводить на 7-сегментный индикатор с помощью регистра сдвига и чтобы нажатие на кнопку считало от 0 до 9 только. Сейчас попробую адаптировать под свою задачу!

Еще раз огромное спасибо!

dbv1991
Offline
Зарегистрирован: 07.11.2016

Добрый день!

Прошу Вашей помощи в модернизации программы, она должна выполнять следущее:

После подсчета нажатий N с кнопки BUTTON_PIN, обратный отсчет отсчет начинается с включения тумблера (не кнопки) BUTTON_REV  после проверки условий (flag=false) с датчиков Холла (2 штуки), если все условия выполненены (flag=true), то программа отсчитывает с N до 0 и загорается светодиод (LED_1), программа ждет 15 секунд и снова проверяет условия (flag=true) и производит обратный отсчет до нуля, и так до тех пор пока не будет выключен тумблер.
Причем, если тумблер выключается во время обратного отсчета программа должна досчитать до нуля и остановиться, и не сбрасывать число нажатий N.
Если же после включения тумблера условия не выполнятся (flag=false), должен загореться светодиод, соответствующий номеру датчика холла (LED_1 и LED_2), и пока условия не пройдут проверку обратный отсчет начинаться не должен.
 
Подскажите, надо ли использовать две логические переменные для программирования тумблера обратного отсчета: одна переменная для фиксирования положения кнопки, а другая для цикла обратного отсчета? И в каком порядке нужно разместить условия проверки flag'ов?
Как можно написать код, чтобы при выключении тумблера программа досчитывала до нуля и останавливалась?
Спасибо!
      //Светодиоды
                                     
#define LED_0 8      //светодиод, загорающийся при окончании обратного отсчета
#define LED_1  7       //светодиод - ошибка на 1-ом датчике Холла
#define LED_2  6        //светодиод - ошибка на 2-ом датчике Холла


      //Датчики Холла

//define SensorPIN_1  A0    // 1-ый датчик Холла
//define SensorPIN_1  A1     // 2-ой датчик Холла

int SensorPIN_1;             
int SensorPIN_2;

boolean flag1=false;         //флаг ошибки на 1-ом Холле
boolean flag2=false;         //флаг ошибки на 2-ом Холле




           //Кнопки

#define BUTTON_PIN  10  //кнопка, с которой происходит подсчет нажатий
#define BUTTON_REV  9   // кнопка запуска обратного отсчета

int clicks = 0;     //число нажатий
boolean BUTT_PIN=false;   // начальное состояние кнопки BUTT_PIN
boolean BUTT_REV=false;   // начальное состояние кнопки  BUTT_REV

//для кнопки обратного отсчета
boolean button_state=false;
uint32_t ms_button=0;
uint32_t ms=millis();

             //Сдвиговый регистр
             
#define DATA_PIN    13 // пин данных 
#define LATCH_PIN   12 // пин строба 
#define CLOCK_PIN   11 // пин такта 


             //Индикатор

byte massiv[10] = {
0b01111101, 0b00100100, 0b01111010, 0b01110110, 0b00100111, 
  0b01010111, 0b01011111, 0b01100100, 0b01111111, 0b01110111   
};



void setup()
{
  //Используем серийный порт
 Serial.begin(9600);

//Светодиоды

 pinMode(LED_0, OUTPUT);
 pinMode(LED_1, OUTPUT);
 pinMode(LED_2, OUTPUT);

 //Холл

  pinMode(SensorPIN_1, INPUT);
  pinMode(SensorPIN_2, INPUT);


  //Кнопки 

 pinMode(BUTTON_PIN, OUTPUT); 
 pinMode(BUTTON_REV, OUTPUT);
 digitalWrite(BUTTON_PIN,1);
 digitalWrite(BUTTON_REV,1);

 //Сдвиговый регистр

   pinMode(DATA_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  
}





void loop()                                                     
                                                      //подсчет количества нажатий на кнопку BUTTON_PIN
{
      if (!digitalRead(BUTTON_PIN)&&!BUTT_PIN)
    {
      Serial.print("INCREASE TIMER = ");
      digitalWrite(LED_1,0);// погасить диод при отсутствии нажатий
      clicks = (clicks + 1) % 10;
    Serial.print(clicks);
     Serial.println(" SEC");

   //Вывод на индикатор через сдвиговый регистр
       digitalWrite(LATCH_PIN, LOW);

  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, massiv[clicks]);
 
  digitalWrite(LATCH_PIN, HIGH);
   BUTT_PIN=true;// Поднять флаг!
    }
     if (digitalRead(BUTTON_PIN)&&BUTT_PIN)
       BUTT_PIN=false;// СПУСТИТЬ ФЛАГ!


                                                                // Обратный отчсет
       
  if (!digitalRead(BUTTON_REV)&&!BUTT_REV&&clicks==0)


//как праывильно записать код для тумблера? Можно так?

     //фиксируем нажатие кнопки
     
if(digitalRead(BUTTON_REV)==LOW&&!button_state&&(ms-ms_button)>50)
{
BUTT_REV=true;
ms_button=ms;
}

       //фиксируем отпускание кнопки
       
if(digitalRead(BUTTON_REV)==HIGH&&!button_state&&(ms-ms_button)>50)
{
BUTT_REV=false;
ms_button=ms;
}

  //проверка условий для датчиков Холла

    //первый датчик

  flag1 = digitalRead(A0);

if (flag1 == LOW){
  digitalWrite(7, HIGH);   // нет сигнала-загорается LED_1
}

else
{
 digitalWrite(7, LOW); 
}

    //второй датчик
{
  flag2 = digitalRead(A1);

if (flag2 == LOW){
  digitalWrite(8, HIGH);   // нет сигнала-загорается LED_2
}
else
{
 digitalWrite(8, LOW); 
}

 //есть сигналы-начинается обратный отсчет

  {
    Serial.println("KNOPKA OBRATNOGO OTSCHETA");
  }
  
  if(!digitalRead(BUTTON_REV)&&!BUTT_REV) 
 {
   Serial.println("AHTUNG!!!COUNTDOWN IS STARTED!!!");
   for (; clicks >= 0; clicks--) 
   {
     delay(1000);
    Serial.println(clicks);
  digitalWrite(LATCH_PIN, LOW);
  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, massiv[clicks]);
  digitalWrite(LATCH_PIN, HIGH);
   }
   }

   //загорается светодиод при окончании обратного отсчета
 digitalWrite(LED_0,1);
    Serial.println("BOOOOOM!");
 clicks=0;
   }
  if(digitalRead(BUTTON_REV)&&BUTT_REV)BUTT_REV=false;
}

 

 

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015

dbv1991 пишет:

Добрый день!

Прошу Вашей помощи в модернизации программы, она должна выполнять следущее:

После подсчета нажатий N с кнопки BUTTON_PIN, обратный отсчет отсчет начинается с включения тумблера (не кнопки) BUTTON_REV  после проверки условий (flag=false) с датчиков Холла (2 штуки), если все условия выполненены (flag=true), то программа отсчитывает с N до 0 и загорается светодиод (LED_1), программа ждет 15 секунд и снова проверяет условия (flag=true) и производит обратный отсчет до нуля, и так до тех пор пока не будет выключен тумблер.
Причем, если тумблер выключается во время обратного отсчета программа должна досчитать до нуля и остановиться, и не сбрасывать число нажатий N.
Если же после включения тумблера условия не выполнятся (flag=false), должен загореться светодиод, соответствующий номеру датчика холла (LED_1 и LED_2), и пока условия не пройдут проверку обратный отсчет начинаться не должен.
 
Подскажите, надо ли использовать две логические переменные для программирования тумблера обратного отсчета: одна переменная для фиксирования положения кнопки, а другая для цикла обратного отсчета? И в каком порядке нужно разместить условия проверки flag'ов?
Как можно написать код, чтобы при выключении тумблера программа досчитывала до нуля и останавливалась?
Спасибо!

Судя по скетчу в Вас каша в голове. Вы не определили пины Холов через #define, хотя попытка была, только решетку забыли добавить. потом решили их через int сделать, что не несть хорошо. лучше все же черед Дефайн. Так как в при их инициализации через int вы их ни к чему не приравняли поскольку трудно приравнять аналоговые порты к int овой переменной  вы сдались, а какие они у Вас значения получили хз. потом.... Работа с тумблером от работы с кнопкой ничем не отличается, работа с датчиком холла от работы с кнопкой особо ничем не отличается. Используйте функции которые опрашивают состояния устройств и ставят флаги когда состояние меняется.

про миллис. вы его используете неправильно, вот вы переменную ms создали, приравняли ее в начале к millis(); это значит что она будет равна millis(); только там где вы ее приравняли, то есть в начале программы, потом она не меняется. Мой совет-уберите ms и пропишите в лупе вместо нее millis()

Моя программа была рабочей и делала то что было нужно. Ваша же уже то что нужно не делает. мне сейчас не хочется сидеть и искать где Вы ошиблись. Попробуйте решать поэтапно, выводите переменные в сириал где это нужно для отладки. Возмите рабочее (мое) решение и постипенно добавляейте элементы, так чтобы оно работало как надо на каждом этапе добавления. Сначала добавьте millis() вместо delay (), потом добавьте датчик Холла с диодом, потом второй... не нужно все городить в кучу а потом искать где ошибся.