Ищу исполнителя скетча для управл. оборотами ШД кнопками и вывод на LCD

leshak
Offline
Зарегистрирован: 29.09.2011

Nikolai54 пишет:

От нашей бедности и нет возможности купить новый "насос перистальтический"

Хм... тогда либо насос ОЧЕНЬ дорогой, либо вы не представляете себе стоимость программера раз в раздел "ищу исполнителя" сунулись :)

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

Nikolai54 пишет:

для подачи физ раствора, 

Хм.. а зачем вам тогда выводить скорость в RPM? Было-бы юзабеленей в каких-то "милилитрах в минуту" и подобном.

В идеале - нужен еще что-то такое Расходомер жидкости | Аппаратная платформа Arduino

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

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

 

leshak
Offline
Зарегистрирован: 29.09.2011

Nikolai54 пишет:

 до сих пор у нас в экспериментальной установки работает.

Сын приходит к отцу-программисту:
- Пап, а почему солнце встает на востоке, а садится на западе?
- Что, каждый день?
- Да.
- Без сбоев?
- Ну да.
- Это хорошо, только смотри, ничего там не трогай!

 

Nikolai54
Offline
Зарегистрирован: 23.06.2013

Нам бы скорость поднять, "миллилитрах в минуту" нам не принципиально какие там будут цифры важно стабильно мы бы для себя отработали, хорошо бы шаг уменьшить 1 шаг и при удержании кнопки допустим переходил на шаг например 10 , 1,10 это условно пускай показывает на LCD другие цифры уже не до жиру.

leshak
Offline
Зарегистрирован: 29.09.2011

Nikolai54 пишет:

 LCD другие цифры уже не до жиру.

Ну, если ваc на LCD устраивает цифра "в попугаях" (кстати я не уверен, что он сейчас там "настоящую скорость показывает"), то тут все просто:

Массив:

const int speed_ticks[] = {-1, 600, 300, 200, 150, 120, 100, 86, 75, 67, 60, 55, 50, 46, 43};

Меняем эти циры. Кроме первой (-1). И общие количество менять нельзя. Можно подкручивать сами цифры. Это задержка между "шагами". Чем меньше задержка - тем быстрее крутимся. Когда вы нажимаете кнопки +/- вы, на самом деле просто переходите по этому массиву вправо/влево. Нажали + - задержка между шагами степера стала 600, нажали еще раз стала 300,150,120 и т.д.

Если нужно "сразу все" увеличить умножить, то меняем циферку в 

 Timer1.initialize(100);

По идее вот это 100 это, в микросекундах с какой частотой тикает таймер. А 600,300,200 - это сколько раз должен тикнуть таймер прежде чем степпер сделает следующий шаг. То есть сейчас, вы проходите такие скорости

Шаг каждые "600*100 микросекунд", "300*100 микросекунд", "150*100 микросекунд"

Вообщем поиграйтесь с этим цифрами. В массиве и Timer1.initialize();

LCD , при этом, будет показывать все "по старому" :)

SONIC300077
Offline
Зарегистрирован: 15.10.2013

Добрый вечер ! У меня вопрос по поведу этого же скетча, есть вариант сделать так чтобы при нажатии на кнопку увеличение скорость , двигатель делал только конкретное количество шагов ?

vvadim
Offline
Зарегистрирован: 23.05.2012

SONIC300077 пишет:

Добрый вечер ! У меня вопрос по поведу этого же скетча, есть вариант сделать так чтобы при нажатии на кнопку увеличение скорость , двигатель делал только конкретное количество шагов ?

Проще сделать отдельную кнопку

SONIC300077
Offline
Зарегистрирован: 15.10.2013

Это было бы еще лучше ! ток как это сделать?

leshak
Offline
Зарегистрирован: 29.09.2011

Возможно память меня подводит, но мне смутно помнится, что я, в итоге, "доводил" этот скетч уже напрямую с топик-стартером. С помощью skype. Так что почти наверняка тут не последняя версия скетча. Так что начинать переделки, думаю, нужно с просьбы к Nikolai54 выложить "актуальную версию"

P.S. Это, конечно, если я не перепутал проекты. Но вроде "физиологи" были тут на форуме одни.

SONIC300077
Offline
Зарегистрирован: 15.10.2013

вот есть еще скатч может в нем проще поправить

вот видео работы скетча http://www.youtube.com/watch?feature=player_embedded&v=zYI5G8f9034


#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <Stepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);

int m=0; //переменная для экранов меню
// количество шагов мотора
#define STEPS 200

// создает класс шагового двигателя и объявляет какими ножками Ардуино
Stepper stepper(STEPS,2,3);

int Step_rev = 0;
int Speed =0;


#define ButtonUp  7
#define ButtonLeft  A0
#define ButtonRight A1
#define ButtonMode  A2
#define ButtonStop  A3


void setup(){
  digitalRead(ButtonUp);
  digitalRead(ButtonLeft);
  digitalRead(ButtonRight);
  digitalRead(ButtonMode);
  digitalRead(ButtonStop);
 
  lcd.init();
  lcd.backlight();
//Выведем на дисплей стартовое сообщение на 2 секунды
  lcd.setCursor(0, 0);
  lcd.print("    *****    ");
  lcd.setCursor(5, 1);
  lcd.print("Motor");
  delay(3000);
  lcd.clear();
  //Считаем из постоянной памяти данные
  EEPROM_readAnything(0, Step_rev);
  EEPROM_readAnything(20, Speed);

}

void loop() {
 
 
  //Обработка нажатия кнопки меню
  if (digitalRead(ButtonLeft) == HIGH)
  {
  m++;//увеличиваем переменную уровня меню
  if (m>2)//если уровень больше 2
  {
  m=0;// то вернуться к началу
  }
  delay (300);
  lcd.clear();
  }
//Обработка нажатия кнопки
if (digitalRead(ButtonMode) == HIGH)

{
  // один полный круг вперед
  stepper.setSpeed(Speed);
  stepper.step(Step_rev);
 
}
//Обработка нажатия кнопки
if (digitalRead(ButtonUp) == HIGH)

{
  // один полный круг назад
  stepper.setSpeed(Speed);
  stepper.step(-Step_rev);
 
}
//Обработка нажатия для Kontur - 1 +
    if (digitalRead(ButtonRight) == HIGH && m==1)//если находимся на экране с переменной Kontur - 1
  {
  Step_rev++;//то при нажатии кнопки + увеличиваем переменную Step_rev на единицу
  if (Step_rev>800)//если переменная достигла придела в 800 едениц
  {
  Step_rev=0;//то возвращаем ее к 0
  }
  delay (50);
  lcd.clear();
  }
//Обработка нажатия для Kontur - 1 -
    if (digitalRead(ButtonStop) == HIGH && m==1)//если находимся на экране с переменной Kontur - 1
  {
  Step_rev--;//то при нажатии кнопки - уменьшаем переменную Step_rev на единицу
  if (Step_rev<0)//если переменная достигла придела в 0 едениц
  {
  Step_rev=800;//то возвращаем ее к 800
  }
  delay (50);
  lcd.clear();
  }

  //speed
  //Обработка нажатия для Kontur - 1 +
    if (digitalRead(ButtonRight) == HIGH && m==2)//если находимся на экране с переменной Kontur - 1
  {
  Speed++;//то при нажатии кнопки + увеличиваем переменную Speed на единицу
  if (Speed>300)//если переменная достигла придела в 300 едениц
  {
  Speed=0;//то возвращаем ее к 0
  }
  delay (50);
  lcd.clear();
  }
  //speed
  if (digitalRead(ButtonStop) == HIGH && m==2)//если находимся на экране с переменной Kontur - 1
  {
  Speed--;//то при нажатии кнопки - уменьшаем переменную Speed на единицу
  if (Speed<0)//если переменная достигла придела в 0 едениц
  {
  Speed=300;//то возвращаем ее к 300
  }
  delay (50);
  lcd.clear();
  }
 
  //menu
  if (m==0){
   
  lcd.setCursor(0, 0);
  lcd.print("Steps: ");
  lcd.print(Step_rev);
  lcd.setCursor(0, 1);
  lcd.print("Speed: ");
  lcd.print(Speed);
 
  }
 
  else if (m==1)
  {
  lcd.setCursor(0, 0);
  lcd.print(" Steps = ");
  lcd.print(Step_rev);
  }
 
  else if (m==2)
  {
  lcd.setCursor(0, 0);
  lcd.print(" Speed = ");
  lcd.print(Speed);
  }
 

 
  if (digitalRead(ButtonLeft) == HIGH){
    EEPROM_writeAnything(0, Step_rev);
    EEPROM_writeAnything(2, Speed);
   
    }
 
}

 

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

//Обработка нажатия кнопки
if (digitalRead(ButtonMode) == HIGH)

{
  // один полный круг вперед
  stepper.setSpeed(Speed);
  stepper.step(Step_rev);
 
}
//Обработка нажатия кнопки
if (digitalRead(ButtonUp) == HIGH)

{
  // один полный круг назад
  stepper.setSpeed(Speed);
  stepper.step(-Step_rev);
 
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Ну, дык а в чем проблема?

Заведите себе переменную direction. Котая будет иметь значите 1 или -1

Одной кнопкой "крутите",

stepper.step(direction*Step_rev)

А другой кнопкой меняйте, значение direction.

direction*=-1;

Логика на уровне "умножение положительных и отрицательных чисел".

SONIC300077
Offline
Зарегистрирован: 15.10.2013

leshak пишет:

Ну, дык а в чем проблема?

Заведите себе переменную direction. Котая будет иметь значите 1 или -1

Одной кнопкой "крутите",

stepper.step(direction*Step_rev)

А другой кнопкой меняйте, значение direction.

direction*=-1;

Логика на уровне "умножение положительных и отрицательных чисел".

да я это понимаю а вот как правильно это все оформить не знаю ! я извиняюсь может мне и нужно побольше с мелочей поучиться, но мне очень нужно это сделать ! если вас не затруднит можете по подробней пожалуйста !

SONIC300077
Offline
Зарегистрирован: 15.10.2013

вот создал переменную вроде как работает а вот как менять значение этой переменной с кнопки не как не пойму?

// Собственно библиотека
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <Stepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);

int m=0; //переменная для экранов меню
// количество шагов мотора
#define STEPS 200

// создает класс шагового двигателя и объявляет какими ножками Ардуино
Stepper stepper(STEPS,2,3);

int direct ;

int Step_rev = 0;

int Speed =0;


#define ButtonUp  7
#define ButtonLeft  A0
#define ButtonRight A1
#define ButtonMode  A2
#define ButtonStop  A3


void setup(){
  digitalRead(ButtonUp);
  digitalRead(ButtonLeft);
  digitalRead(ButtonRight);
  digitalRead(ButtonMode);
  digitalRead(ButtonStop);
  
  lcd.init();
  lcd.backlight(); //Выведем на дисплей стартовое сообщение на 2 секунды
  lcd.setCursor(0, 0);
  lcd.print("    *****    ");
  lcd.setCursor(5, 1);
  lcd.print("Motor");
  delay(3000);
  lcd.clear();
 
  //Считаем из постоянной памяти заданную скорость и кол шагов
  EEPROM_readAnything(0, Step_rev);
  EEPROM_readAnything(20, Speed);

  direct = -1;

}

void loop() {
  
  
  //Обработка нажатия кнопки меню
  if (digitalRead(ButtonLeft) == HIGH)
  {
  m++;//увеличиваем переменную уровня меню
  if (m>2)//если уровень больше 2
  {
  m=0;// то вернуться к началу
  }
  delay (300);
  lcd.clear();
  }
 
 //Обработка нажатия кнопки
if (digitalRead(ButtonMode) == HIGH)

{
  // один полный круг вперед
  stepper.setSpeed(Speed);
  stepper.step(direct*Step_rev);
  
}
 //Обработка нажатия кнопки смена направления
if (digitalRead(ButtonUp) == HIGH)
{
 
  
}

//Обработка нажатия для Kontur - 1 +
    if (digitalRead(ButtonRight) == HIGH && m==1)//если находимся на экране с переменной Kontur - 1
  {
  Step_rev++;//то при нажатии кнопки + увеличиваем переменную Step_rev на единицу
  if (Step_rev>800)//если переменная достигла придела в 800 едениц
  {
  Step_rev=0;//то возвращаем ее к 0
  }
  delay (50);
  lcd.clear();
  }
//Обработка нажатия для Kontur - 1 -
    if (digitalRead(ButtonStop) == HIGH && m==1)//если находимся на экране с переменной Kontur - 1
  {
  Step_rev--;//то при нажатии кнопки - уменьшаем переменную Step_rev на единицу
  if (Step_rev<0)//если переменная достигла придела в 0 едениц
  {
  Step_rev=800;//то возвращаем ее к 800
  }
  delay (50);
  lcd.clear();
  }

  //speed
  //Обработка нажатия для Kontur - 1 +
    if (digitalRead(ButtonRight) == HIGH && m==2)//если находимся на экране с переменной Kontur - 1
  {
  Speed++;//то при нажатии кнопки + увеличиваем переменную Speed на единицу
  if (Speed>300)//если переменная достигла придела в 300 едениц
  {
  Speed=0;//то возвращаем ее к 0
  }
  delay (50);
  lcd.clear();
  }
  //speed
  if (digitalRead(ButtonStop) == HIGH && m==2)//если находимся на экране с переменной Kontur - 1
  {
  Speed--;//то при нажатии кнопки - уменьшаем переменную Speed на единицу
  if (Speed<0)//если переменная достигла придела в 0 едениц
  {
  Speed=300;//то возвращаем ее к 300
  }
  delay (50);
  lcd.clear();
  }
  
  //menu
  if (m==0){
    
  lcd.setCursor(0, 0);
  lcd.print("Steps: ");
  lcd.print(Step_rev);
  lcd.setCursor(0, 1);
  lcd.print("Speed: ");
  lcd.print(Speed);
  
  }
  
  else if (m==1)
  {
  lcd.setCursor(0, 0);
  lcd.print(" Steps = ");
  lcd.print(Step_rev);
  }
  
  else if (m==2)
  {
  lcd.setCursor(0, 0);
  lcd.print(" Speed = ");
  lcd.print(Speed);
  }
  

  
  if (digitalRead(ButtonLeft) == HIGH){
    EEPROM_writeAnything(0, Step_rev);
    EEPROM_writeAnything(2, Speed);
    
    }
  
}

 

SONIC300077
Offline
Зарегистрирован: 15.10.2013

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

а так вот что у меня получилось буду рад если посмотрите !

// Собственно библиотека
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <Stepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);

int m=0; //переменная для экранов меню
// количество шагов мотора
#define STEPS 200

// создает класс шагового двигателя и объявляет какими ножками Ардуино
Stepper stepper(STEPS,2,3);

int regim=1;

int direct = 1;

int Step_rev = 0;

int Speed =0;

long previousMillis = 0;

#define ButtonUp  7
#define ButtonLeft  A0
#define ButtonRight A1
#define ButtonMode  A2
#define ButtonStop  A3


void setup(){
  digitalRead(ButtonUp);
  digitalRead(ButtonLeft);
  digitalRead(ButtonRight);
  digitalRead(ButtonMode);
  digitalRead(ButtonStop);
  
  lcd.init();
  lcd.backlight(); //Выведем на дисплей стартовое сообщение на 2 секунды
  lcd.setCursor(0, 0);
  lcd.print("    *****    ");
  lcd.setCursor(5, 1);
  lcd.print("Motor");
  delay(3000);
  lcd.clear();
 
  //Считаем из постоянной памяти заданную скорость и кол шагов
  EEPROM_readAnything(0, Step_rev);
  EEPROM_readAnything(20, Speed);

  direct = 1;

}

void loop() {
  
  
  //Обработка нажатия кнопки меню
  if (digitalRead(ButtonLeft) == HIGH)
  {
  m++;//увеличиваем переменную уровня меню
  if (m>2)//если уровень больше 2
  {
  m=0;// то вернуться к началу
  }
  delay (300);
  lcd.clear();
  }
 
 //Обработка нажатия кнопки
if (digitalRead(ButtonMode) == HIGH){

{
  // один полный круг вперед
  stepper.setSpeed(Speed);
  stepper.step(direct*Step_rev);
  
}
 delay (1000);
}
 
 //Обработка нажатия кнопки смена направления
if (digitalRead(ButtonUp) == HIGH ){//если кнопка нажата ... 
{  
 regim++;
 if(regim>2)//ограничим количество режимов 
        { 
          regim=1;//так как мы используем только одну кнопку, 
                    // то переключать режимы будем циклично 
        }
 } 
if(regim==1)//первый режим 
    { 
       direct=-1;
    } 
    if(regim==2)//второй режим 
    { 
      direct=1;
    }
delay (500);
}
//Обработка нажатия для Kontur - 1 +
    if (digitalRead(ButtonRight) == HIGH && m==1)//если находимся на экране с переменной Kontur - 1
  {
  Step_rev++;//то при нажатии кнопки + увеличиваем переменную Step_rev на единицу
  if (Step_rev>800)//если переменная достигла придела в 800 едениц
  {
  Step_rev=0;//то возвращаем ее к 0
  }
  delay (50);
  lcd.clear();
  }
//Обработка нажатия для Kontur - 1 -
    if (digitalRead(ButtonStop) == HIGH && m==1)//если находимся на экране с переменной Kontur - 1
  {
  Step_rev--;//то при нажатии кнопки - уменьшаем переменную Step_rev на единицу
  if (Step_rev<0)//если переменная достигла придела в 0 едениц
  {
  Step_rev=800;//то возвращаем ее к 800
  }
  delay (50);
  lcd.clear();
  }

  //speed
  //Обработка нажатия для Kontur - 1 +
    if (digitalRead(ButtonRight) == HIGH && m==2)//если находимся на экране с переменной Kontur - 1
  {
  Speed++;//то при нажатии кнопки + увеличиваем переменную Speed на единицу
  if (Speed>300)//если переменная достигла придела в 300 едениц
  {
  Speed=0;//то возвращаем ее к 0
  }
  delay (50);
  lcd.clear();
  }
  //speed
  if (digitalRead(ButtonStop) == HIGH && m==2)//если находимся на экране с переменной Kontur - 1
  {
  Speed--;//то при нажатии кнопки - уменьшаем переменную Speed на единицу
  if (Speed<0)//если переменная достигла придела в 0 едениц
  {
  Speed=300;//то возвращаем ее к 300
  }
  delay (50);
  lcd.clear();
  }
  
  //menu
  if (m==0){
    
  lcd.setCursor(0, 0);
  lcd.print("Steps: ");
  lcd.print(Step_rev);
  lcd.setCursor(0, 1);
  lcd.print("Speed: ");
  lcd.print(Speed);
  
  }
  
  else if (m==1)
  {
  lcd.setCursor(0, 0);
  lcd.print(" Steps = ");
  lcd.print(Step_rev);
  }
  
  else if (m==2)
  {
  lcd.setCursor(0, 0);
  lcd.print(" Speed = ");
  lcd.print(Speed);
  }
  

  
  if (digitalRead(ButtonLeft) == HIGH){
    EEPROM_writeAnything(0, Step_rev);
    EEPROM_writeAnything(2, Speed);
    
    }
  
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

А чего "мне смотреть"? Если работает и ВАМ ПОНЯТНО как - то это лучший вариант :)

Хотя - мне не понятно. Зачем вы вводили еще одну переменную regim. Которая используется только для того, что-бы выставлять direction. Почему, в этом if-е сразу не менять саму direction?

if(direction==1){
  direction=-1;
} else { //  значит direction==-1
   direction=1;
}

 

Или через switch

switch(direction){
  case 1: direction=-1;
            break;
  case -1:direction=1;
            break;
  default:
        Serial.println("Vignya v direction popala"); // если нет ошибок, эта строчка никогда не выполнится
}

Если уж вы хотите через if менять.  

А если, из школы вспомнить что, -1*-1=1 и -1*1=-1, то чередовать 1/-1 можно вообще без if-а. Банальным умножением

direction=-1*direction;

Каждое выполнение этой строки будет менять значение direction с -1 на 1, а 1 на -1

Потом можно вспомнить про унарные операторы (посмотрите в раздел программирование в шапке сайта) и записать это выражение короче

direction*=-1;

Вот мы и пришли, к тому что я вам изначально дал в #60. Которую нужно было только вставить внутрь 

if (digitalRead(ButtonUp) == HIGH )

Почему-то вы для первой кнопки вы "взяли и вставили то что я дал", а для второй - пошли "сочинять" :)  Вообщем-то это очень даже хорошо :)  Только на "сочинениях" и можно чему-то научится. 

А еще, рекомендую вам почитать прикрепленную ветку про кнопки. И попробовать переделать все это так, что-бы ловить, действительно именно "нажатие кнопки", не срабатывать лишний раз при ее "удержании". Избавится от delay().

Nikolai54
Offline
Зарегистрирован: 23.06.2013

leshak пишет:

Возможно память меня подводит, но мне смутно помнится, что я, в итоге, "доводил" этот скетч уже напрямую с топик-стартером. С помощью skype. Так что почти наверняка тут не последняя версия скетча. Так что начинать переделки, думаю, нужно с просьбы к Nikolai54 выложить "актуальную версию"

P.S. Это, конечно, если я не перепутал проекты. Но вроде "физиологи" были тут на форуме одни.


Да делали для нашей лабы, за что Вам большое спасибо, единстевенно что не доделали отклчения двигателя, постоянно под напряжением, греется. А так все ок еще раз спасибо Вам Алексей.

Вот скетч


 

Nikolai54
Offline
Зарегистрирован: 23.06.2013
// by alxarduino@gmail.com aka leshak (arduino.ru)

#include <LiquidCrystal.h>
#include <TimerOne.h>
#include <EEPROM.h> // для сохранения значение в энергонезависимую память



/*******************  Конфигурация ***************************/


// параметры "скорости"
#define START_SPEED 20 // изначальная скорость  "в тиках таймера между шагами"
#define MAX_STEP_DELAY  20000 // максимальная задержка между шагами  (самая медленная возможная скорость)
#define MIN_STEP_DELAY 9    // минимальная задержка между шагами (самая быстрая возвожная скорость)
#define LONG_STEP_MULT 10 // при длинных нажатиях Up/Down скорость будет менятся на SPEP*LONG_STEP_MULT


// параметры "шага изменения скорости" aka "делитель" 
#define START_TIKS_STEP 20 //  изначальные "шаг изменения" скорости кнопками UP/DOWN, сам шаг меняется кнопками RIGH/LEFT
#define TICKS_STEP_STEP 1 // скорость изменения скорости изменения кнопками RIGH/LEFT
#define TICKS_STEP_STEP_LONG 10 // скорость изменения скорости изменения кнопками RIGH/LEFT при "долгом нажатии"
#define MIN_STEP 1 // минимально возможный шаг
#define MAX_STEP 1000 // максимально возможный шаг



// debounce time (milliseconds)
#define DEBOUNCE_TIME 500 // "антидребезг". сколько времени не реагировать на кнопки после очередного нажатия
#define LONG_PRESS 1000 // если кнопка нажата дольше этого времени (миллисекунды), то нажатие считается "длинным нажатием"

#define DIRECTION true // направление вращения

// Отладочная конфигурация

#define DBG Serial // закоментируйте эту строчку если работы с Serial не нужна
#ifdef DBG
  #define SERIAL_SPEED 57600 // скорость ком-порта. в него выводим лог
#endif

/********************   Подключение  *****************************************/
// PINs for Pololu controller
#define PIN_STEP 2
#define PIN_DIR 3


// пины куда экран подключен
#define LCD_PINS 8, 9, 4, 5, 6, 7


/********************Энерго независимая конфиграция   ********/
#define LOAD_STATE_ON_START true // если true - начальные значения ticks/tickStep загрузятся из EEPROM, если false - будут использованы  START_SPEED и START_TIKS_STEP из конфигурации выше
#define SAVE_INTERVAL 5000UL // время в течении которого tiks/tickStep не должны меняться что-бы "сохранится" в EEPROM

#define ADDR_TICKS 0 // начальный адрес, куда будем сохранять переменную tiks
#define ADDR_TICKSTEP (ADDR_TICKS+sizeof(ticks)) // адрес куда будет сохранятся ticks, не менять. вычисляется сам

/************************************************************/
// Глобальные переменные
volatile int ticks=START_SPEED; // сколько нужно сделать "тиков" таймера между шагами
static int tickStep=START_TIKS_STEP; // на сколько меняется скоростьи при каждом нажатии кнопок UP/DOWN
volatile bool isRun=false; // запущено или нет
bool saveRequired=false; // требуется ли сохранение параметров в EEPROM

bool isLongPress=false; // кнопку "удерживает" дольше чем LONG_PRESS времени


// buttons code
typedef  enum buttons_codes{
	btnNONE=0,
	btnUP,  //1
	btnDOWN,   //2
	btnLEFT,    //3
	btnRIGHT, //4
	btnSELECT, //5

	btnDirectSet // физически такой кнопки нет, используется только "псевдо-кнопками" из сериал что-бы сразу уставноить скорость командой вида =500;
                     // всегда должна быть "последней", используется как "признак окончания" списка кодов
};

typedef void (*CommandHandler)(); // тип "обрабочик команды", функция вида void someFunction();
CommandHandler command_handlers[btnDirectSet]={ // таблица обработчиков коммнад, обработчики должны быть в том же порядке что и buttons_codes
  increase_speed, // UP
  decrease_speed, //DOWN
  decrease_step, // LEFT
  increase_step, // RIGHT
  start_stop, // SELECT
  set_speed // btnDirectSet
};




// global variables
LiquidCrystal lcd(LCD_PINS);



void setup() {
  
#ifdef DBG  
  DBG.begin(SERIAL_SPEED);// 
#endif  

  if(LOAD_STATE_ON_START)loadState(); // если нужно загружаем скорость/шаг из энергонезависимой памяти

  // init the timer1, interrupt every 0.1ms
  Timer1.initialize(100);
  Timer1.attachInterrupt(timerIsr);
  
  // init LCD and custom symbol
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  
  // pins direction
  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_DIR, OUTPUT);
  
  // initial values


  digitalWrite(PIN_DIR, DIRECTION);

  showState(); // показываем текущие состояние на LCD/Serial


}

  
void loop() {
  int   button=getButtonCode(); // получаем код кнопки (с защитой от анти-дребезга, с настоящих кнопок и "псевдо из serial")
  if(button) { //если кнопка нажата - 
    command_handlers[button-1](); // вызываем соотвествующий обработчик по таблице command_handlers[]
    
    showState(); // показываем текущие состояние на LCD/Serial
  }
  monitorAndSaveState(); // следим за текущей скорость/шагом, если они меняются - сохраняем их в EEPROM

}


// *********************** Обработчики прерываний *****************************/
// timer1 interrupt function
// собственно функция которая делает "шаги" степером
void timerIsr() {
  if(!isRun)return; // не запущен, не нужно ничего делать
  
  static volatile int tick_count=0; // сколько раз тикнул таймер от предыдущего степе
  
  tick_count++;
  
  if(tick_count >= ticks) { // пора делать шаг
    
    // make a step
    digitalWrite(PIN_STEP, HIGH);
    digitalWrite(PIN_STEP, LOW);
    
    tick_count = 0; // запустили счетчик заново
  }
}


// ****************************************  Обработчики комманд  *****************************

// increase speed if it's below the max (70)
void increase_speed() {
  ticks=constrain(ticks-tickStep*(isLongPress?LONG_STEP_MULT:1),MIN_STEP_DELAY,MAX_STEP_DELAY);  // уменьшая задержку - увеличиваем скорость
}



// decrease speed if it's above the min (0)
void decrease_speed() {
  ticks=constrain(ticks+tickStep*(isLongPress?LONG_STEP_MULT:1),MIN_STEP_DELAY,MAX_STEP_DELAY);  // увеличивая задержку - уменьшаем скорость
}


void increase_step(){ // увеличивает "шаг изменения" aka "делитель"
  tickStep=constrain(tickStep+(isLongPress?TICKS_STEP_STEP_LONG:TICKS_STEP_STEP),MIN_STEP,MAX_STEP);
}

void decrease_step(){ // уменьшает "шаг изменения"
  tickStep=constrain(tickStep-(isLongPress?TICKS_STEP_STEP_LONG:TICKS_STEP_STEP),MIN_STEP,MAX_STEP);
}


// emergency stop: speed 0
void start_stop() {
  isRun=!isRun;
}

void  set_speed(){
#ifdef DBG  
   unsigned int newSpeed=DBG.parseInt();
   Serial.print("New Speed=");
   Serial.println(newSpeed);
   ticks=constrain(newSpeed,MIN_STEP_DELAY,MAX_STEP_DELAY);
#endif   
}



// ****************************************  /Обработчики комманд *****************************


// ********************** Вывод текущиего состояния **************************************/

// отображает текущие состояние (на экран, в Serial и т.п.)
void showState(){
  updateLCD();
  
  #ifdef DBG
    updateToSerial();
  #endif
}

// update LCD
void updateLCD() {
  
  // print first line:
  
  lcd.setCursor(0,0);
  lcd.print("SD:"); // SD - Step Delay
  lcd.print(ticks);
  lcd.print("    "); // затираем финальные нули от больших чисел, лень высчитывать точное количетсво пробелов
  
  
  
  lcd.setCursor(14,0);
  if(isRun)lcd.print(" "); // запущенно
  else lcd.print("P");
  
  lcd.print(saveRequired?"*":" "); // показываем нужно ли сохранение.
  
  // Вторая строка
  // скорость изменения
  lcd.setCursor(0,1);
  lcd.print("Step:");
  lcd.print(tickStep);
  
  lcd.print("    "); // затираем финальные нули от больших чисел, лень высчитывать точное количетсво пробелов
  

}

#ifdef DBG
void updateToSerial(){
  // Выводим время
  Serial.print("["); Serial.print(millis());Serial.print("] ");
  
  Serial.print("isRun=");Serial.print(isRun);
  Serial.print(", ticks=");Serial.print(ticks);
  Serial.print(", step=");Serial.print(tickStep);
  Serial.print(", saveRequired=");Serial.print(saveRequired);
  
  Serial.println();
}
#endif


/*********************************************************************************************/

/************************  Обработка кнопок ************************************************/


// read buttons connected to a single analog pin
int read_buttons() {
  
 int adc_key_in = analogRead(0);
 
 if (adc_key_in > 1000) return btnNONE;
 if (adc_key_in < 50) return btnRIGHT;
 if (adc_key_in < 195) return btnUP;
 if (adc_key_in < 380) return btnDOWN;
 if (adc_key_in < 555) return btnLEFT;
 if (adc_key_in < 790) return btnSELECT;
 return btnNONE;
}

// объдиняем в себе read_serial_buttons() и read_debounces_buttons() от кого пришел "код кнопки", от того и возвращает
int getButtonCode(){
  int button=read_debounces_buttons();// вначале пытаемся прочитать настоящие кнопки

#ifdef DBG
  if(!button)button=read_serial_buttons(); // если настоящую не нажали, пытаемся прочитать "псевдо-кнопку"
#endif

  return button;
}


// то же самое что и read_buttons() только с защитой от дребезга
int read_debounces_buttons(){
  int buttonResult; // что вернет функция

  
  static int prevButton=btnNONE; // состояние кнопки при прошлом вызове
  static boolean debounce=false;
  static  unsigned long previous_time;
  static unsigned long lastButtonStateChanged;// когда последний раз менялось настоящие состояние кнопки, используется для определения "длинных" нажатий
  
  int realButtonState=read_buttons();

  
  // check if debounce active
  if(debounce) {
    buttonResult = btnNONE;
    if(millis() - previous_time >   DEBOUNCE_TIME) debounce = false;
  } else buttonResult =  realButtonState; // вернем настоящие состояние кнопки
  
  // if a button is pressed, start debounce time
  if(buttonResult) {
    previous_time = millis();
    debounce = true;
  }
  
  
  if(prevButton!=realButtonState){
    lastButtonStateChanged=millis();
  }
  
  isLongPress=(millis()-lastButtonStateChanged>=LONG_PRESS); // выставили глобальную переменную указывающую это "длинное нажатие" или нет
  
  
  prevButton=realButtonState;// сохранили настоящие состояние кнопки
  
  return buttonResult;
}

// аналог read_buttons() только "кнопки" читает и Serial
int read_serial_buttons(){
  static char cmdChars[btnDirectSet]={'u','d','l','r','s','='}; // Up/Down/Left/Righ/Select/=500;
  
  if(Serial.available()){
    char ch=Serial.read(); // читаем пришедший символ
    // ищем его в списке команд и его индекс возвращаем в качестве "кода кнопки"
    for(byte i=0;i<btnDirectSet;i++)if(cmdChars[i]==ch)return i+1;
  }
   return btnNONE; // ничего не нашли
  
}


/******************** Сохранение/запись ticks и tickStep в энергонезависимую память ***********************************************/

// три "помогалки" для чтения/записи int-та в eeprom
void saveInt(int addr, int* value){
  byte* p=(byte*)value;
  for(byte i=0;i<sizeof(int);i++)EEPROM.write(addr+i,*p++);
}

// То же самое что saveInt, только предварительно делает чтение и записывает только если значение действительно отличается. для экономии циклов записи.
void saveIntSmart(int addr,int* value){
  int currentValue=loadInt(addr);
  if(currentValue!=*value)saveInt(addr,value);
}


int loadInt(int addr){
  int result;
  byte* p=(byte*)&result;
  for (byte i = 0; i < sizeof(int); i++)*p++ = EEPROM.read(addr+i);
  return result;
}

// сохраняет текущие ticks и tickStep в eeprom
void saveState(){
  saveIntSmart(ADDR_TICKS,(int *)&ticks);
  saveIntSmart(ADDR_TICKSTEP,&tickStep);
}

void loadState(){
  ticks=loadInt(ADDR_TICKS);
  tickStep=loadInt(ADDR_TICKSTEP);
}

// следит за изменением переменных ticks и tickStep
// если они поменялиь и сохраняют свое значение дольше SAVE_INTERVAL
// записывает их в eeprom
void monitorAndSaveState(){
  
  static int prevTicks=ticks; // инициализируем, что-бы при первом вызове не включался режим сохранения
  static int prevTickStep=tickStep;
  static unsigned long lastChangeTime=0; // когда состояние менялось последний раз
  

  
  if(prevTicks!=ticks ||  prevTickStep!=tickStep){
    saveRequired=true; // выставляем флаг что требуется сохранение
    lastChangeTime=millis(); // и запомнили когда произошло изменение
    showState(); 
  }
  
  
  if(saveRequired && ( (millis()-lastChangeTime)>SAVE_INTERVAL)){ // если сохранение требуется и прошло достаточно времени
     saveState();// сохраняем
     saveRequired=false;// и снимаем флаг "нужно сохранение"
     showState(); // показываем текущие состояние на LCD/Serial
  }
  

  // запоминаем на следующий проход  
  prevTicks=ticks;
  prevTickStep=tickStep;
}

 

jonatandok
Offline
Зарегистрирован: 11.04.2017

Тема уже не актуальна?