Мигаем светодиодом с таймером по прерыванию

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Может не совсем верно озаглавил, но что первое пришло в голову - то и написал.

В общем суть такая:

Есть (будет) код, который заставляет светодиоды мигать в определённый момент времени (которое, кстати тоже постоянно изменяется).

По нажатию на кнопку, которая будет висеть на 2-м пине (пин прерывания №0) должно засекаться время (timer) от последнего нажатия (прерывания) и цикл мигания должен начинаться с самого начала, причём этот цикл должен быть равен предыдущему измерению времени между нажатиями (прерываниями).

Наверное написал полную чушь, т.к. сложно объяснить идею в пару строк.

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

Светодиодов будет к примеру 6, т.е. каждый будет мигать ровна 2 раза за один цикл.

Вот что набросал для начала:

#include <Wire.h> 

int ledState = LOW;             // этой переменной устанавливаем состояние светодиода
long previousMillis = 0;        // храним время последнего переключения светодиода
long interval = 100;           // интервал между включение/выключением светодиода (0,1 секунда)


void setup()
{
  Serial.begin(9600); 
  pinMode(13, OUTPUT);
   pinMode(12, OUTPUT);
   pinMode(11, OUTPUT);
   pinMode(10, OUTPUT);
   pinMode(9, OUTPUT);
  //Digital Pin 2 Set As An Interrupt
 attachInterrupt(0, fan_interrupt, RISING); // по прирыванию запускаем fan_interrupt

}

void loop()
{
 
      Serial.print(digitalRead(2)); // смотрим как отрабатывает кнопка или геркон

}

void cikl()
{
// Тут нужно заставить мигать светодиоды в определённый момент времени
// и так, что-бы цикл после прерывания начинался заново с новым временем,
// рассчитанным в функции прерывания. Т.е. 6 светодиодов должны загореться с временем
// равным interval/6 (для мигания светодиодов в различных местах конструкции) или interval/2 (для мигания в часовых точках циферблата), вроде так, но не уверен
// так же нужно обойтись без delay, т.к. иногда светодиоды должны будут загораться одновременно
 
  
    // если светодиод не горит, то зажигаем, и наоборот
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
 
    // устанавливаем состояния выхода, чтобы включить или выключить светодиод
    digitalWrite(13, ledState);
//  }
}

void fan_interrupt()
{
   interval = (millis() - time_last);  // рассчитываем время между нажатиями кнопки или замыканиями геркона
   time_last = millis(); // в качестве последнего значения времени присваиваем текущее
   cikl(); // запускаем цикл мигания светодиодов
}

 

Leshiy
Offline
Зарегистрирован: 19.07.2014

То есть у нас есть время Tx , равное времени, прошедшему с момента старта устройства, или с момента последнего нажатия на кнопку. После нажатия на кнопку, мы берём это время Tх и что с ним делаем? То, что отсчёт начинается заново, абсолютно понятно. Но что мы делаем со значением Tx, куда и как применяем?

if (ledState == LOW)
  ledState = HIGH;
else
  ledState = LOW;

замените на

ledState=!ledState;

 

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Время Tx = время с момента последнего нажатия на кнопку.

Дальше это время разбиваем на 6 частей, т.к. в конструкции будет 6 независимых светодиодов, и используя уже "урезанное" время добиваемся загорания и гашения светодиодов в определённой позиции, для вырисовывания определённой картинки (точки, короткие полоски, штрих-пунктир)

Если взять за основу циферблат часов, то светодиоды будут стоять на местах 12 часов, 2, 4, 6, 8 и 10.

Это необходимо, т.к. скорость вращения круга будет меняться.
Учитывая, что скорость предыдущего оборота ~ равняется текущему (в большинстве случаев).
Именно из-за этого и решил привязаться к времени между нажатиями кнопки или замыкания геркона (скорее всего геркон поставлю, т.к. надёжнее) .

Leshiy
Offline
Зарегистрирован: 19.07.2014

Время Тх = время от старта устройства до первого нажатия на кнопку, а уже потом от предпоследнего, до последнего нажатия на кнопку.

А, писать на спицах лисапеда, увы, мой друг, не мудрено? Это что-ли? Или там на лопастях вентилятора? Так если это, то готовые проекты есть наверняка, зачем изобретать велосипед для велосипеда? Не стесняйтесь пользоваться плодами чужих трудов.

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Да, что-то вроде того должно получиться.
Можно и на спицах велосипеда и на вентилятор.
Тут немного другая штука (но суть на 100% такая же) - ветряк, ременная передача на диск, где вся эта электроника и будет висеть.

Бегло просмотрел гугл:
http://www.instructables.com/id/RGBike-POV-Open-project/
http://habrahabr.ru/post/151042/

Но всё без исходников. 

Если есть на примете похожие проекты с исходниками, подкиньте пожалуйста.

Leshiy
Offline
Зарегистрирован: 19.07.2014

И лучше датчик Холла использовать вместо геркона. Хотя если обороты небольшие, то можно и геркон.

Опенсорц - http://hacknmod.com/hack/open-source-bicycle-pov-display-pictures-animation/

Искать следует по "bike bicycle led arduino pov"

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Спасибо!)

Есть 2 датчика хола (http://www.ebay.com/itm/NEW-Hall-element-switch-For-Magnetic-Detect-Ardu...) Но он даже неодимовый магнит улавливает вплотную, как увеличить его чувствительность?
 

 

Leshiy
Offline
Зарегистрирован: 19.07.2014

Там вверху есть кнопка "Number steps", ежели её нажать, то увидите и сорцы и описание. А также есть кнопка Download PDF, в котором всё сразу.

Увеличить чувствительность... Ну, самое простое, заменить датчик на более чутьёвый (чёрная такая штучка с тремя ножками в прорези платы). Или изменить коэффициент усиления у ОУ, там ведь мелкосхема ОУ, насколько я понимаю, стоит? Вот с нею можно ещё разобраться. Что, кстати, за мелкосхема?

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Да это ОУ LM393, буду подбирать сопротивление из того, что имеется в наличии.

Leshiy
Offline
Зарегистрирован: 19.07.2014

Centrovoy пишет:

Да это ОУ LM393, буду подбирать сопротивление из того, что имеется в наличии.

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

Посмотрел даташит, в нём она заявлена как компаратор, такшта вопрос вообще снимается. Смотрите куда там этот датчик подключен, если в цепи есть сопротивление, то попробуйте его просто закоротить для проверки работоспособности устройства.

Centrovoy
Offline
Зарегистрирован: 06.05.2014

на схеме есть место для подстроечного резистора, но вместо него стоит резистор на 100кОм, при его закорачивании, датчик не работает (сигнала на выходе нет)

Leshiy
Offline
Зарегистрирован: 19.07.2014

Надо смотреть, куда он там стоит. Тут, по слухам, у датчиков Холла по выходу открытый коллектор. Так что это может быть нагрузочный резистор, который, если закоротить, превратит датчик в неработающее устройство либо на время КЗ, либо навсегда.

Моя вина, как-то ваще не подумал уточнить. В общем тут посмотрите - http://carnovato.ru/princip-raboty-shema-datchika-holla-skutere/

А тут схемы включения: https://www.google.ru/search?q=подключение+датчика+холла

Centrovoy
Offline
Зарегистрирован: 06.05.2014

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

Но при отсутствии ветра вращения может не быть и несколько часов.

Т.е. нужно заменить эту часть кода

if(interval > 1000) {  
 
digitalWrite(13, HIGH);  
   delay(100);            
   digitalWrite(13, LOW);     
digitalWrite(12, HIGH);  
   delay(100);            
   digitalWrite(12, LOW);       
digitalWrite(11, HIGH);  
   delay(100);            
   digitalWrite(11, LOW);       
digitalWrite(10, HIGH);  
   delay(100);            
   digitalWrite(10, LOW);      
digitalWrite(9, HIGH);  
   delay(100);            
   digitalWrite(9, LOW);    
  }   

на функцию, которая будет запускаться при бездействии более 10 секунд.

На данный момент код выглядит так:

volatile float time = 0;
volatile float time_last = 0;
volatile int rpm_array = 0;


int ledState = LOW;             // этой переменной устанавливаем состояние светодиода
long previousMillis = 0;        // храним время последнего переключения светодиода
long interval = 100;           // интервал между включение/выключением светодиода (0,1 секунда)


void setup()
{
  pinMode(13, OUTPUT);
   pinMode(12, OUTPUT);
   pinMode(11, OUTPUT);
   pinMode(10, OUTPUT);
   pinMode(9, OUTPUT);
  //Digital Pin 2 Set As An Interrupt
 attachInterrupt(0, fan_interrupt, RISING);
 
}

void loop()
{
  
  unsigned long currentMillis = millis();
  
  if(interval > 1000) {  
 
digitalWrite(13, HIGH);  
   delay(100);            
   digitalWrite(13, LOW);     
digitalWrite(12, HIGH);  
   delay(100);            
   digitalWrite(12, LOW);       
digitalWrite(11, HIGH);  
   delay(100);            
   digitalWrite(11, LOW);       
digitalWrite(10, HIGH);  
   delay(100);            
   digitalWrite(10, LOW);      
digitalWrite(9, HIGH);  
   delay(100);            
   digitalWrite(9, LOW);    
  }   
  
 else //проверяем не прошел ли нужный интервал, если прошел то
  if(currentMillis - previousMillis > interval) {
    // сохраняем время последнего переключения
    previousMillis = currentMillis; 
 
      ledState =! ledState;
 
    // устанавливаем состояния выхода, чтобы включить или выключить светодиод
    digitalWrite(13, ledState);
    digitalWrite(12, ledState);
    digitalWrite(11, ledState);
    digitalWrite(10, ledState);
    digitalWrite(9, ledState);
  }
 }


void fan_interrupt()
{
   interval = (millis() - time_last);  // рассчитываем время между нажатиями кнопки или замыканиями геркона
   time_last = millis(); // в качестве последнего значения времени присваиваем текущее
   interval = interval/5;
   loop(); // запускаем цикл мигания светодиодов заново
}

 

Leshiy
Offline
Зарегистрирован: 19.07.2014

Я сам новичок, поэтому могу ошибаться, но мне кажется, что для хранения значений таймера (millis) следует использовать unsigned long, а не просто long.

7 строка:
long previousMillis = 0;

заменить на:
unsigned long previousMillis = 0;

Лично я все переменные, которые используются для вычислений времени с помощью millis, сделал бы unsigned long. Во избежание ошибок. А то у вас там и float, и long, и unsigned long, так лучше не делать, как мне кажется.

Конкретно по вопросу - я его не понял, увы, но насколько понял, настолько и отвечу:

if(interval > 1000) {
MyFunction(); 
}

 

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

Leshiy пишет:

Конкретно по вопросу - я его не понял, увы, но насколько понял, настолько и отвечу:

if(interval > 1000) {
MyFunction(); 
}

нолик дорисуй для 10 секунд :)

Leshiy
Offline
Зарегистрирован: 19.07.2014

Puhlyaviy пишет:

Leshiy пишет:

Конкретно по вопросу - я его не понял, увы, но насколько понял, настолько и отвечу:

if(interval > 1000) {
MyFunction(); 
}

нолик дорисуй для 10 секунд :)

А это мысль!

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Спасибо, типы переменных подправлю.

Leshiy пишет:

Конкретно по вопросу - я его не понял, увы, но насколько понял, настолько и отвечу:

if(interval > 1000) {
MyFunction(); 
}

Да, объяснил я конечно не очень понятно.

Можно было и так сделать, но переменная interval при отсутствии ветра может и не изменяться в течении нескольких часов. Т.к. нет ветра - нет вращения, нет вращения - датчик хола не даёт сигнал на прерывание, где и рассчитывается переменная interval.

Отсюда и вопрос, можно ли отсчитать 10 секунд при отсутствии сигнала с датчика хола более 10 секунд?

Leshiy
Offline
Зарегистрирован: 19.07.2014

Centrovoy пишет:

Спасибо, типы переменных подправлю.

Leshiy пишет:

Конкретно по вопросу - я его не понял, увы, но насколько понял, настолько и отвечу:

if(interval > 1000) {
MyFunction(); 
}

Да, объяснил я конечно не очень понятно.

Можно было и так сделать, но переменная interval при отсутствии ветра может и не изменяться в течении нескольких часов. Т.к. нет ветра - нет вращения, нет вращения - датчик хола не даёт сигнал на прерывание, где и рассчитывается переменная interval.

Отсюда и вопрос, можно ли отсчитать 10 секунд при отсутствии сигнала с датчика хола более 10 секунд?

Так введите второй счётчик, который растёт постоянно, а в те моменты, когда Холл посылает имульс и ваше прерывание срабатывает - обнуляйте его. Таким образом, пока имульсы есть, счётчик никогда не достигнет предельного значения, после которого запутстися ваша функция.

А ещё в микроконтроллерах есть таймер, который может сам отсчитывать время и, по достижению указанного значения, генерировать прерывание, в которое можете засунуть свою подпрограмму.

Centrovoy
Offline
Зарегистрирован: 06.05.2014

Тоже была такая идея, но обнулять millis() вроде как нельзя...
Про встроеный таймер почитаю.

Leshiy
Offline
Зарегистрирован: 19.07.2014

Зачем нам миллис обнулять? Нам надо, чтобы миллис-превмиллис было <10000 тогда, когда лопасти крутятся. Этого достаточно вполне.

Што нам для этого надо? Праааальн - в ISR добавить строку prevmillis=millis(). Ну то есть в void fan_interrupt().

Но тогда код перестанет работать, да? Верно. А кто сказал, что превмиллисов может быть только 1? Сделайте себе второй превмиллис, назовите его как хотите и пользуйтесь на здоровье.

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

Centrovoy пишет:

Тоже была такая идея, но обнулять millis() вроде как нельзя...
Про встроеный таймер почитаю.

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