Помогите с созданием таймера обратного отсчета

Inappropriate
Offline
Зарегистрирован: 03.11.2013
Здравствуйте, уважаемые форумчане. 
 
Помогите, пожалуйста, со скечтем. Возможно, есть какие- то похожие решения с минимальной переделкой. К сожалению, с программированием и ардуино, в частности, дружу не очень сильно. Аппаратную часть реализую, а вот с программной туго) Нужен таймер с таким функционалом:
 
Два 7-и сегментных индикатора, одна кнопка. Отсчет только 60 секунд без возможности выбора. Логика работы такая: при нажатии на кнопку начинается предварительный отсчет в 5 секунд со звуковой индикацией, затем, по его истечении, основной в 60 секунд с отображением на индикаторах, после начала предварительного и до конца основного отсчета кнопка должна быть "заблокирована", т.е. никак не влиять идущий отсчет. Так же, в начале и в момент окончания основного отсчета, на какой- либо пин должен посылаться высокий уровень на 200-300мс для управления внешним устройством. После окончания отсчета - звуковой сигнал и блокировка кнопки на 15 секунд. Затем устройство готово к следующему циклу работы. Точность большая не нужна, +/- 1-2 секунды вполне нормально.
achest
achest аватар
Offline
Зарегистрирован: 01.10.2012

В брейн ринг играете, так и скажи.

Проблем как таковых с программированием нет. Но как мы можем тебе помочь,если не известно, что ты там куда напоял?  Саму программу писать быстро (30 мин) но ее еще надо отладить. Фальшстарт, плохой контакт в кнопках.... Думаю, что день на отладку положить надо...

Блокировка это никаая не проблема, скорее наоборот  сложность. В 90% случаев происходит программный опрос клавиатуры. т.е. Не опросил, не знаешь что там на кнопке происходит. Почему индикаторы 7 сегментные? Мне кажется что это сильно устарело...

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

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

Параметры лучше в константы вынести, но оно мне лень :)


const int buttonPin = 26;       // назначаем пин D26 для кнопки
const int ledPin =  13;         // назначаем пин D13 для сигнального светодиода
const int buzzerPin =  46;      // назначаем PWM-пин D46 для пищалки, кроме 3 и 11!!!
const int highLevelPin =  30;   // назначаем пин D30 для дублирущего светодиода
// const int displayPin =  6;   // назначаем пины для семисегментого индикатора
boolean buttonState = false;    // логическая переменная для состояния кнопки

void setup() 
{
  // инициализация пинов
  pinMode(buttonPin, INPUT);     
  digitalWrite(buttonPin, HIGH); //включаем внутренний подтягивающий резистор на 20К, чтобы не закоротить пины при нажатии кнопки без обвязки

  pinMode(ledPin, OUTPUT);   
  digitalWrite(ledPin, LOW); // гасим светодиод

  pinMode(buzzerPin, OUTPUT);   
  //  digitalWrite(buzzerPin, LOW); // затыкаем пищалку

  pinMode(highLevelPin, OUTPUT);  
  digitalWrite(highLevelPin, LOW); // обнуляем выход на устройство

  //  pinMode(displayPin, OUTPUT); 
  // display.init();  // сюда подставить команду инициализации дисплея
  // display.show("--");

  Serial.begin(9600);  // вывод отладочной информации
  Serial.println("Ready...");
}


void loop()
{
  buttonState = !digitalRead(buttonPin);  // читаем состояние кнопки и инвертируем его, т.к. у нас включен подтягивающий резистор
  //  Serial.println(buttonState);

  if (buttonState == true) 
  {  // дeйствия при нажатии кнопки
    Serial.println("Button pressed!");
    preStart(); // выполняем условия предстарта
    sendSignal(); // подаем сигнал на устройство
    mainStart(); // выполняем условия старта
    sendSignal(); // подаем сигнал на устройство    
    finish(); // выполняем условия финиша
    Serial.println("THE END");  
  } 
  else 
  {  // действия, если кнопка не нажата
    //    digitalWrite(ledPin, LOW); 
  }
}


void preStart()
{
  Serial.println("Prestart...");  
  // display.show("  ");  // очищаем дисплей

  for (int i = 5; i > 0; i--)
  {  
    Serial.println(i);  // выводим в порт показания отсчета
    // display.show(i); // выводим на дисплей показания отсчета
    digitalWrite(ledPin, HIGH); // мигаем светодиодом 
    tone(buzzerPin, 2000, 200);  // пищим 200 мс с частотой 2000 Гц
    delay(800);        // ждем еще 0,8 сек
    digitalWrite(ledPin, LOW);
  }
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Done.");  
}

void mainStart()
{
  Serial.println("Main Start!");    
  digitalWrite(ledPin, HIGH); // включаем светодиод 

  for (int i = 0; i <= 59; i++)
  {
    Serial.println(i);  // выводим в порт показания отсчета
    // display.show(i); // выводим на дисплей показания отсчета
    delay(1000);        // ждем 1 сек
  }

  digitalWrite(ledPin, LOW);// гасим светодиод 
  Serial.println("Done.");  
}


void finish()
{
  Serial.println("Finish...");  

  tone(buzzerPin, 2000, 1000);  // пищим сигнал об окончании
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Wait 15 seconds...");    
  delay(15000);  // ничего не делаем 15 сек

  for (int i = 0; i <= 4; i++)  // пищим финальный аккорд
  {  
    tone(buzzerPin, 3000, 100);  
    delay(200);   
  }      

  noTone(buzzerPin); // затыкаем пищалку  
  // display.show("--");  // закончили задачу, выводим режим готовности к следующему этапу
  buttonState = false;  // устанавливаем исходное состояние кнопки 
  Serial.println("Done.");  
}


void sendSignal()
{
  Serial.println("Send HI to device");  
  digitalWrite(highLevelPin, HIGH); // подаем высокий уровень на устройство

  delay(250);        // ждем 250 мсек

  digitalWrite(highLevelPin, LOW);
  Serial.println("Send LOW to device");  
}

Думаю, на дребезг кнопки можно не обращать внимания, ибо она после старта все равно блокируется на 75 сек.

А мелькающие индикаторы создают видимость действия и динамику (хотя вместо их можно сделать и световой столб на LED-лентах) ;)

Inappropriate
Offline
Зарегистрирован: 03.11.2013

achest, спасибо за ответ. 

Нет, это не брейн-ринг) Для брейн- ринга где-то встречал готовый проект. Устройство нужно для подключения к контактам кнопки авто-регистратора для записи минутных видеороликов при нажатии на кнопку. Паять ничего не паял. На макетке сдвоенный 7-и сегментый индикатор с общими катодами. Ардуино Mega1280. Аноды спараллелены посимвольно. На катодах резисторы на 220 Ом или около того. Пробовал нескольо готовых проектов типа кухонного таймера только так и не смог додумать как изменить на нужный мне функционал. 7-и сегментные индикаторы нагляднее да и достать проще нужного цвета и размера.

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Tomasina, большое спасибо за помощь.
Завтра обязательно попробую Ваш варант. Сейчас, к сожалению, нет возможности. Индикатор обычный светодиодный 7-и сегментный сдвоенный в общим катодом. Вроде такого http://www.chipdip.ru/product/bl-d56a-21upg/ Т.е. им ни спец. интерфейс ни библиотеки не нужны. Только 7пинов ардуины для управления анодами и 2 пина для катодов.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

пример работы с индикатором: http://lucky-byte.livejournal.com/9748.html

Inappropriate
Offline
Зарегистрирован: 03.11.2013

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

Немного подправил код, вот что получилось.

const int buttonPin = 26;       // назначаем пин D26 для кнопки
const int ledPin =  10;         // назначаем пин D13 для сигнального светодиода
const int buzzerPin =  46;      // назначаем PWM-пин D46 для пищалки, кроме 3 и 11!!!
const int highLevelPin =  30;   // назначаем пин D30 для дублирущего светодиода
// const int displayPin =  6;   // назначаем пины для семисегментого индикатора
boolean buttonState = false;    // логическая переменная для состояния кнопки

void setup() 
{
  // инициализация пинов
  pinMode(buttonPin, INPUT);     
  digitalWrite(buttonPin, HIGH); //включаем внутренний подтягивающий резистор на 20К, чтобы не закоротить пины при нажатии кнопки без обвязки

  pinMode(ledPin, OUTPUT);   
  digitalWrite(ledPin, LOW); // гасим светодиод

  pinMode(buzzerPin, OUTPUT);   
  //  digitalWrite(buzzerPin, LOW); // затыкаем пищалку

  pinMode(highLevelPin, OUTPUT);  
  digitalWrite(highLevelPin, LOW); // обнуляем выход на устройство

  //  pinMode(displayPin, OUTPUT); 
  // display.init();  // сюда подставить команду инициализации дисплея
  // display.show("--");

  Serial.begin(9600);  // вывод отладочной информации
  Serial.println("Ready...");
}


void loop()
{
  buttonState = !digitalRead(buttonPin);  // читаем состояние кнопки и инвертируем его, т.к. у нас включен подтягивающий резистор
  //  Serial.println(buttonState);

  if (buttonState == true) 
  {  // дeйствия при нажатии кнопки
    Serial.println("Button pressed!");
    preStart(); // выполняем условия предстарта
    sendSignal(); // подаем сигнал на устройство
    mainStart(); // выполняем условия старта
    sendSignal(); // подаем сигнал на устройство    
    finish(); // выполняем условия финиша
    Serial.println("THE END");  
  } 
  else 
  {  // действия, если кнопка не нажата
    //    digitalWrite(ledPin, LOW); 
  }
}


void preStart()
{
  Serial.println("Prestart...");  
  // display.show("  ");  // очищаем дисплей

  for (int i = 4; i > 0; i--)
  {  
    digitalWrite(ledPin, HIGH); // мигаем светодиодом
    Serial.println(i);  // выводим в порт показания отсчета
    // display.show(i); // выводим на дисплей показания отсчета
    tone(buzzerPin, 2000, 200);  // пищим 200 мс с частотой 2000 Гц
    delay(900);        // ждем еще 0,8 сек
    digitalWrite(ledPin, LOW);
  }
  tone(buzzerPin, 1000, 200); //низкий тон
  delay(900);        // ждем еще 0,8 сек
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Done.");  
}

void mainStart()
{
  Serial.println("Main Start!");    
  digitalWrite(ledPin, HIGH); // включаем светодиод 

  for (int i = 10; i >= 0; i--)
  {
    Serial.println(i);  // выводим в порт показания отсчета
    // display.show(i); // выводим на дисплей показания отсчета
    delay(1000);        // ждем 1 сек
  }

  digitalWrite(ledPin, LOW);// гасим светодиод 
  Serial.println("Done."); 
    
}

void finish()
{
  Serial.println("Finish...");  

  for (int i = 0; i <= 4; i++)  // пищим финальный аккорд
  {  
    tone(buzzerPin, 3000, 100);  
    delay(200);   
  }      
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Wait 15 seconds...");    
  delay(5000);  // ничего не делаем 15 сек
  // display.show("--");  // закончили задачу, выводим режим готовности к следующему этапу
  buttonState = false;  // устанавливаем исходное состояние кнопки 
  Serial.println("Done.");  
}


void sendSignal()
{
  Serial.println("Send HI to device");  
  digitalWrite(highLevelPin, HIGH); // подаем высокий уровень на устройство

  delay(250);        // ждем 250 мсек

  digitalWrite(highLevelPin, LOW);
  Serial.println("Send LOW to device");  
}

Временные промежутки для теста выставил пока поменьше. В блоке void preStart() почему то должен мигать светодиод ledPin но он горит постоянно.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

проверь, что светодиод подключен к пину D10.

tone(buzzerPin, 2000, 200);
delay(900); 

200+900, это уже больше секунды получается, за цикл preStart набежит 0,5 секунды, не учтенных в ТЗ :)

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Да, знаю что больше выходит, просто слишком быстро как то получается) Мне не критично +/- 1 секунда. Светодиод точно на 10 порту. Пробовал разные порты. Почему то не мигает.

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Сделал так, замигало:

void preStart()
{
  Serial.println("Prestart...");  
  // display.show("  ");  // очищаем дисплей

  for (int i = 4; i > 0; i--)
  {  
    digitalWrite(ledPin, HIGH); // мигаем светодиодом
    Serial.println(i);  // выводим в порт показания отсчета
    // display.show(i); // выводим на дисплей показания отсчета
    tone(buzzerPin, 2000, 200);  // пищим 200 мс с частотой 2000 Гц
    delay(450);        // ждем еще 0,8 сек
    digitalWrite(ledPin, LOW);
    delay(450);
   }
  digitalWrite(ledPin, HIGH);  
  tone(buzzerPin, 1000, 200); //низкий тон
  delay(450);        // ждем еще 0,8 сек
  digitalWrite(ledPin, LOW);
  delay(450);
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Done.");  
}

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

вожможно, светодиод дохлый или подключен в обратной плоярности. Ибо на моей плате он моргает :)

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Светодиод рабочий. Пробовал несколько разных, пробовал другой пин. Пробовал в обратной полярности- вообще не горит)

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Понял, почему не мигает светодиод. Странно, что у вас работает. Получается, что в цикле он включается затем, через 900мс он выключается и сразу включается. Т.е. не горит он долю секунды. 

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Проблема теперь в том, как прицепить индикатор. Т.к. таймер реализован через deleay(), то хз как делать динамическую индикацию. Завтра, если найду в продаже, буду пробовать через сдвиговые регистры 74HC595 как тут http://greenoakst.blogspot.ru/2012/01/arduino-7-spi.html

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Вот что наваял. Проблема тепереь в том, что единицы секунд отображаются нормально а десятки вспыхивают очень быстро в начале отсчета. Как сделать нормальное отображение десятков?

 

const int buttonPin = 26;       // назначаем пин D26 для кнопки
const int ledPin =  42;         // назначаем пин D13 для сигнального светодиода
const int buzzerPin =  46;      // назначаем PWM-пин D46 для пищалки, кроме 3 и 11!!!
const int highLevelPin =  30;   // назначаем пин D30 для дублирущего светодиода
// const int displayPin =  6;   // назначаем пины для семисегментого индикатора
boolean buttonState = false;    // логическая переменная для состояния кнопки
#include <SPI.h> 
enum { reg = 53 }; 

void setup() 
{
  SPI.begin();   
  pinMode(reg, OUTPUT); 
  
  // инициализация пинов
  pinMode(buttonPin, INPUT);     
  digitalWrite(buttonPin, HIGH); //включаем внутренний подтягивающий резистор на 20К, чтобы не закоротить пины при нажатии кнопки без обвязки

  pinMode(ledPin, OUTPUT);   
  digitalWrite(ledPin, LOW); // гасим светодиод

  pinMode(buzzerPin, OUTPUT);   
  //  digitalWrite(buzzerPin, LOW); // затыкаем пищалку

  pinMode(highLevelPin, OUTPUT);  
  digitalWrite(highLevelPin, LOW); // обнуляем выход на устройство

  //  pinMode(displayPin, OUTPUT); 
  // display.init();  // сюда подставить команду инициализации дисплея
  // display.show("--");

  Serial.begin(9600);  // вывод отладочной информации
  Serial.println("Ready...");
}

int hundreds=0, tens=0, ones=0; 
int delayTime=7;        
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF}; 
static uint8_t pos[3]= {0xE7,0xEB,0xF3};


void loop()
{
  buttonState = !digitalRead(buttonPin);  // читаем состояние кнопки и инвертируем его, т.к. у нас включен подтягивающий резистор
  //  Serial.println(buttonState);

  if (buttonState == true) 
  {  // дeйствия при нажатии кнопки
    Serial.println("Button pressed!");
    preStart(); // выполняем условия предстарта
    sendSignal(); // подаем сигнал на устройство
    mainStart(); // выполняем условия старта
    sendSignal(); // подаем сигнал на устройство    
    finish(); // выполняем условия финиша
    Serial.println("THE END");  
  } 
  else 
  {  // действия, если кнопка не нажата
    //    digitalWrite(ledPin, LOW); 
  }
}


void preStart()
{
  Serial.println("Prestart...");  
  // display.show("  ");  // очищаем дисплей

  for (int i = 4; i > 0; i--)
  {  
    digitalWrite(ledPin, HIGH); // мигаем светодиодом
    Serial.println(i);  // выводим в порт показания отсчета
    // display.show(i); // выводим на дисплей показания отсчета
    tone(buzzerPin, 2000, 200);  // пищим 200 мс с частотой 2000 Гц
    delay(450);        // ждем еще 0,8 сек
    digitalWrite(ledPin, LOW);
    delay(450);
   }
  digitalWrite(ledPin, HIGH);  
  tone(buzzerPin, 1000, 200); //низкий тон
  delay(450);        // ждем еще 0,8 сек
  digitalWrite(ledPin, LOW);
  delay(450);
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Done.");  
}

void mainStart()
{
  Serial.println("Main Start!");    
   

  for (int i = 60; i >= 0; i--)
  {
    hundreds=i/100;
    tens=(i-hundreds*100)/10; 
    ones=i-hundreds*100-tens*10;
    digitalWrite(reg, LOW); 
    SPI.transfer(digit[tens]);
    SPI.transfer(pos[1]);                
    digitalWrite(reg, HIGH); 
    delay(delayTime); 
        
    digitalWrite(reg, LOW); 
    SPI.transfer(digit[ones]);  
    SPI.transfer(pos[2]);                
    digitalWrite(reg, HIGH); 
    delay(delayTime);
    digitalWrite(ledPin, HIGH); // мигаем светодиодом
    Serial.println("SEC");  // выводим в порт показания отсчета
    Serial.println(i);
    // display.show(i); // выводим на дисплей показания отсчета
    delay(1000);        // ждем 1 сек
    digitalWrite(ledPin, LOW);
  }

  
  Serial.println("Done."); 
    
}

void finish()
{
  Serial.println("Finish...");  

  for (int i = 0; i <= 4; i++)  // пищим финальный аккорд
  {  
    tone(buzzerPin, 3000, 100);  
    delay(200);   
  }      
  noTone(buzzerPin); // затыкаем пищалку  
  Serial.println("Wait 15 seconds...");    
  delay(5000);  // ничего не делаем 15 сек
  // display.show("--");  // закончили задачу, выводим режим готовности к следующему этапу
  buttonState = false;  // устанавливаем исходное состояние кнопки 
  Serial.println("Done.");  
}


void sendSignal()
{
  Serial.println("Send HI to device");  
  digitalWrite(highLevelPin, HIGH); // подаем высокий уровень на устройство

  delay(250);        // ждем 250 мсек

  digitalWrite(highLevelPin, LOW);
  Serial.println("Send LOW to device");  
}

 

Inappropriate
Offline
Зарегистрирован: 03.11.2013

В итоге, заставил этого Франкенштейна работать. Отказался от динамической индикации, подключил к двум 74HC595 катоды от каждого разряда, общие аноды повесил на +. Через SPI посылаю байт с десятками и байт с единицами. Мягко говоря, не самый элегантный код вышел, но оно работает, так как нужно)

std
Offline
Зарегистрирован: 05.01.2012

Если reg=53 для обоих регистров, т. е. из первого с выхода Q7' (вывод 8) идет провод на DS второго (вывод 14), тогда понятно почему промигивает старшая цифра. Некоторое время на ней находится информация с младшей цифры, потом продвигается дальше (в этот момент становится нормально). Нужно завести 2 переменных reg, и каждую цифру посылать отдельно. Иначе (соединение регистров в цепь и один общий вывод защёлки) визуально воспринимается как промигивание во время изменения цифр. У меня была такая хрень когда делал трёхзначный индикатор из рассыпухи (некогда было ждать с Китая TM1637).

Решается так:

- вход данных (MOSI, вывод 14) второго регистра (того что для единиц) отцепить с переноса первого, соединить на 14 первого регистра, т. е. параллельно.

- вход защёлки второго регистра отцепить от первого, завести на отдельный провод, допустим 52 или что там рядом с 53.

Как выводить:

#define reg1 (52)
#define reg2 (53)
// ..........
digitalWrite(reg1,LOW);
SPI.Transfer(MSG_TENS);
digitalWrite(reg1,HIGH);
digitalWrite(reg2,LOW);
SPI.Transfer(MSG_ONES);
digitalWrite(reg2,HIGH);

где:

MSG_TENS - десятки, MSG_ONES - единицы; 52 - вывод защёлки десяток (первый регистр), 53 - вывод защёлки единиц (второй регистр).

Inappropriate
Offline
Зарегистрирован: 03.11.2013

Это ничего не меняет. Для динамической индикации мне нужно было быстро переключать аноды для вывода десятков и единиц. Т.е. фактически в один момент времени горит либо десяток либо единица. Но, в силу инерции человеческого зрения и высокой скорости переключения, картинка воспринимается без миганий. Проблема в том, что в счетчике используется delay(), из-за которого быстрого переключения не получить. В итоге, сделал так:

digitalWrite(reg, LOW); 
SPI.transfer(digit[tens]);
SPI.transfer(digit[ones]);                
digitalWrite(reg, HIGH); 

Отказался от динамической индикации, соединил со 2-м сдвиговым регистром 2-е катоды сегментов. По SPI сначала посылается байт десятка потом байт единицы. Т.к. регистр 8-и битовый, 2-й байт проходит через него дальше, 1-й остается. Может быть, до меня дойдет, как все сделать без delay()-ев. А пока, пусть будет такой велосипед)

vvbelin
Offline
Зарегистрирован: 01.04.2016

del