Помогите найти ошибку

ИльяК

✩✩✩✩✩✩✩
10 Мар 2020
5
0
Всем привет. Посмотрите пожалуйста мой код. Закралась ошибка. Не могу ее вычислить.

Данный код предназначен для управление коллекторным двигателем. В данном случае приводом медогонки.
Предполагается работа в двух режимах - ручном и автоматическом.

Ручной режим : нажали кнопку"Старт" - Двигатель плавно разгоняется до заданного значения - Продолжает работать с заданной скоростью при удержании кнопки Старт- остановка при отпускании

Автоматический режим : Однократное нажатие на Старт - Плавный разгон - Работа на заданной скорости нужное время - Остановка

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

Сам код :

#define SMOOTH_TIME 15000 // время разгона от нуля до макс. в милисек
//#define HOLD_TIME 80

const int pwmPin = 9;//подключ. двигателя
const int Speed = A0;
const int Time = A1;

const int MIN_speed = 0; //мин. частота вращ. двигателя
const int MAX_speed = 85;// макс. частота вращ двигателя
const int MIN_time = 10;//мин. время работы программы в сек.
const int MAX_time = 300;////макс. время работы программы в сек.

long previousMillis = 0;
long MotorDelay = 0;
long interval = SMOOTH_TIME/255;//скорость разгона двигателя

int i = 0;
int val_1 = 0;
int val_prc;
long val_2 = 0;

boolean MotorRun = false ;//начальное состояние двигателя

#include <LiquidCrystal_I2C.h> //библиотека дисплея
LiquidCrystal_I2C lcd(0x27,16,2);

#include <GyverButton.h> //библиотека работы с кнопкой и назначения кнопок управления
GButton Button(2, LOW_PULL, NORM_OPEN);
GButton Manual(5, LOW_PULL, NORM_OPEN);
GButton Auto(7, LOW_PULL, NORM_OPEN);

void setup()
{
pinMode(pwmPin, OUTPUT);
pinMode(Speed, INPUT);
pinMode(Time, INPUT);
//Button.setTickMode(MANUAL);
Serial.begin(9600);

lcd.init(); // Инициализация дисплея
lcd.backlight(); // Подключение подсветки
lcd.setCursor(0,0); // Установка курсора в начало первой строки
lcd.print("PrivodMedogonki"); // Набор текста на первой строке
lcd.setCursor(8,1); // Установка курсора в начало второй строки
lcd.print("Vers.1");
delay(1000);
}

void loop ()//выбор режима работы и вывод инфо на экран
{
Manual.tick();//опрос кнопки перехода в ручной режим управления
Auto.tick();//опрос кнопки перехода в автоматический режим управления

if (Auto.isHold())//если удержана кнопка автоматич.работы
{
MotorRunAuto();
Serial.println("Auto");
}

else if (Manual.isHold())//если удержана кнопка ручной.работы
{
MotorRunManual();
Serial.println("Manual");
}

else if (!Manual.isHold()&& !Auto.isHold())//не выбран режим работы
{
i = 0;
analogWrite(pwmPin,i);
Serial.println("Vubor");
MotorRun = false ;
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("PrivodMedogonki");
lcd.setCursor(0,1);
lcd.print(" "); // Набор текста на первой строке
lcd.setCursor(2,1); // Установка курсора в начало второй строки
lcd.print("Vyberi_rezim");
}

else if (Manual.isHold()&& Auto.isHold())//аварийный вариает
{
i = 0;
analogWrite(pwmPin,i);
Serial.println("Oshibka");
MotorRun = false ;
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("PrivodMedogonki");
lcd.setCursor(0,1);
lcd.print(" "); // Набор текста на первой строке
lcd.setCursor(4,1); // Установка курсора в начало второй строки
lcd.print("Oshibka");
}
delay (100);

}

void MotorRunAuto() //функция автоматич. управления
{
Serial.println(MotorRun);
//MotorRun = false ;///!!!!
lcd.setCursor(0,0); //вывод параметров работы на экран
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("Auto: Speed %");
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" Time c");

val_1=analogRead(Speed); //читаем потенциометр на частоту вращ. двигателя
val_1=map(val_1, 0, 1023, MIN_speed , MAX_speed);
val_1=constrain(val_1, 0, 255);
val_prc=val_1*100/MAX_speed;//перевод заданного значения в проценты от максимума
lcd.setCursor(12,0);//вывод значенияв процентах на экран
lcd.print(val_prc);

val_2=analogRead(Time);//читаем потенциометр на время работы двигателя
val_2=map(val_2, 0, 1023, MIN_time , MAX_time);
lcd.setCursor(12,1);//вывод времени работы на экран
lcd.print(val_2);
//val_2=constrain(val, 0, 255);
//i=MIN_speed;
//Serial.println(val_2);

Button.tick();//опрос кнопки на начало работы
Serial.println(MotorRun);
if(Button.isHolded())//если кнопка нажата
{
MotorRun = true;//разрешить работу
Serial.println("AutoStart");
//Serial.println(MotorRun);
}
if (MotorRun == true)
{
unsigned long currentMillis = millis();//начало разгона двигателя
if(currentMillis-previousMillis > interval)
{
previousMillis = currentMillis;
analogWrite(pwmPin,i);
i ++;
//Serial.println(i);
if (i > val_1)
{
i = val_1;
//Serial.println(i);
analogWrite(pwmPin,i);

unsigned long MotorTime = millis(); //выдержка времени после выхода на режим
if (MotorTime - MotorDelay > val_2*1000)
{
MotorDelay = MotorTime;
i = 0;
analogWrite(pwmPin,i);
//Serial.println(millis());
MotorRun = false ;
Serial.println(MotorRun);
}
}
}
}
else /*if (!Button.isPress())*///если кнопка не нажата
{
MotorRun = false ;//запрет работы
i=0;
analogWrite(pwmPin,i);
Serial.println("AutoStop");
//Serial.println(MotorRun);
}
}



void MotorRunManual() //функция ручного управления по аналогии с автоматическим режимом
{
Serial.println(MotorRun);
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print("Manual: ");
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" Speed %");

val_1=analogRead(Speed);
val_1=map(val_1, 0, 1023, MIN_speed , MAX_speed);
val_1=constrain(val_1, 0, 255);
val_prc=val_1*100/MAX_speed;
lcd.setCursor(12,1);
lcd.print(val_prc);
//i=MIN_speed;
//Serial.println(i);

Serial.println(MotorRun);

Button.tick();
if(Button.isHold())//если кнопка нажата
{
Serial.println("ManualStart");
unsigned long currentMillis = millis();//начало разгона
if(currentMillis-previousMillis > interval)
{
previousMillis = currentMillis;
analogWrite(pwmPin,i);
i ++;
if (i>val_1)//работа с постоянной частотой вращения
{
i = val_1;
}
}
}
else /* (Button.isRelease())//если кнопка отпушена*/
{
//
i=0;
analogWrite(pwmPin,i);
MotorRun = false ;//запрет работы
Serial.println("ManualStop");
//Serial.println(MotorRun);
}
}


Ошибка проявляет себя так : каждый режим по отдельности работает нормально. Но если произвели старт двигателя в ручном режиме, а затем произвели переход в автоматический режим происходит однократное выполнение программы самопроизвольно. Такое чувство как-будто программа запоминает состояние кнопки Старт в ручном режиме и при переходе в автоматический режим не обнуляет состояние кнопки
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
начал смотреть, сломал глаза на ифак и элсах. ну очень неудобно читать. тут запросто можно наделать логических косяков. Можно сделать примерно так, для удобства чтения, в псевдокоде

если удерживается кнопка авто то
{
делаем то что надо
return; // вот этот ретурн завершит текущее выполнение цикла loop и этот самый цикл начнется сначала.
}

это раз. потом я всегда за минимизацию кол-ва кнопоко.
переключатель ручной / авто режим я бы повесил на клик единственной кнопки, клик меняет режим на другой.
а пуск по удержанию, тогда и кнопок меньше и кол-во условий тоже
 

ИльяК

✩✩✩✩✩✩✩
10 Мар 2020
5
0
Тогда по другому вопрос сформирую.

Как можно обнулить состояние кнопки при использовании библиотеки GyverButton после удержания Button.Hold?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
При считывании последнего состояния кнопка обнуляется до следующего раза. Но считывать надо правильные состояния.
Для удержания это будет isHold, а функции Hold я вообще не нашел в своей версии
да, функция isHold будет выдавать true раз в какое то время, пока держится кнопка.
 

ИльяК

✩✩✩✩✩✩✩
10 Мар 2020
5
0
А если есть две функции и в обоих используется одна кнопка, при переходе из одной функции в другую состояние кнопки сохраняется или обнуляется?

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

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
При считывании любого состояния - сбрасывается. независимо от кол-ва функций. одна кнопа - одно состояние.
Но я тебя понял , нет четкой информации , когда функция сменилась.
Есть еще двойной кликю Больше 2-х раз на мой взгляд клиакать не айс.
я удержание использую для отмены действий, для изменения параметров вместо поворота энкодера например.

Задача не самая простая, но требует аккуратности, Вот посидел минут 20 и состряпал скетч с одной кнопкой на 10 пине.
Что делает кнопка:
Двойной клик - переключает режимы с 0 , ручного на 1 автоматический и обратно.
Одинарный клик в ручном режиме переводит в режим работы и ждет удержания кнопки. При удерживании скорость увеличивается до 100 и держится до отпускания кнопки. В любой момент отпускания скорость начнет снижаться. Если достигнет 0, то вернется из рабочего режима и можно будет опять выбрать режим. Если удерживать опять , не дождавшись 0, то будет опять увеличиваться. Так можно до посинения играть
В авто режиме (1-ый) сразу будет работать цикл увеличение-удержание-уменьшение скорости.
Да, использовал delay, себе бы так делать не стал ) но для примера сойдет. Да и в ручном режиме есть delay для масштабирования скорости.
Вся информация выводиться в serial на скорости 115200
C++:
#include "GyverButton.h"

uint8_t global_mode = 0;   // режимы 0 ручной  и 1 авто
uint8_t old_global_mode = 100; //
bool is_work = false;

#define BTN_PIN 10
GButton buttPD(BTN_PIN, false);  //

void setup() {
  Serial.begin(115200); // нужно для отладки
}

void loop() {
  buttPD.tick();

  if (is_work)
  {
    if (global_mode == 1)
    {
      Auto_moto();
      return;
    };
    if (buttPD.isHold() && (global_mode == 0)) Manual_moto(true); else Manual_moto(false);
  }
  else
  {
    if (buttPD.isDouble()) // двойной клик - переключение режимов.
    {
      global_mode = ++global_mode % 2;
      return;
    }

    if (buttPD.isSingle())  // одиночный режим включает мотор в авто и ждет нажатия в ручном.
    {
      Serial.println("к запуску готов..");
      is_work = true;
      return;
    };
  };
  if (old_global_mode != global_mode)   // просто показываем изменение режима, ни на что не влияет
  {
    Serial.println("Режим " + String(global_mode));
    old_global_mode = global_mode;
  };
}


void Auto_moto()  // тут в принципе можно забирать все управление и делать с делеями, то тогда аварийное выключение только розеткой.
{
  Serial.println("Старт в авто режиме");
  for (int i = 0; i < 100; i++)
  {
    Serial.println("Скорость: " + String(i));
    delay(100);
  };
  for (int i = 0; i < 4; i++) // а можно одним делеем сделать
  {
    Serial.println("Заданная скорость: " + String(i));
    delay(500);
  };
  for (int i = 100; i > 0; i--)
  {
    Serial.println("Скорость: " + String(i));
    delay(100);
  };
  Serial.println("Стоп в авто режиме");
  is_work = false;
};


void Manual_moto(bool go)
{
  static bool last_go = false;
  static int8_t curspeed = 0;
  if (go)
  {
    // Разгон
    last_go = true;
    if (curspeed < 100) curspeed++;
    Serial.println("Скорость: " + String(curspeed));
    delay(50);
  }
  else
  {
    // торможение или не начато
    if (!last_go) return;
    curspeed--;
    delay(50);
    Serial.println("Скорость: " + String(curspeed));
    if (curspeed == 0)
    {
      last_go = false;
      is_work = false;
    }
  }
};
 
  • Лойс +1
Реакции: Arhat109