Помогите заставить конечный автомат работать правильно
- Войдите на сайт для отправки комментариев
День добрый.
Пытаюсь заставить конечный автомат работать правильно, но пока тщетно.
Прощу помощи, т.к. в программировании я очень слаб.
Суть программы: существует некий конечный автомат с определенными режимами (GOROD, TRASSA,DOGD,OFF).
Каждый режим отвечает за включение мотора в определенных временных условиях, перключение режимов осуществляется кнопкой1
Режим OFF выключает мотор-кнопка 2
Также есть определенные ограничения описанные в функциях Regim_t SpeedSensorReturnRegim() и void SpeedSensor() которые отвечают за выключение мотора (при наступлении определенных условий) и возврат режима после после окончания ограничения.
Есть еще вольтметр, термометр и вывод всего этого на экран, но это уже суть.
Проблема в следующем: после загрузки скетча в ардуино уно включается первый режим (GOROD), по нажатию кнопки должна происходить смена режима, но ничего не происходит (сам режим GOROD работает вроде правильно).
Код писал не сам (выдирал куски кода аналогичного уствроства и переделывал под себя). Подразумеваю, что ошибка либо в определение typedef enum или в функции Button_1().
//Управление сказчиком //пины 6 и 3 это кнопки //пин 2 выход на насос //программа 1 - "город" - 2 капли в минуту (2 открытия в минуту) //программа 2 - "трасса" - 3-4 капли в минуту (3-4 открытия в минуту) //программа 3 - "дождь" - 10 капель в минуту (10 открытия в минуту) //кнопка 3 - выключает автосмажчик //!!!!количество открытий в минуту и время открытия пока ориентировочное!!!! //Вывод на экран //пины 4,5,10-13 для соединения с экраном //на экран выводится только информация какой режим сейчас включен //Подключаем стандартную библиотеку LiquidCrystal //-------------------------------------------------------------------------------------------------- #define BUTTON_1 6 //включает программу смазки 1 - "город" на второй ноге //каждое последующее нажатие включает следующую программу #define BUTTON_2 3 //batton_2 - выключает смазчик на 3-й ноге #define motor 7 //транзистор управляющий насосом на 7-й ноге #define speedsensor 2 //датчик скорости подключен к пину D2 long speedlimit=5; //количество импульсов за один оборот колеса (при скорости примерно 7км/ч) //!!!!! ВНИМАНИЕ //ЭТО ЗНАЧЕНИЕ НУЖНО ПОЛУЧИТЬ НАТУРНО (ИЗМЕРИТЬ НА ДАТЧИКЕ ПРИ ВРАЩАЮЩЕМСЯ КОЛЕСЕ #define SEC 1000 //1секунда=период таймера(1мс)*коэффициет(1000) #define TIME_MEASURE_VOLTAGE 1*SEC #define K_VOLT 0.14//0.18//0.14 //коэффициент делителя(для определения напряжения) (0.14 R1 = 13k, R2 = ) #define V_MAX 5 #define VIN A1 unsigned long sp=0; //переменная для хранения данных с датчика скорости long T_speed = 1000; //интервал времени для определения текущей скорости (1 секунда) unsigned long previousMillis_sp = 0; //время последнего импульса unsigned long previousMillis = 0; //храним время последнего переключения светодиода long interval_on = 3000; //интервал между вкл/выкл насоса (3 секунда) long interval_7 = 7000; //интервал между вкл/выкл насоса (7 секунда) long interval_17 = 17000; //интервал между вкл/выкл насоса (17 секунда) long interval_27 = 27000; //интервал между вкл/выкл насоса (27 секунда) #include <LiquidCrystal.h> //библиотека экрана LiquidCrystal lcd(4,5,10,11,12,13); #include <math.h> #define T A0 //датчик температуры подключен к А0 //-------------------------------------------------------------------------------------------------- boolean buttonWasUp_1 = true; // была ли кнопка1 отпущена? boolean buttonWasUp_2 = true; // была ли кнопка2 отпущена? //-------------------------------------------------------------------------------------------------- uint16_t voltage = 0; boolean fMeasureVoltage = true; uint16_t tMeasureVoltage = TIME_MEASURE_VOLTAGE; //-------------------------------------------------------------------------------------------------- typedef enum{GOROD, TRASSA, DOGD, OFF} Regim_t; Regim_t regim, prevRegim; //enum Regim_t regim = GOROD; bool fOn = false; //-------------------------------------------------------------------------------------------------- void OutLCD(String s){ //функция печати на экран режима lcd.setCursor(8,1); //устанавливаем курсор в первую строку, 8-я колонна lcd.print(s); } //-------------------------------------------------------------------------------------------------- void On(){ //функция отвечающая за включение насоса digitalWrite(motor, HIGH); } void Off(){ //функция отвечающая за выключение насоса digitalWrite(motor, LOW); } //-------------------------------------------------------------------------------------------------- void WorkPump(int delay){ //функция отвечающая за выбор режима unsigned long currentMillis = millis(); if (fOn) { On(); if(currentMillis - previousMillis > interval_on){ //проверяем не прошел ли нужный интервал,если прошел то Off(); //закрыть клапан fOn = false; previousMillis = currentMillis; //сохраняем время последнего переключения } } if (!fOn) { Off(); if(currentMillis - previousMillis > delay){ //проверяем не прошел ли нужный интервал,если прошел то fOn = true; previousMillis = currentMillis; //сохраняем время последнего переключения } } } //-------------------------------------------------------------------------------------------------- Regim_t SpeedSensorReturnRegim() { unsigned long currentMillis = millis(); if ((currentMillis - previousMillis_sp > T_speed)&&sp++ <speedlimit){ prevRegim = regim; regim = OFF; previousMillis_sp = currentMillis; //сохраняем время последнего переключения lcd.setCursor(11,1); //устанавливаем курсор в первую строку, 11-я колонна lcd.print("non active"); } else{ regim = prevRegim; return regim; } } //-------------------------------------------------------------------------------------------------- void SpeedSensor(){ //функция отвечающая за расчет ограничителя unsigned long currentMillis = millis(); sp++; if(((currentMillis - previousMillis_sp) > T_speed) && sp++ <speedlimit){ regim = OFF; previousMillis_sp = currentMillis; //сохраняем время последнего переключения sp=0; } } //-------------------------------------------------------------------------------------------------- void SensorVolt() { int sensorValue; if (fMeasureVoltage) { sensorValue = analogRead(VIN); voltage = (int)(((sensorValue / 1023.0f * V_MAX) / K_VOLT) * 1000); fMeasureVoltage = false; } lcd.setCursor(9,2); lcd.print(voltage); delay(1000); } void SensorTemp(){ int val; double temp; val=analogRead(T); temp=Thermister(val); long Temperature=temp; lcd.setCursor(13,3); lcd.print(Temperature); delay(1000); } //-------------------------------------------------------------------------------------------------- void Button_1(){ boolean buttonIsUp_1 = digitalRead(BUTTON_1); if (buttonWasUp_1 && !buttonIsUp_1) { delay(10); buttonIsUp_1 = digitalRead(BUTTON_1); if (!buttonIsUp_1) { int a=(int)regim; regim=(Regim_t)a++; } } // запоминаем последнее состояние кнопки для новой итерации buttonWasUp_1 = buttonIsUp_1; } void Button_2(){ boolean buttonIsUp_2 = digitalRead(BUTTON_2); if (buttonWasUp_2 && !buttonIsUp_2) { delay(10); buttonIsUp_2 = digitalRead(BUTTON_2); if (!buttonIsUp_2) { regim = OFF; } } // запоминаем последнее состояние кнопки для новой итерации buttonWasUp_2 = buttonIsUp_2; } //-------------------------------------------------------------------------------------------------- void setup (){ pinMode(motor, OUTPUT); //транзистор управляющий клапаном на 6-й ноге pinMode(BUTTON_1, INPUT_PULLUP); //кнопка 1 - вход pinMode(BUTTON_2, INPUT_PULLUP); //кнопка 2 - вход pinMode(speedsensor, INPUT); attachInterrupt(0,SpeedSensor, FALLING); //Подсчет импульсов voltage = analogRead(VIN); lcd.begin(20, 4); //устанавливаем размер (количество столбцов и строк) экрана lcd.setCursor(0,1); //устанавливаем курсор lcd.print("Smazka-"); //печатаем первую часть строки lcd.setCursor(1,2); //устанавливаем курсор lcd.print("Voltage-"); //печатаем первую часть строки lcd.setCursor(1,3); //устанавливаем курсор lcd.print("Temperature-"); //печатаем первую часть строки } //-------------------------------------------------------------------------------------------------- double Thermister(int RawADC){ double Temp; Temp = log(((10240000/RawADC) - 10000)); Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp ); Temp = Temp - 273.15; return Temp; } //-------------------------------------------------------------------------------------------------- void loop() { if ((int)regim > DOGD){ regim = GOROD; } SpeedSensorReturnRegim(); SensorTemp(); SensorVolt(); SpeedSensor();
Button_1()
Button_2()
switch (regim){ case GOROD: OutLCD("Gorod"); WorkPump( interval_27 ); break; case TRASSA: OutLCD("Trassa"); WorkPump( interval_17 ); break; case DOGD: OutLCD("Dogd"); WorkPump( interval_7 ); break; case OFF: OutLCD("OFF"); Off(); break; } }
В чем смысл этой проверки и зачем здесь использовать приведение типов?
"В чем смысл этой проверки"
смысл в том, что наступление событий следующего режима (после режима DOGD идет режим OFF) происходит по нажатию другой кнопки2 (button_2), а здесь по нажатию кнопки1 (button_1) после режима "дождь" наступает опять режим "город"
"и зачем здесь использовать приведение типов?"
а вот на это я не могу ответить, уже не помню почему так...(((
void loop() {
нет закрывающей скобки.
Вы говорите что нет смены режима а можете подсказать какой номер строчки вызывает функцию отвечающая за выбор режима.
void loop() {
нет закрывающей скобки.
Есть))) иначе бы компиляция не проходила бы
Вы говорите что нет смены режима а можете подсказать какой номер строчки вызывает функцию отвечающая за выбор режима.
Смена режимов по нажатию кнопок производится в функциях void Button_1() и void Button_2().
Строки 169, 170 (для первой функции)
Button_1() _ Вы сами писали?
смотрите:
Условием узменения режима является то, что сохраненное и прочтенное состояния кнопки: ОБА = "не нажата" + то, что в течении 10 миллисекунд работы функции, кнопку нажмут. Именно в это 10 мс, несмотря на то, что в следующей каманде главного цикла Вы еще 10 мс ожидаете еще лдну кнопку!
И как бедному пользователю попасть пальцем в нужные 10 мс, притом что остальной код тоже требует времени.
Итого получаем, что кнопку нужно нажать в конкретные 10 мс, раз в (... ну сколько там цикл выполняется, учитывая логарифмы и double умножения) пусть 300мс. Что равно 3%. То есть вероятность того, что удастся нажать кнопку сменыф режима - 3%. КРУТО!
Уберите delay() из чтения состояния кнопки - это КТО(???) Вас научил так кодировать? Вы с этим мальчиком больше не дружите, а то он еще и курить научит!
Не стану кодировать за Вас, но стоит сделать примерно так:
если <СЕЙЧАС> нажата, а <ДО ТОГО> отпущена - то меняем режим и запоминаем состояние кнопки <ДО ТОГО>. У Вас ОЧЕНЬ длинный главный цикл, он сам по себе устранит дребезг. Можете 2 раза считать текущее состояние с разницей НЕ БОЛЕЕ 1 миллисекунды, и тогда убедиться, что кнопка нажата. Но никак не ждать конкретные 10 мс! В главном цикле замирать на 10мс в трепетном ожидании нажатия, при том, что все остальное время по кнопке можно хоть кувалдой бить - это крутой бэдкодинг, честное пионерское!
=================================================
НО(!!!!!) при таком длинном главном цикле на изменение состояния кнопки нужно вешать прерывание. Возьмите из примеров. там все совсеми просто. Иначе пользоваться будет все равно неудобно.
НО(!!!!!) при таком длинном главном цикле на изменение состояния кнопки нужно вешать прерывание. Возьмите из примеров. там все совсеми просто. Иначе пользоваться будет все равно неудобно.
ну, да - длинный главный цикл... нужно вешать
*все сошли с ума.
Раньше смена режимов у меня была другая(брал отсюда, из темы "кнопка в помощь начинающему").
Но результаты пробного теста со светодиодами меня не удовлетворили, поэтому код работы кнопок взял с амперки (эксперимент 10).
Про delay догадывался, что надо ее убрать, но что настолько все плохо, даже не думал(.
Спасибо, буду вечером пробовать(на работе нет ни ардуины, ни кнопок)
ну, да - длинный главный цикл... нужно вешать
*все сошли с ума.
Солнце! Тебе что-то не нравится? У тебя есть любимый "титановый велосипед" на кнопку и ты его предлагаешь?
PinChange вроде как для удобства придумали, как раз для продходящего случая.
Я в шоке и не могу понять, что тебя так затронуло? Термин "длинный главный цикл" ;). Ну ты прикинь время выполнения loop() у автора. Там много интересного происходит. Ты противник жаргонизмов типа "подвешивать"? Ну прости, я думал тут только один, на чистоте языка двинутый, придется или терпеть или банить.
---------------------------------------------------------
Если ты по делу, а не просто с похмелья - то поясни, если не трудно: ты против прерываний и за опрос, или что-то еще имел ввиду? Торможу я, не въехал в наезд, уж прости.
Если ты просто сторонник опроса, вместо прерываний - да ради Б..га! Я спорить не стану, это на уровне - кому-то нравятся сисястые, а кому-то жопастые.
При хорошем коде - все равно. С прерываниями - просто (конкретно) мне проще.
Я в шоке и не могу понять, что тебя так затронуло? Термин "длинный главный цикл" ;). Ну ты прикинь время выполнения loop() у автора.
возьми и прикинь - сколько?.. без делаев, не на глаз, а замерь. если начал что-то там считать, то нужно знать, прежде, чем утверждать, что длинный настолько, что прерывания нужно вешать.
Спасибо, буду вечером пробовать(на работе нет ни ардуины, ни кнопок)
Смотри: в примерах код НИЧЕГО, кроме работы с кнопкой не делает.
В твоем случае нужно сравнивать время, в течении которого ты интересуешься кнопкой и ПОЛНОЕ время работы loop() - то есть главного цикла, как бы не веселил этот термин нашего друга.
Твоя функция срабатывает ТОЛЬКО если пользователь нажмет кнопку во время ее работы. А это 3% от времени работы цикла. В примерах опрос кнопки - в самом loop() и кроме него - минимальной по времени код.
Понятно ли, что если я нажал кнопку вне (по времени) функции button-что-то-там(), то у меня , при следующем вызове функции, старое состояние - отпущена, новое - нажата и IF уже не выполнится? поэтому я советую PinChange преравание. Есть в сети, может и тут, на форуме, много уже готовых решений на PCINT и с устранением дребезга и все такое прочее, и все состояния - нажате, отпускание, удержание, дабл клик и т.п.
Смотри: в примерах код НИЧЕГО, кроме работы с кнопкой не делает.
прекращай бредить и скажи мне, сколько времени длится один цикл кода ТС(без делаев) - ты же точно знаешь, что это время настолько большое, что ты успеешь нажать на кнопку чаще, чем два раза... :D
Калапуций, ты хоть сильно невежлив, но я и сам - такой же! ;)
Вынужден признать, что double математика не такая страшная. Ты - прав, я не полность, так скажем.
Я написал мелкий код для проверки, сколько времени занимают вычисления температуры у автора, вот код:
вот платка - Амперковский Леонардо, ближе всего лежала.
а вот вывод ком-порта:
Так что, даже с парой дополнительных тригонометрических функций мы имеем 12 микросекунд на шаг вычислений.
Так что, без делеев, цикл не такой уж и страшный.
Доволен? Коллега ;)
смысл в том, что наступление событий следующего режима (после режима DOGD идет режим OFF) происходит по нажатию другой кнопки2 (button_2), а здесь по нажатию кнопки1 (button_1) после режима "дождь" наступает опять режим "город"
Подозреваю, что вы намеряли время выполнения вообще пустого цикла, в смысле совсем пустого. Похоже что оптимизатор выкинул все ваши высиления как нетребующиеся и бесполезные. 12 микросекунд на 16Мгц это "всего" около 150 команд процессора. Помнится времена выполнения одного вызова из float функций в glibc авторами назывались поболее.
P.S. Так бывает. Тестировать время исполнения надо не на "липовых" примерах, оптимизатор он жеж не такой "дурак" уже, как тут встречаются (не про вас, так, общее замечание). :)
Я к тому, что после проверки у Вас стоит вызов функции, в которой значение переменной опять изменяется.
Хоть убейте, но я в этом не вижу криминала, да, оно должно поменяться, но при определенных условиях, а они еще должны наступить.