roboforum.ru

Технический форум по робототехнике.

Arduino pro mini для управления шаговым двигателем

Как собрать и запрограммировать робота на Arduino(Freeduino, Roboduino, Seeduino ...). Используем Wiring и Processing.

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 17 сен 2015, 19:31

Промежуточный результат.
Код: Выделить всёРазвернуть
/*
Управление шаговиком (драйвер степ-дир) переменным резистором
теперь с кнопками! :)
А на этот раз - с экраном и резистивными кнопками.
Добавил измерение скорости вращения экструдера. Чуть подправил генерацию импульса на шаговик.
Управление преработано. Кнопка Select переключает режим "выбор" и "редактирование" Частота экструдера - в оборотах в минуту.
v 0.6 */

/*
Вывод    Назначение
0        UART
1        UART
2        Внешнее прерывание от оптопары (не менять!)
3       
4       
5        Dir_pin
6        Step_pin     
7        LCD RS
8        LCD RW
9        LCD E
10       LCD DB4
11       LCD DB5
12       LCD DB6
13       LCD DB7
A0       Резистор
A1        Кнопки
A2
A3
A4       
A5       
A6
A7
*/


//#define Razr 1 //признак разработчика. Если пользователь - комментируем или удаляем.

#include <EEPROM.h> //подключаем библиотеку EEPROM

#include <LiquidCrystal.h> // Подключаем стандартную библиотеку LiquidCrystal
// Инициализируем объект-экран, передаём использованные
// для подключения контакты на Arduino в порядке:
#define lcd_RS_pin 7
#define lcd_RW_pin 8
#define lcd_E_pin 9
#define lcd_DB4_pin 10
#define lcd_DB5_pin 11
#define lcd_DB6_pin 12
#define lcd_DB7_pin 13
//                    RS,         RW,        E,         DB4,           DB5,       DB6,          DB7
LiquidCrystal lcd(lcd_RS_pin, lcd_RW_pin, lcd_E_pin, lcd_DB4_pin, lcd_DB5_pin, lcd_DB6_pin, lcd_DB7_pin);


#define Step_pin 6 //вывод Arduino для ноги STEP контроллера (выход)
#define Dir_pin 5 //вывод Arduino для ноги DIR контроллера (выход)
#define Resistor_pin A0 //вывод Arduino резистора 0-5вольт (аналоговый вход)

//Оптодатчик:
#define OptoSensor_pin 2 //вывод Arduino для оптодатчика (вход)
#define ToothSumm 4 //сколько усреднять событий
#define ToothAll 140 // Событий на оборот
float ExtruderConst=(float)60/((float)(ToothAll/ToothSumm)*((float)1/250000)); // Коэффициент тиков :) Для оборотов в минуту делим его на измеренные

volatile unsigned long OptoSensorTicks=0;//Переменная для хранения тиков таймера на 1 вызов (время между зубьями)
volatile byte OptoToothCounter =0; //Указатель на текущтй зуб в массиве
volatile unsigned int OptoToothTimeArray [ToothSumm]; //массив с таймингами зубьев




//********** Кнопки:
#define btnRIGHT  1 //Определяем кнопки, это просто внутренние номера, НЕ ноги-выводы
#define btnUP     2
#define btnDOWN   3
#define btnLEFT   4
#define btnSELECT 5
#define btnNONE   0
//**********
#define btnDEFtime   30 //Значение для антидребезга - клавиша удерживаемая столько времени в миллисекундах считается нажатой.
#define btnPAUSEtime   400 //Значение в миллисекундах паузы между повторами
int lcd_key     = btnNONE;  //Для хранения полученного значеник кнопки
byte keyTemp = btnNONE; //Переменная для хранения временного состояния кнопок
byte keyCONTpress; //Переменная взводится после первого срабатывания клавиши и определяет автоповтор
unsigned long BtnTime; //Хранит время начала нажатия кнопок
byte btnCONTpress; //Переменная взводится после первого срабатывания клавиши и определяет автоповтор
byte btnTemp=btnNONE; //Для хранения номера ранее нажатой кнопки
#if defined(Razr)  //Мне для отладки
    #define ButtonPin A7 //Сюда подключены кнопки
#else
  #define ButtonPin A1 //Сюда подключены кнопки
#endif
//************

/*
//структура под меню
typedef struct
{
     char* nam;
    byte posX;
    byte posY;
} menu;
//typedef struct menu menuEl;

menu menus[3] ={{"Mm1",0,0},
{"Mm2",1,1},
{"Mm3",3,3}};
*/


//Строка1:
byte ControlPos = 0; //Переменная определяет элемент в строке управления. 1 - коэффициент. 0 - частота протяжки

//строка: 0 - резистор, кнопки 1 - кнопки.
byte ControlMode = 0; //Переменная определяет режим управления. 0 - выбор, 1 - редактирование

//Переменная для значения таймера
volatile unsigned char FullTimer0;
//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256;//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256Count; //внутренный счетчик таймера

//значение минимальной частоты
#define LowFreq 10
//значение МАКСИмальной частоты
#define HighFreq 10000
//Переменная для шага чаcтоты на едниницу изменения АЦП
float FreqStep;
int OutFreq = LowFreq; //Переменная для хранения установленной частоты



//Переменная для текущего значения резистора
int sensorValue = 0;
//Переменная для Старого значения резистора
int OLDsensorValue = 0;

void setup() {               
  pinMode(Step_pin,OUTPUT);  // Конфигурим вывод Step_pin как выход
  pinMode(Dir_pin,OUTPUT);  // Конфигурим вывод Dir_pin как выход
  pinMode(Resistor_pin,INPUT);  // Конфигурим вывод Dir_pin как вход

  pinMode(OptoSensor_pin,INPUT);  // Конфигурим вывод OptoSensor_pin как вход
 
  attachInterrupt(0, OptoSensor, CHANGE); // привязываем 0-е прерывание к функции OptoSensor(). На изменение состояния. Да, из-за разницы между шириной самого зуба и межзубного расстояния - бкдкт различия, усредним.
 
  lcd.begin(16,2); //Инициализируем экран
 
 
  //Установим на выходах 0
  digitalWrite(Step_pin, LOW);
  digitalWrite(Dir_pin, LOW);
 
  // Посчитаем "шаг" изменения частоты на единицу изменения резистора
  FreqStep=(float)(HighFreq-LowFreq)/1024;
 
  //Запускает последовательный порт
  Serial.begin(9600);
 
  //Сообщение о запуске программы
  Serial.println("Program started"); 
// Serial.print("FreqStep="); 
// Serial.println(FreqStep); 
 
  //Запускает таймер и получает загружаемое значение таймера.
  //Параметр - желаемая частота в герцах.
 
   FullTimer0=SetupTimer2(OutFreq);
   
  //Установки Таймер2: Делитель частоты /64, режим 0
  //Частота = 16MHz/64 = 250000 герц или
  //Делитель /64 дает нам хороший рабочий диапазон в районе 1 кгц
  //так что сейчас мы просто жестко запрограммируем это.
  TCCR2A = 0;
  TCCR2B = 1<<CS22 | 0<<CS21 | 0<<CS20; //это на 64
  //Подключение прерывания по переполнению Timer2
  TIMSK2 = 1<<TOIE2;

  //Установки Таймер1: Делитель частоты /64, режим 0
  //Частота = 16MHz/64 = 250000 герц или
  //Делитель /64 дает нам хороший рабочий диапазон
  //так что сейчас мы просто жестко запрограммируем это.
  TCCR1A = 0;
  TCCR1B = 1<<CS22 | 0<<CS21 | 0<<CS20; //это на 64
  //Подключение прерывания по переполнению Timer0 (для обнуления скорости)
  TIMSK1 = 1<<TOIE1;
   
 
  //Выводит загружаемое значение таймера
  Serial.print("Timer2 Load:");
  Serial.println(FullTimer0,HEX);
 
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.print("Stepper control"); //выводим на него  строку
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.print(" St  Div  Ext"); //выводим на него  строку
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.cursor(); //Включим курсор
  lcd.blink(); //Пусть мигает
 
  delay(10);
}

void loop() {
  lcd_key=read_LCD_buttons(); //прочитаем нажатую кнопку
//0   4   8   12  |
// St  Div  Ext
//000  00000 00000
 
  if (lcd_key) //Нажата какая-то кнопка
  {
     if (btnSELECT==lcd_key) //Если нажата кнопка Select
       {ControlMode=!ControlMode;} //Меняем режим на другой :)
     if (ControlMode) //Если режим - "редактирование"
       {
         if (btnUP==lcd_key) //Если нажата кнопка "Больше"
           {
                 if (ControlPos==0) //Меняем частоту протяжки шагами
                   {OutFreq+=10;}
                 if (ControlPos==1) //Меняем коэффициент шагами
                   {OutFreq+=10;}
            }
         if (btnDOWN==lcd_key) //Если нажата кнопка "Больше"
           {
                 if (ControlPos==0) //Меняем частоту протяжки шагами
                   {OutFreq-=10;}
                 if (ControlPos==1) //Меняем коэффициент шагами
                   {OutFreq-=10;}
            }
         if (btnRIGHT==lcd_key) //Если нажата кнопка "ВПРАВО"
           {
                 if (ControlPos==0) //Меняем частоту протяжки единицами
                   {OutFreq+=1;}
                 if (ControlPos==1) //Меняем коэффициент единицами
                   {OutFreq+=1;}
            }
         if (btnLEFT==lcd_key) //Если нажата кнопка "ВЛЕВО"
           {
                 if (ControlPos==0) //Меняем частоту протяжки единицами
                   {OutFreq-=1;}
                 if (ControlPos==1) //Меняем коэффициент единицами
                   {OutFreq-=1;}
            }
            SetupTimer2(OutFreq); //устанавливаем таймер
       }
      else //режим "выбор"
       {
          if (btnRIGHT==lcd_key) //нажата кнопка ВПРАВО
            {
              lcd.setCursor(0, 0);
              lcd.print(" ");
              lcd.setCursor(4, 0);
              lcd.print("#");
              ControlPos = 1;
            }
          if (btnLEFT==lcd_key) //нажата кнопка ВЛЕВО   
          {
              lcd.setCursor(4, 0);
              lcd.print(" ");
              lcd.setCursor(0, 0);
              lcd.print("#");
              ControlPos = 0;
          }
       }
  }
       




//    Serial.print("Button=");
//      Serial.println(lcd_key,DEC);
//    if (btnRIGHT==lcd_key) //нажата кнопка ВПРАВО
//    {
//      lcd.setCursor(6, 1);
//      ControlMode = 1;
//    }
//    if (btnLEFT==lcd_key) //нажата кнопка ВЛЕВО   
//    {
//     lcd.setCursor(0, 1);
//      ControlMode = 0;
//    }
//  }





//   if (ControlMode) //Если режим "кнопки"
//     {
//       if (btnUP==lcd_key) //Если нажата кнопка "Больше"
//          #if defined(Razr) //Мне для отладки
//            {OutFreq+=1000;}
//          #else
//            {OutFreq+=1;}
//          #endif
//       if (btnDOWN==lcd_key) //Если нажата кнопка "Меньше"
//          #if defined(Razr) //Мне для отладки
//            {OutFreq-=1000;}
//          #else
//            {OutFreq-=1;}
//          #endif
//       if (lcd_key) //Если вообще что-то нажато
//       {
//         SetupTimer2(OutFreq);
//         lcd.setCursor(0, 0);
//         lcd.print("        ");
//         lcd.setCursor(0, 0);
//         lcd.print(FullTimer0);
//         lcd.setCursor(4, 0);
//         lcd.print(FullTimer256);

         
//         lcd.setCursor(7, 1);
//         lcd.print("     "); //чистим вывод
//         lcd.setCursor(7, 1);
//         lcd.print(OutFreq); //выводим Частоту
//         lcd.setCursor(6, 1);
//       }
//     }
//   else //Если режим резистор (светодиод выключен)
//     {
//        //Тут будем читать занчение
//        sensorValue=analogRead(Resistor_pin); 
//        //Сравним значение со старым, если отличается - пересчитаем установку таймера
//        if (sensorValue!= OLDsensorValue)
//          {  //Serial.println("ReCall"); //debug
//           OLDsensorValue=sensorValue;
//           OutFreq = LowFreq+FreqStep*sensorValue; //Вычислим новое значение частоты
//           lcd.setCursor(1, 1);
//           lcd.print("     "); //Чистим вывод
//           lcd.setCursor(1, 1);
//           lcd.print(OutFreq); //выводим Частоту
//           lcd.setCursor(0, 1);
//           SetupTimer2(OutFreq);
//         }
//     }



//        lcd.setCursor(0, 1);
//        lcd.print(ExtruderConst);
//выведем скорость
    OptoSensorTicks=0;
    //Один тик таймера - это 4 микросекунды (.000004) Зубъев у нас 70, причем реагируем на вход зуба а оптосенсор и на выход. Значит - 140 событий на оборот экструдера.
    //Но
    //Складывая время
    for (int i=0; i<ToothSumm; i++)   {OptoSensorTicks+=OptoToothTimeArray[i];}
        lcd.setCursor(10, 0);
    lcd.print("     ");
    lcd.setCursor(10, 0);
    lcd.print(ExtruderConst/OptoSensorTicks);

    lcd.setCursor(0, 1);
    lcd.print(OutFreq); //выведем частоту

      Serial.print("ExtruderConst=");
      Serial.println(ExtruderConst,DEC);
   
    /*
      Serial.print("sensor=");
      Serial.println(sensorValue,DEC);
      Serial.print("FullTimerCount");
      Serial.println(FullTimerCount);
      Serial.print("FullTimer");
      Serial.println(FullTimer,DEC);
     
      Serial.print("timer=");
      Serial.println(FullTimer0,DEC);
     
      Serial.print("ButtPressed=");
      Serial.println(ButtPressed,DEC);
      Serial.print("OutFreq=");
      Serial.println(OutFreq,DEC);
      Serial.println("***");
      Serial.println("");
   
    Serial.println("***");   
    Serial.println(OptoSensorTicks);
    delay (200);
        */
            delay (200);
}

//Timer1 указатель вектора прерывания по переполнению
ISR(TIMER1_OVF_vect)
{
    OptoToothTimeArray[OptoToothCounter]=0; //присваиваем элементу массива 0
    OptoToothCounter++; //инкрементим счетчик
    if (ToothSumm==OptoToothCounter) {OptoToothCounter=0;} //если счетчик равен количеству элементов массива (а индекс массива начинается с 0 - то обнуляем.
}

//Timer2 указатель вектора прерывания по переполнению
ISR(TIMER2_OVF_vect)
{
  if (FullTimer256Count>1)
  {FullTimer256Count--;}
  else
  {if (FullTimer256Count==1) //предпоследний цикл
      {
       TCNT2+=FullTimer0;
       FullTimer256Count=0;
      }
   {if (FullTimer256Count==0) // Срабатывание
        {
         //Переключение IO-вывода в HIGH
          digitalWrite(Step_pin,HIGH);
         FullTimer256Count=FullTimer256;
         //Переключение IO-вывода в другое состояние.
        // digitalWrite(Step_pin,!digitalRead(Step_pin));
            //Перезагрузка таймера и коррекция по задержке
            if (FullTimer256)
                {TCNT2=0;} //Если холостые циклы есть  - максимальное значение
            else
                {TCNT2+=FullTimer0;}
          //Переключение IO-вывода в LOW
          digitalWrite(Step_pin,LOW);
        }

    }
  }
}


#define TIMER_CLOCK_FREQ 250000.0
//15625 for /1024
//2MHz for /8 prescale from 16MHz

  //Установка Таймера2.
//Конфигурирует 8-битный Таймер2 ATMega168 для выработки прерывания
//с заданной частотой.
//Возвращает начальное значение таймера, которое должно быть загружено в TCNT2
//внутри вашей процедуры ISR.
//Смотри пример использования ниже.
unsigned char SetupTimer2(float timeoutFrequency){
  //Подсчет начального значения таймера
  //Слегка усложним, добавив холостые просчеты
  long ticks = TIMER_CLOCK_FREQ/timeoutFrequency; // тиков счетчика для получения заданной частоты
//          #if defined(Razr)
//             lcd.setCursor(9, 0);
//             lcd.print(ticks);
//          #endif
  FullTimer256=ticks/256; //Вычисляем количество ПОЛНЫХ 256 циклов таймера
  ticks-=FullTimer256*256;
  FullTimer0=(byte)(257.0-(ticks+0.5));  //Вычисляем количество доплнительных тиков таймера
  //257 на самом деле должно быть 256, но я получил лучшие результаты с 257.

  //загружает таймер для первого цикла
  if (FullTimer256)
    {TCNT2=0;} //Если холостые циклы есть  - максимальное значение
  else
      {TCNT2=FullTimer0;}
}



// *****************************
//Чтение кнопок с антидребезгом
// read the buttons
int read_LCD_buttons()
{
int adc_key_in = analogRead(ButtonPin);      // Получаем напряжение с АЦП
byte btnTempTemp = btnNONE; //Переменная для хранения свежесчитанного внутри функции
//digitalWrite (LED_pin,0); //debug
//lcd.setCursor(5, 0); //debug
//lcd.print(adc_key_in); //debug
//delay (400); //debug
//Тут используем millis() для отслеживания нажатия кнопки
// Запоминиаем в переменной BtnTime ВРЕМЯ НАЧАЛА нажатия
// Запоминаем в переменной btnTemp нажатия
// Дефиним время антидребезга #define

if (adc_key_in > 950) //Ничего не нажато
  {
   //BtnTime=0; //Скидываем время нажатия
   btnCONTpress=0;
   return btnNONE; // We make ths the 1st option for speed reasons since it will be the most likely result
  }
else //что-то нажато
   {
      if (adc_key_in < 790) { btnTempTemp=btnSELECT; } //Определяем в переменную ТЕКУЩУЮ нажатую кнопку.
      if (adc_key_in < 555) { btnTempTemp=btnLEFT; }
      if (adc_key_in < 380) { btnTempTemp=btnDOWN; }
      if (adc_key_in < 195) { btnTempTemp=btnUP; }
      if (adc_key_in < 50)  { btnTempTemp=btnRIGHT; }

     //А тут проверим, совпадает ли с btnTemp (была ли нажата ранее)
/*     lcd.setCursor(0, 0); //debug
     lcd.print(btnTempTemp); //debug
     lcd.setCursor(2, 0); //debug
     lcd.print(btnTemp); //debug
     lcd.setCursor(1, 1); //debug
     lcd.print(millis()); //debug
     lcd.setCursor(12, 1); //debug
     digitalWrite (LED_pin,1); //debug
     delay (100);
     lcd.print("    "); //debug
     lcd.setCursor(12, 1); //debug
     lcd.print(adc_key_in); //debug
*/
     if (btnTempTemp==btnTemp) //Кнопка уже БЫЛА нажата
     {
        if (btnCONTpress) //Повторные установки клавиши
          {
            if ((millis()-BtnTime) > btnPAUSEtime)
            {
              BtnTime=millis(); // Ставим стартово время для автоповтора
              return btnTemp;
            }
          } 
        else //Первая установка клавиши
           {
              if ((millis()-BtnTime) > btnDEFtime)
              {
                btnCONTpress=1;
                BtnTime=millis(); // Ставим стартово время для автоповтора
                return btnTemp;
              }
           }
     }
     else //Раньше было другое состояние
     {
//       digitalWrite (LED_pin,1); //debug
//       delay (100);
       btnCONTpress=0;
       BtnTime=millis(); //Устанавливаем ТЕКУЩЕЕ время в переменную BtnTime
       btnTemp=btnTempTemp; //устанавливаем кнопку во временную переменную.
     }
   }
return btnNONE;   // Если ничего не сработало, то ничего не возвращаем
}
//***********************************

void OptoSensor() // функция обработки прерывания. Меняет глобальные прееменные
{//Сработал - значит считаем тики. F njxytt - echtlyztv pf
  OptoToothTimeArray[OptoToothCounter]=TCNT1; //присваиваем элементу массива текущее значение таймера
  TCNT1 = 0; //сбрасываем таймер
  OptoToothCounter++; //инкрементим счетчик
  if (ToothSumm==OptoToothCounter) {OptoToothCounter=0;} //если счетчик равен количеству элементов массива (а индекс массива начинается с 0 - то обнуляем.
}


Добавлено спустя 50 секунд:
Недопилено! коэффициент пока не работает.
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 17 сен 2015, 22:07

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

Добавлено спустя 2 часа 7 минут 11 секунд:
Всё загрузил. Работает вроде. В смысле включил, поднял частоту мотора (изначально 10 оочень медленно, но пока экструдер выводим на нужную температуру, то пойдёт)выбрав курсором "влево" и селект кажись (частота ШД заметно меняется только до 100 и после 800). На резистор не реагирует как куда не нажимал. Что значит СТ и ДИВ? Инструкцию похоже пора начинать писать ))
На изменения в оптодатчике тут же меняется показание на дисплее (пока нет движения там просто "ИНФ" с какой-то цифрой), но на обороты шаговика нет реакции.
Вот коротко и всё.

п.с. В моём понимании принцип должен быть таким (просто напоминаю хотелку): включаем экструдер, можно одновременно уже крутить ШД протяжки. Через пару минут "колбаса" дотянется до пластика, заводим в ролики и, догоняя скорость/температуру экструдера и скорость ШД протяжки, доведя до нужного диаметра и скорости работы (не обязательно же на максимуме пытаться) как бы "фиксируем" параметры, отдавая контроль за экструдером уже ардуине, и она подгоняет скорость протяжки уже основываясь на работе экструдера.
Надеюсь понятно пояснил.
Конечно, при наличии цифрового штангеля/микрометра это в механических манипуляциях было бы проще, но для программирования скорее всего сложно. Вроде и есть подобные системы, но чтобы их собрать, надо заново всё покупать.. и ардуины другие и контроллеры ((
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 18 сен 2015, 10:39

Сейчас допишу коэффициент - вот этот самый Div.
Для того чтоб штангель подключить - той же ардуины вполне хватит.
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 18 сен 2015, 11:00

И инструкцию куда что! :)
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 18 сен 2015, 19:08

Схема не меняется.
Вот код, надо б получить похожие на рабочие значения: Частоту экструдера (в верхнем правом углу, в оборотах в минуту) Частоту шаговика (пока в герцах, в нижней строке под "St")
Идеология такая: После включения символ "#" обозначает выбранную позицию. Кнопки влево и вправо позволяют выбирать другую.
Нажатие кнопки Select переключает выбранную позицию в режим редактирования. При этом кнопками вверх-вниз меняются единицы, а влево-вправо - десятые значения.
Повторное нажатие Select - возвращает к выбору.
Код: Выделить всёРазвернуть
/*
Управление шаговиком (драйвер степ-дир) переменным резистором
теперь с кнопками! :)
А на этот раз - с экраном и резистивными кнопками.
Добавил измерение скорости вращения экструдера. Чуть подправил генерацию импульса на шаговик.
Управление преработано. Кнопка Select переключает режим "выбор" и "редактирование" Частота экструдера - в оборотах в минуту.
Добавлен коэффициент
v 0.6a */

/*
Вывод    Назначение
0        UART
1        UART
2        Внешнее прерывание от оптопары (не менять!)
3       
4       
5        Dir_pin
6        Step_pin     
7        LCD RS
8        LCD RW
9        LCD E
10       LCD DB4
11       LCD DB5
12       LCD DB6
13       LCD DB7
A0       Резистор
A1        Кнопки
A2
A3
A4       
A5       
A6
A7
*/


//#define Razr 1 //признак разработчика. Если пользователь - комментируем или удаляем.

#include <EEPROM.h> //подключаем библиотеку EEPROM

#include <LiquidCrystal.h> // Подключаем стандартную библиотеку LiquidCrystal
// Инициализируем объект-экран, передаём использованные
// для подключения контакты на Arduino в порядке:
#define lcd_RS_pin 7
#define lcd_RW_pin 8
#define lcd_E_pin 9
#define lcd_DB4_pin 10
#define lcd_DB5_pin 11
#define lcd_DB6_pin 12
#define lcd_DB7_pin 13
//                    RS,         RW,        E,         DB4,           DB5,       DB6,          DB7
LiquidCrystal lcd(lcd_RS_pin, lcd_RW_pin, lcd_E_pin, lcd_DB4_pin, lcd_DB5_pin, lcd_DB6_pin, lcd_DB7_pin);


#define Step_pin 6 //вывод Arduino для ноги STEP контроллера (выход)
#define Dir_pin 5 //вывод Arduino для ноги DIR контроллера (выход)
#define Resistor_pin A0 //вывод Arduino резистора 0-5вольт (аналоговый вход)

//Оптодатчик:
#define OptoSensor_pin 2 //вывод Arduino для оптодатчика (вход)
#define ToothSumm 4 //сколько усреднять событий
#define ToothAll 140 // Событий на оборот
float ExtruderConst=(float)60/((float)(ToothAll/ToothSumm)*((float)1/250000)); // Коэффициент тиков :) Для оборотов в минуту делим его на измеренные
float DivK =1; //Коэффициент для соотношения скорости шаговика к скорости экструдера

volatile unsigned long OptoSensorTicks=0;//Переменная для хранения тиков таймера на 1 вызов (время между зубьями)
volatile byte OptoToothCounter =0; //Указатель на текущтй зуб в массиве
volatile unsigned int OptoToothTimeArray [ToothSumm]; //массив с таймингами зубьев




//********** Кнопки:
#define btnRIGHT  1 //Определяем кнопки, это просто внутренние номера, НЕ ноги-выводы
#define btnUP     2
#define btnDOWN   3
#define btnLEFT   4
#define btnSELECT 5
#define btnNONE   0
//**********
#define btnDEFtime   30 //Значение для антидребезга - клавиша удерживаемая столько времени в миллисекундах считается нажатой.
#define btnPAUSEtime   400 //Значение в миллисекундах паузы между повторами
int lcd_key     = btnNONE;  //Для хранения полученного значеник кнопки
byte keyTemp = btnNONE; //Переменная для хранения временного состояния кнопок
byte keyCONTpress; //Переменная взводится после первого срабатывания клавиши и определяет автоповтор
unsigned long BtnTime; //Хранит время начала нажатия кнопок
byte btnCONTpress; //Переменная взводится после первого срабатывания клавиши и определяет автоповтор
byte btnTemp=btnNONE; //Для хранения номера ранее нажатой кнопки
#if defined(Razr)  //Мне для отладки
    #define ButtonPin A7 //Сюда подключены кнопки
#else
  #define ButtonPin A1 //Сюда подключены кнопки
#endif
//************

/*
//структура под меню
typedef struct
{
     char* nam;
    byte posX;
    byte posY;
} menu;
//typedef struct menu menuEl;

menu menus[3] ={{"Mm1",0,0},
{"Mm2",1,1},
{"Mm3",3,3}};
*/



byte ControlPos = 0; //Переменная определяет элемент в строке управления. 1 - коэффициент. 0 - частота протяжки
byte ControlMode = 0; //Переменная определяет режим управления. 0 - выбор, 1 - редактирование
int ScreenPass =100; //Для пропуска циклов, чтоб экран не мерцал.

//Переменная для значения таймера
volatile unsigned char FullTimer0;
//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256;//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256Count; //внутренный счетчик таймера

//значение минимальной частоты
#define LowFreq 10
//значение МАКСИмальной частоты
#define HighFreq 1500
//Переменная для шага чаcтоты на едниницу изменения АЦП
// float FreqStep;
float OutFreq = LowFreq; //Переменная для хранения установленной частоты


/*
//Переменная для текущего значения резистора
int sensorValue = 0;
//Переменная для Старого значения резистора
int OLDsensorValue = 0;
*/

void setup() {               
  pinMode(Step_pin,OUTPUT);  // Конфигурим вывод Step_pin как выход
  pinMode(Dir_pin,OUTPUT);  // Конфигурим вывод Dir_pin как выход
//  pinMode(Resistor_pin,INPUT);  // Конфигурим вывод Dir_pin как вход

  pinMode(OptoSensor_pin,INPUT);  // Конфигурим вывод OptoSensor_pin как вход
  attachInterrupt(0, OptoSensor, CHANGE); // привязываем 0-е прерывание к функции OptoSensor(). На изменение состояния. Да, из-за разницы между шириной самого зуба и межзубного расстояния - будут различия, усредним.
 
  lcd.begin(16,2); //Инициализируем экран
 
 
  //Установим на выходах 0
  digitalWrite(Step_pin, LOW);
  digitalWrite(Dir_pin, LOW);
 
//  // Посчитаем "шаг" изменения частоты на единицу изменения резистора
//  FreqStep=(float)(HighFreq-LowFreq)/1024;
 
  //Запускает последовательный порт
  Serial.begin(9600);
 
  //Сообщение о запуске программы
  Serial.println("Program started"); 
// Serial.print("FreqStep="); 
// Serial.println(FreqStep); 
 
  //Запускает таймер и получает загружаемое значение таймера.
  //Параметр - желаемая частота в герцах.
 
   FullTimer0=SetupTimer2(OutFreq);
   
  //Установки Таймер2: Делитель частоты /64, режим 0
  //Частота = 16MHz/64 = 250000 герц или
  //Делитель /64 дает нам хороший рабочий диапазон в районе 1 кгц
  //так что сейчас мы просто жестко запрограммируем это.
  TCCR2A = 0;
  TCCR2B = 1<<CS22 | 0<<CS21 | 0<<CS20; //это на 64
  //Подключение прерывания по переполнению Timer2
  TIMSK2 = 1<<TOIE2;

  //Установки Таймер1: Делитель частоты /64, режим 0
  //Частота = 16MHz/64 = 250000 герц или
  //Делитель /64 дает нам хороший рабочий диапазон
  //так что сейчас мы просто жестко запрограммируем это.
  TCCR1A = 0;
  TCCR1B = 1<<CS22 | 0<<CS21 | 0<<CS20; //это на 64
  //Подключение прерывания по переполнению Timer0 (для обнуления скорости)
  TIMSK1 = 1<<TOIE1;
   
 
  //Выводит загружаемое значение таймера
  Serial.print("Timer2 Load:");
  Serial.println(FullTimer0,HEX);
 
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.print("Stepper control"); //выводим на него  строку
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.print("#St  Div  Ext"); //выводим на него  строку
  lcd.setCursor(0, 0); //устанавливаем курсор
//  lcd.cursor(); //Включим курсор
//  lcd.blink(); //Пусть мигает
 
  delay(10);
}

void loop() {
OptoSensorTicks=0;
//Один тик таймера - это 4 микросекунды (.000004) Зубъев у нас 70, причем реагируем на вход зуба а оптосенсор и на выход. Значит - 140 событий на оборот экструдера.
//Складывая время
for (int i=0; i<ToothSumm; i++)   {OptoSensorTicks+=OptoToothTimeArray[i];}
ScreenPass++; //Инкремент счетчика
if (ScreenPass>1000)
{
  ScreenPass=0; //Обнуляем счетчик
 
        lcd_key=read_LCD_buttons(); //прочитаем нажатую кнопку
      //0   4   8   12  |
      // St  Div  Ext
      //000  00000 00000
       
        if (lcd_key) //Нажата какая-то кнопка
        {
           if (btnSELECT==lcd_key) //Если нажата кнопка Select
             {
               ControlMode=!ControlMode;
               if (ControlMode)
               {
                 if (ControlPos==0)
                     {
                       lcd.setCursor(0, 0);
                        lcd.print("*");
                        lcd.setCursor(4, 0);
                        lcd.print(" ");
                     }
                 if (ControlPos==1)
                     {
                        lcd.setCursor(0, 0);
                        lcd.print(" ");
                        lcd.setCursor(4, 0);
                        lcd.print("*");
                     }
                 }
                 else
                 {
                   lcd.setCursor(0, 0);
                   lcd.print(" ");
                   lcd.setCursor(4, 0);
                   lcd.print(" ");
                   if (ControlPos==1){lcd.setCursor(4, 0); lcd.print("#"); }
                   if (ControlPos==0){lcd.setCursor(0, 0); lcd.print("#"); }
                 }
              } //Меняем режим на другой :)
           if (ControlMode) //Если режим - "редактирование"
             {
               if (btnUP==lcd_key) //Если нажата кнопка "Больше"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки шагами
                         {
                           OutFreq+=10;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                          }
                       if (ControlPos==1) //Меняем коэффициент шагами
                         {DivK+=1;}
                  }
               if (btnDOWN==lcd_key) //Если нажата кнопка "Меньше"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки шагами
                         {
                           OutFreq-=10;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                          }
                       if (ControlPos==1) //Меняем коэффициент шагами
                         {DivK-=1;}
                  }
               if (btnRIGHT==lcd_key) //Если нажата кнопка "ВПРАВО"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки единицами
                         {
                           OutFreq+=1;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                          }
                       if (ControlPos==1) //Меняем коэффициент единицами
                         {DivK+=.01;}
                  }
               if (btnLEFT==lcd_key) //Если нажата кнопка "ВЛЕВО"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки единицами
                         {
                           OutFreq-=1;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                         }
                       if (ControlPos==1) //Меняем коэффициент единицами
                         {DivK-=.01;}
                  }
             }
            else //режим "выбор"
             {
                if (btnRIGHT==lcd_key) //нажата кнопка ВПРАВО
                  {
                    lcd.setCursor(0, 0);
                    lcd.print(" ");
                    lcd.setCursor(4, 0);
                    lcd.print("#");
                    ControlPos = 1;
                  }
                if (btnLEFT==lcd_key) //нажата кнопка ВЛЕВО   
                {
                    lcd.setCursor(4, 0);
                    lcd.print(" ");
                    lcd.setCursor(0, 0);
                    lcd.print("#");
                    ControlPos = 0;
                }
             }
        }
             
             
      //Теперь в зависимости от режима нужно...
      //Либо считать частоту шаговика от коэффициента при ControlMode==1 и ControlPos==1
      //Либо вычислять коэффициент при ControlMode==1 и ControlPos==0
      //Либо вычислять частоту шаговика от коэффициента при ControlMode==0
      //Короче, только при ControlMode==1 и ControlPos==0 вычислять коэффициент. В остальных случаях - частоту.
       if ((ControlMode==1) && (ControlPos==0)) //Если режим - редактирование, изменение шаговика
        {
         DivK= (float)OutFreq* ((float)100/OptoSensorTicks);  //Вычислим коэффициент
         }
      else
        {
          OutFreq=(float)DivK/(float)(100/OptoSensorTicks);
          if (OutFreq>HighFreq){OutFreq=HighFreq;}
          if (OutFreq<LowFreq){OutFreq=LowFreq;}
          SetupTimer2(OutFreq); //устанавливаем таймер
        }
 
      //Выведем коэффициент
         lcd.setCursor(10, 1);
         lcd.print(DivK);
     
      //выведем скорость
          lcd.setCursor(10, 0);
          lcd.print("     ");
          lcd.setCursor(10, 0);
          lcd.print(ExtruderConst/OptoSensorTicks);
         
      //    lcd.setCursor(9, 1);
      //    lcd.print("     ");
      //    lcd.setCursor(9, 1);
      //    lcd.print(OptoSensorTicks);
     
          lcd.setCursor(0, 1);
          lcd.print(OutFreq); //выведем частоту
     
       
          /*
            Serial.print("sensor=");
            Serial.println(sensorValue,DEC);
            Serial.print("FullTimerCount");
            Serial.println(FullTimerCount);
            Serial.print("FullTimer");
            Serial.println(FullTimer,DEC);
           
            Serial.print("timer=");
            Serial.println(FullTimer0,DEC);
           
            Serial.print("ButtPressed=");
            Serial.println(ButtPressed,DEC);
            Serial.print("OutFreq=");
            Serial.println(OutFreq,DEC);
            Serial.println("***");
            Serial.println("");
         
          Serial.println("***");   
          Serial.println(OptoSensorTicks);
          delay (200);
              */
}

}

//Timer1 указатель вектора прерывания по переполнению
ISR(TIMER1_OVF_vect)
{
    OptoToothTimeArray[OptoToothCounter]=0; //присваиваем элементу массива 0
    OptoToothCounter++; //инкрементим счетчик
    if (ToothSumm==OptoToothCounter) {OptoToothCounter=0;} //если счетчик равен количеству элементов массива (а индекс массива начинается с 0 - то обнуляем.
}

//Timer2 указатель вектора прерывания по переполнению
ISR(TIMER2_OVF_vect)
{
  if (FullTimer256Count>1)
  {FullTimer256Count--;}
  else
  {if (FullTimer256Count==1) //предпоследний цикл
      {
       TCNT2+=FullTimer0;
       FullTimer256Count=0;
      }
   {if (FullTimer256Count==0) // Срабатывание
        {
         //Переключение IO-вывода в HIGH
          digitalWrite(Step_pin,HIGH);
         FullTimer256Count=FullTimer256;
         //Переключение IO-вывода в другое состояние.
        // digitalWrite(Step_pin,!digitalRead(Step_pin));
            //Перезагрузка таймера и коррекция по задержке
            if (FullTimer256)
                {TCNT2=0;} //Если холостые циклы есть  - максимальное значение
            else
                {TCNT2+=FullTimer0;}
          //Переключение IO-вывода в LOW
          digitalWrite(Step_pin,LOW);
        }

    }
  }
}


#define TIMER_CLOCK_FREQ 250000.0
//15625 for /1024
//2MHz for /8 prescale from 16MHz

  //Установка Таймера2.
//Конфигурирует 8-битный Таймер2 ATMega168 для выработки прерывания
//с заданной частотой.
//Возвращает начальное значение таймера, которое должно быть загружено в TCNT2
//внутри вашей процедуры ISR.
//Смотри пример использования ниже.
unsigned char SetupTimer2(float timeoutFrequency){
  //Подсчет начального значения таймера
  //Слегка усложним, добавив холостые просчеты
  long ticks = TIMER_CLOCK_FREQ/timeoutFrequency; // тиков счетчика для получения заданной частоты
//          #if defined(Razr)
//             lcd.setCursor(9, 0);
//             lcd.print(ticks);
//          #endif
  FullTimer256=ticks/256; //Вычисляем количество ПОЛНЫХ 256 циклов таймера
  ticks-=FullTimer256*256;
  FullTimer0=(byte)(257.0-(ticks+0.5));  //Вычисляем количество доплнительных тиков таймера
  //257 на самом деле должно быть 256, но я получил лучшие результаты с 257.

  //загружает таймер для первого цикла
  if (FullTimer256)
    {TCNT2=0;} //Если холостые циклы есть  - максимальное значение
  else
      {TCNT2=FullTimer0;}
}



// *****************************
//Чтение кнопок с антидребезгом
// read the buttons
int read_LCD_buttons()
{
int adc_key_in = analogRead(ButtonPin);      // Получаем напряжение с АЦП
byte btnTempTemp = btnNONE; //Переменная для хранения свежесчитанного внутри функции
//digitalWrite (LED_pin,0); //debug
//lcd.setCursor(5, 0); //debug
//lcd.print(adc_key_in); //debug
//delay (400); //debug
//Тут используем millis() для отслеживания нажатия кнопки
// Запоминиаем в переменной BtnTime ВРЕМЯ НАЧАЛА нажатия
// Запоминаем в переменной btnTemp нажатия
// Дефиним время антидребезга #define

if (adc_key_in > 950) //Ничего не нажато
  {
   //BtnTime=0; //Скидываем время нажатия
   btnCONTpress=0;
   return btnNONE; // We make ths the 1st option for speed reasons since it will be the most likely result
  }
else //что-то нажато
   {
      if (adc_key_in < 790) { btnTempTemp=btnSELECT; } //Определяем в переменную ТЕКУЩУЮ нажатую кнопку.
      if (adc_key_in < 555) { btnTempTemp=btnLEFT; }
      if (adc_key_in < 380) { btnTempTemp=btnDOWN; }
      if (adc_key_in < 195) { btnTempTemp=btnUP; }
      if (adc_key_in < 50)  { btnTempTemp=btnRIGHT; }

     //А тут проверим, совпадает ли с btnTemp (была ли нажата ранее)
/*     lcd.setCursor(0, 0); //debug
     lcd.print(btnTempTemp); //debug
     lcd.setCursor(2, 0); //debug
     lcd.print(btnTemp); //debug
     lcd.setCursor(1, 1); //debug
     lcd.print(millis()); //debug
     lcd.setCursor(12, 1); //debug
     digitalWrite (LED_pin,1); //debug
     delay (100);
     lcd.print("    "); //debug
     lcd.setCursor(12, 1); //debug
     lcd.print(adc_key_in); //debug
*/
     if (btnTempTemp==btnTemp) //Кнопка уже БЫЛА нажата
     {
        if (btnCONTpress) //Повторные установки клавиши
          {
            if ((millis()-BtnTime) > btnPAUSEtime)
            {
              BtnTime=millis(); // Ставим стартово время для автоповтора
              return btnTemp;
            }
          } 
        else //Первая установка клавиши
           {
              if ((millis()-BtnTime) > btnDEFtime)
              {
                btnCONTpress=1;
                BtnTime=millis(); // Ставим стартово время для автоповтора
                return btnTemp;
              }
           }
     }
     else //Раньше было другое состояние
     {
//       digitalWrite (LED_pin,1); //debug
//       delay (100);
       btnCONTpress=0;
       BtnTime=millis(); //Устанавливаем ТЕКУЩЕЕ время в переменную BtnTime
       btnTemp=btnTempTemp; //устанавливаем кнопку во временную переменную.
     }
   }
return btnNONE;   // Если ничего не сработало, то ничего не возвращаем
}
//***********************************

void OptoSensor() // функция обработки прерывания. Меняет глобальные прееменные
{//Сработал - значит считаем тики. F njxytt - echtlyztv pf
  OptoToothTimeArray[OptoToothCounter]=TCNT1; //присваиваем элементу массива текущее значение таймера
  TCNT1 = 0; //сбрасываем таймер
  OptoToothCounter++; //инкрементим счетчик
  if (ToothSumm==OptoToothCounter) {OptoToothCounter=0;} //если счетчик равен количеству элементов массива (а индекс массива начинается с 0 - то обнуляем.
}


Добавлено спустя 4 минуты 18 секунд:
Хотя да, чего-то с частотой... Непропорционально меняется.

Добавлено спустя 25 минут 18 секунд:
Ищу причину.
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 18 сен 2015, 20:23

А такой вопрос... а экструдером мы как управлять будем? У меня сейчас типа шимконтроллера на 12В китайческого.
И еще бы уточнить, что означает какая позиция. Чтобы не бездумно методом тыка нажимать :oops: Мне, как несоздателю, сложно быстро уловить ход мыслей гуру :friends:

Добавлено спустя 41 минуту 32 секунды:
Блин... только что дошло... а нафига нам управлять двигателем экструдера, если мы собрались управлять двигателем протяжки :ROFL: Совсем уже крышу сорвало из-за обилия информации.

Добавлено спустя 20 минут 22 секунды:
Залил, попробовал. Пока понял только что верх справа - частота оптодатчика, снизу справа пока нечто не понятное (может что на что делим?). Частота слева почему-то стала 1500 и снижая/повышая кнопками, нажав затем селект, она снова возвращается на 1500. Если сигнал на опто не идёт некоторое время, то ШД останавливается полностью. Почти. Еле тюлюпается вроде и слева частота 10, ну а справа inf0.

Прошу прощения за подобные пояснения.. "что вижу то пою" ))

Добавлено спустя 4 минуты 29 секунд:
А сейчас не останавливался.. что-то понажимал наверное.. Так и крутит на 1500.
Я к чему - как бы не менялось показание оптодатчика, ШД на это вроде бы не реагирует. И так же пока не реагирует на переменный резистор.
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 19 сен 2015, 13:59

Работа возможна в трёх режимах.
Первый - автомат. Оба символа # скорость шаговика зависит от скорости экструдера.
Второй - изменение частоты шаговика. Левый символ - * при этом коэффициент рассчитывается динамически.
Третий - изменение коэффициента. Правый символ - *.

Проверять надо при работающем оптодатчике.
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 19 сен 2015, 15:31

Так надо сначала выйти на рабочий режим, доведя скорость ШД до нужной, затем "передать управление" ардуине.
На резистор не реагирует у меня только или пока эта функция отключена?

Проверял не на шестерне экструдера, а быстро прерывая оптодатчик куском пластика вручную.
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 19 сен 2015, 18:22

Да, в режиме установки частоты ШД - коэффициент и вычисляется.

Добавлено спустя 1 минуту 2 секунды:
Резистор отключен. Вообще планировал запоминание настроек.
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 19 сен 2015, 18:34

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

А режимы 1,2,3 как должны реагировать и на что? Первый автомат... как функциклировать должен? Ну второй понятно, меняем частоту шаговика увеличивая тем самым скорость вращения, а третий как и на что должен влиять?
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 19 сен 2015, 23:07

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

Добавлено спустя 1 минуту 20 секунд:
Первый режим - скорость шаговика=скорость экструдера*коэффициент.
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 20 сен 2015, 00:37

В общем понял. Завтра попробую собрать на экструдере. Всё равно надо пластик попробовать чуть сделать, а то печатать нечем.
Немного непонятно только как мне уменьшить вращение ШД (в смысле не вообще, про нажатия кнопок я понял) а как зафиксировать это значение, т.к. когда я на столе баловался с оптодатчиком и схемой, то по нажатию на селект, я возвращался автоматом на 1500 вращения сколько бы я кнопками не накрутил, а если в режиме 3 убирал до минимуму коэффициент (или как правильно), то ШД останавливался и показывалась частота 10.
ВопЧем надо пробовать в реальных боевых действиях )
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 20 сен 2015, 16:59

х.з. что случилось, но перенёс всё к экструдеру, установил оптопару, включил и... не меняются показания оптопары, т.е. сигнала нет. Отвёрточкой потыкал на обратной стороне платы (оптопары, где резисторы паял) показания на дисплее зашевелились, но не более того.
Ну и так же, на слух нет никаких изменений во вращении ШД при изменении частоты, выдаваемой с оптопары.
Разве что как-то получалось так, что если перестали меняться показания оптопары на какой-то промежуток времени, то ШД останавливался полностью (на дисплее слева 10), а если импульс появлялся, то частота ШД тут же становилась 1500.
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Re: Arduino pro mini для управления шаговым двигателем

Сообщение RootAdmin » 21 сен 2015, 15:27

Чуть отладил.
Теперь обороты шаговика и экструдера в оборотах в секунду.
Код: Выделить всёРазвернуть
/*
Управление шаговиком (драйвер степ-дир) переменным резистором
теперь с кнопками! :)
А на этот раз - с экраном и резистивными кнопками.
Добавил измерение скорости вращения экструдера. Чуть подправил генерацию импульса на шаговик.
Управление преработано. Кнопка Select переключает режим "выбор" и "редактирование" Частота экструдера - в оборотах в минуту.
Добавлен коэффициент
v 0.6a */

/*
Вывод    Назначение
0        UART
1        UART
2        Внешнее прерывание от оптопары (не менять!)
3       
4       
5        Dir_pin
6        Step_pin     
7        LCD RS
8        LCD RW
9        LCD E
10       LCD DB4
11       LCD DB5
12       LCD DB6
13       LCD DB7
A0       Резистор
A1        Кнопки
A2
A3
A4       
A5       
A6
A7
*/



#include <EEPROM.h> //подключаем библиотеку EEPROM

#include <LiquidCrystal.h> // Подключаем стандартную библиотеку LiquidCrystal
// Инициализируем объект-экран, передаём использованные
// для подключения контакты на Arduino в порядке:
#define lcd_RS_pin 7
#define lcd_RW_pin 8
#define lcd_E_pin 9
#define lcd_DB4_pin 10
#define lcd_DB5_pin 11
#define lcd_DB6_pin 12
#define lcd_DB7_pin 13
//                    RS,         RW,        E,         DB4,           DB5,       DB6,          DB7
LiquidCrystal lcd(lcd_RS_pin, lcd_RW_pin, lcd_E_pin, lcd_DB4_pin, lcd_DB5_pin, lcd_DB6_pin, lcd_DB7_pin);


#define Step_pin 6 //вывод Arduino для ноги STEP контроллера (выход)
#define Dir_pin 5 //вывод Arduino для ноги DIR контроллера (выход)
#define Resistor_pin A0 //вывод Arduino резистора 0-5вольт (аналоговый вход)

//Оптодатчик:
#define OptoSensor_pin 2 //вывод Arduino для оптодатчика (вход)
#define ToothSumm 4 //сколько усреднять событий
#define ToothAll 140 // Событий на оборот
float ExtruderConst=(float)1/((float)(ToothAll/ToothSumm)*((float)1/250000)); // Коэффициент тиков :) Для оборотов в СЕКУНДУ делим его на измеренные
float DivK =35; //Коэффициент для соотношения скорости шаговика к скорости экструдера

volatile unsigned long OptoSensorTicks=0;//Переменная для хранения тиков таймера на 1 вызов (время между зубьями)
volatile byte OptoToothCounter =0; //Указатель на текущтй зуб в массиве
volatile unsigned int OptoToothTimeArray [ToothSumm]; //массив с таймингами зубьев




//********** Кнопки:
#define btnRIGHT  1 //Определяем кнопки, это просто внутренние номера, НЕ ноги-выводы
#define btnUP     2
#define btnDOWN   3
#define btnLEFT   4
#define btnSELECT 5
#define btnNONE   0
//**********
#define btnDEFtime   30 //Значение для антидребезга - клавиша удерживаемая столько времени в миллисекундах считается нажатой.
#define btnPAUSEtime   400 //Значение в миллисекундах паузы между повторами
int lcd_key     = btnNONE;  //Для хранения полученного значеник кнопки
byte keyTemp = btnNONE; //Переменная для хранения временного состояния кнопок
byte keyCONTpress; //Переменная взводится после первого срабатывания клавиши и определяет автоповтор
unsigned long BtnTime; //Хранит время начала нажатия кнопок
byte btnCONTpress; //Переменная взводится после первого срабатывания клавиши и определяет автоповтор
byte btnTemp=btnNONE; //Для хранения номера ранее нажатой кнопки
#if defined(Razr)  //Мне для отладки
    #define ButtonPin A7 //Сюда подключены кнопки
#else
  #define ButtonPin A1 //Сюда подключены кнопки
#endif
//************

/*
//структура под меню
typedef struct
{
     char* nam;
    byte posX;
    byte posY;
} menu;
//typedef struct menu menuEl;

menu menus[3] ={{"Mm1",0,0},
{"Mm2",1,1},
{"Mm3",3,3}};
*/



byte ControlPos = 0; //Переменная определяет элемент в строке управления. 1 - коэффициент. 0 - частота протяжки
byte ControlMode = 0; //Переменная определяет режим управления. 0 - выбор, 1 - редактирование
int ScreenPass =100; //Для пропуска циклов, чтоб экран не мерцал.


volatile unsigned char FullTimer0; //Переменная для остатка значения таймера
volatile unsigned int FullTimer256;//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256Count; //внутренный счетчик таймера


//значение минимальной частоты
#define LowFreq 10
//значение МАКСИмальной частоты
#define HighFreq 2000
//Переменная для шага чаcтоты на едниницу изменения АЦП
// float FreqStep;
float OutFreq = LowFreq; //Переменная для хранения установленной частоты


/*
//Переменная для текущего значения резистора
int sensorValue = 0;
//Переменная для Старого значения резистора
int OLDsensorValue = 0;
*/

void setup() {               
  pinMode(Step_pin,OUTPUT);  // Конфигурим вывод Step_pin как выход
  pinMode(Dir_pin,OUTPUT);  // Конфигурим вывод Dir_pin как выход
//  pinMode(Resistor_pin,INPUT);  // Конфигурим вывод Dir_pin как вход

  pinMode(OptoSensor_pin,INPUT);  // Конфигурим вывод OptoSensor_pin как вход
  attachInterrupt(0, OptoSensor, CHANGE); // привязываем 0-е прерывание к функции OptoSensor(). На изменение состояния. Да, из-за разницы между шириной самого зуба и межзубного расстояния - будут различия, усредним.
 
  lcd.begin(16,2); //Инициализируем экран
 
 
  //Установим на выходах 0
  digitalWrite(Step_pin, LOW);
  digitalWrite(Dir_pin, LOW);
 
//  // Посчитаем "шаг" изменения частоты на единицу изменения резистора
//  FreqStep=(float)(HighFreq-LowFreq)/1024;
 
  //Запускает последовательный порт
  Serial.begin(9600);
 
  //Сообщение о запуске программы
  Serial.println("Program started"); 
// Serial.print("FreqStep="); 
// Serial.println(FreqStep); 
 
  //Запускает таймер и получает загружаемое значение таймера.
  //Параметр - желаемая частота в герцах.
 
   FullTimer0=SetupTimer2(OutFreq);
   
  //Установки Таймер2: Делитель частоты /64, режим 0
  //Частота = 16MHz/32 = 500000 герц или
  //Делитель /32 дает нам лучший рабочий диапазон в районе 1 кгц
  //так что сейчас мы просто жестко запрограммируем это.
  TCCR2A = 0;
  TCCR2B = 0<<CS22 | 1<<CS21 | 1<<CS20; //это на 32
  //TCCR2B = 1<<CS22 | 0<<CS21 | 0<<CS20; //это на 64
  //TCCR2B = 1<<CS22 | 0<<CS21 | 1<<CS20; //это на 128
  //Подключение прерывания по переполнению Timer2
  TIMSK2 = 1<<TOIE2;

  //Установки Таймер1: Делитель частоты /64, режим 0
  //Частота = 16MHz/64 = 250000 герц или
  //Делитель /64 дает нам хороший рабочий диапазон
  //так что сейчас мы просто жестко запрограммируем это.
  TCCR1A = 0;
  TCCR1B = 1<<CS22 | 0<<CS21 | 0<<CS20; //это на 64
  //Подключение прерывания по переполнению Timer0 (для обнуления скорости)
  TIMSK1 = 1<<TOIE1;
   
 
  //Выводит загружаемое значение таймера
  Serial.print("Timer2 Load:");
  Serial.println(FullTimer0,HEX);
 
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.print("Stepper control"); //выводим на него  строку
  lcd.setCursor(0, 0); //устанавливаем курсор
  lcd.print("#St  Div  Ext"); //выводим на него  строку
  lcd.setCursor(0, 0); //устанавливаем курсор
//  lcd.cursor(); //Включим курсор
//  lcd.blink(); //Пусть мигает
 
  delay(10);
}

void loop() {
OptoSensorTicks=0;
//Один тик таймера - это 4 микросекунды (.000004) Зубъев у нас 70, причем реагируем на вход зуба а оптосенсор и на выход. Значит - 140 событий на оборот экструдера.
//Складывая время
for (int i=0; i<ToothSumm; i++)   {OptoSensorTicks+=OptoToothTimeArray[i];}
ScreenPass++; //Инкремент счетчика
if (ScreenPass>1000)
{
  ScreenPass=0; //Обнуляем счетчик
 
        lcd_key=read_LCD_buttons(); //прочитаем нажатую кнопку
      //0   4   8   12  |
      // St  Div  Ext
      //000  00000 00000
       
        if (lcd_key) //Нажата какая-то кнопка
        {
           if (btnSELECT==lcd_key) //Если нажата кнопка Select
             {
               ControlMode=!ControlMode;
               if (ControlMode)
               {
                 if (ControlPos==0)
                     {
                       lcd.setCursor(0, 0);
                        lcd.print("*");
                        lcd.setCursor(4, 0);
                        lcd.print(" ");
                     }
                 if (ControlPos==1)
                     {
                        lcd.setCursor(0, 0);
                        lcd.print(" ");
                        lcd.setCursor(4, 0);
                        lcd.print("*");
                     }
                 }
                 else
                 {
                   lcd.setCursor(0, 0);
                   lcd.print(" ");
                   lcd.setCursor(4, 0);
                   lcd.print(" ");
                   if (ControlPos==1){lcd.setCursor(4, 0); lcd.print("#"); }
                   if (ControlPos==0){lcd.setCursor(0, 0); lcd.print("#"); }
                 }
              } //Меняем режим на другой :)
           if (ControlMode) //Если режим - "редактирование"
             {
               if (btnUP==lcd_key) //Если нажата кнопка "Больше"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки шагами
                         {
                           OutFreq+=10;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                          }
                       if (ControlPos==1) //Меняем коэффициент шагами
                         {DivK+=1;}
                  }
               if (btnDOWN==lcd_key) //Если нажата кнопка "Меньше"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки шагами
                         {
                           OutFreq-=10;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                          }
                       if (ControlPos==1) //Меняем коэффициент шагами
                         {DivK-=1;}
                  }
               if (btnRIGHT==lcd_key) //Если нажата кнопка "ВПРАВО"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки единицами
                         {
                           OutFreq+=1;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                          }
                       if (ControlPos==1) //Меняем коэффициент единицами
                         {DivK+=.01;}
                  }
               if (btnLEFT==lcd_key) //Если нажата кнопка "ВЛЕВО"
                 {
                       if (ControlPos==0) //Меняем частоту протяжки единицами
                         {
                           OutFreq-=1;
                           SetupTimer2(OutFreq); //устанавливаем таймер
                         }
                       if (ControlPos==1) //Меняем коэффициент единицами
                         {DivK-=.01;}
                  }
             }
            else //режим "выбор"
             {
                if (btnRIGHT==lcd_key) //нажата кнопка ВПРАВО
                  {
                    lcd.setCursor(0, 0);
                    lcd.print(" ");
                    lcd.setCursor(4, 0);
                    lcd.print("#");
                    ControlPos = 1;
                  }
                if (btnLEFT==lcd_key) //нажата кнопка ВЛЕВО   
                {
                    lcd.setCursor(4, 0);
                    lcd.print(" ");
                    lcd.setCursor(0, 0);
                    lcd.print("#");
                    ControlPos = 0;
                }
             }
        }
             
             
      //Теперь в зависимости от режима нужно...
      //Либо считать частоту шаговика от коэффициента при ControlMode==1 и ControlPos==1
      //Либо вычислять коэффициент при ControlMode==1 и ControlPos==0
      //Либо вычислять частоту шаговика от коэффициента при ControlMode==0
      //Короче, только при ControlMode==1 и ControlPos==0 вычислять коэффициент. В остальных случаях - частоту.
       if ((ControlMode==1) && (ControlPos==0)) //Если режим - редактирование, изменение шаговика
        {
         DivK= (float)OptoSensorTicks/(float)OutFreq;  //Вычислим коэффициент
         }
      else
        {
          OutFreq=(float)OptoSensorTicks/(float)DivK;
          if (OutFreq>HighFreq){OutFreq=HighFreq;}
          if (OutFreq<LowFreq){OutFreq=LowFreq;}
          SetupTimer2(OutFreq); //устанавливаем таймер
        }
 
      //Выведем коэффициент
         lcd.setCursor(10, 1);
         lcd.print(DivK);
     
      //выведем скорость
          lcd.setCursor(10, 0);
          lcd.print("     ");
          lcd.setCursor(10, 0);
          lcd.print(ExtruderConst/OptoSensorTicks);
         
      //    lcd.setCursor(9, 1);
      //    lcd.print("     ");
      //    lcd.setCursor(9, 1);
      //    lcd.print(OptoSensorTicks);
     
          lcd.setCursor(0, 1);
          lcd.print(OutFreq/200); //выведем частоту в оборотах в СЕКУНДУ. Коэффициент 1 секунд*200 имп.оборот = 200
     
       
          /*
            Serial.print("sensor=");
            Serial.println(sensorValue,DEC);
            Serial.print("FullTimerCount");
            Serial.println(FullTimerCount);
            Serial.print("FullTimer");
            Serial.println(FullTimer,DEC);
           
            Serial.print("timer=");
            Serial.println(FullTimer0,DEC);
           
            Serial.print("ButtPressed=");
            Serial.println(ButtPressed,DEC);
            Serial.print("OutFreq=");
            Serial.println(OutFreq,DEC);
            Serial.println("***");
            Serial.println("");
         
          Serial.println("***");   
          Serial.println(OptoSensorTicks);
          delay (200);
              */
              Serial.println("***");   
              Serial.print(FullTimer256);
              Serial.print("===");
              Serial.println(FullTimer0);
}

}

//Timer1 указатель вектора прерывания по переполнению
ISR(TIMER1_OVF_vect)
{
    OptoToothTimeArray[OptoToothCounter]=0; //присваиваем элементу массива 0
    OptoToothCounter++; //инкрементим счетчик
    if (ToothSumm==OptoToothCounter) {OptoToothCounter=0;} //если счетчик равен количеству элементов массива (а индекс массива начинается с 0 - то обнуляем.
}

//Timer2 указатель вектора прерывания по переполнению
ISR(TIMER2_OVF_vect)
{
  noInterrupts(); //Запретили прерывания
  if (FullTimer256Count==0) // Срабатывание
        {
         //Переключение IO-вывода в HIGH
          digitalWrite(Step_pin,HIGH); //Переключение IO-вывода в HIGH (Старт импульса)
         FullTimer256Count=FullTimer256;
//         //Переключение IO-вывода в другое состояние.
//         digitalWrite(Step_pin,!digitalRead(Step_pin));
            //Перезагрузка таймера и коррекция по задержке
            if (!FullTimer256)
//                {TCNT2=0;} //Если холостые циклы есть  - максимальное значение
//            else
//                {TCNT2+=FullTimer0;}
                  {TCNT2=FullTimer0;}
          digitalWrite(Step_pin,LOW); //Переключение IO-вывода в LOW (Стоп импульса)
        }
  if (FullTimer256Count==1) //предпоследний цикл
     {
       TCNT2=FullTimer0;
       FullTimer256Count=0;
     }
  if (FullTimer256Count>1) //Обычный цикл
     {FullTimer256Count--;}
  interrupts(); //Разрешили прерывания
}


#define TIMER_CLOCK_FREQ 1000000.0
//15625 for /1024
//2MHz for /8 prescale from 16MHz
//Возвращает начальное значение таймера, которое должно быть загружено в TCNT2
//внутри вашей процедуры ISR.
unsigned char SetupTimer2(float timeoutFrequency){
  //Подсчет начального значения таймера
  //Слегка усложним, добавив холостые просчеты
  long ticks = TIMER_CLOCK_FREQ/timeoutFrequency; // тиков счетчика для получения заданной частоты
//          #if defined(Razr)
//             lcd.setCursor(9, 0);
//             lcd.print(ticks);
//          #endif
  FullTimer256=ticks/256; //Вычисляем количество ПОЛНЫХ 256 циклов таймера
  ticks-=FullTimer256*256;
  ticks-=8;
  FullTimer0=(byte)(255.0-ticks);  //Вычисляем количество доплнительных тиков таймера
  //загружает таймер для первого цикла
  //Сейчас не надо, убираю.
//  if (FullTimer256)
//    {TCNT2=0;} //Если холостые циклы есть  - максимальное значение
//  else
//      {TCNT2=FullTimer0;}
}



// *****************************
//Чтение кнопок с антидребезгом
// read the buttons
int read_LCD_buttons()
{
int adc_key_in = analogRead(ButtonPin);      // Получаем напряжение с АЦП
byte btnTempTemp = btnNONE; //Переменная для хранения свежесчитанного внутри функции
//digitalWrite (LED_pin,0); //debug
//lcd.setCursor(5, 0); //debug
//lcd.print(adc_key_in); //debug
//delay (400); //debug
//Тут используем millis() для отслеживания нажатия кнопки
// Запоминиаем в переменной BtnTime ВРЕМЯ НАЧАЛА нажатия
// Запоминаем в переменной btnTemp нажатия
// Дефиним время антидребезга #define

if (adc_key_in > 950) //Ничего не нажато
  {
   //BtnTime=0; //Скидываем время нажатия
   btnCONTpress=0;
   return btnNONE; // We make ths the 1st option for speed reasons since it will be the most likely result
  }
else //что-то нажато
   {
      if (adc_key_in < 790) { btnTempTemp=btnSELECT; } //Определяем в переменную ТЕКУЩУЮ нажатую кнопку.
      if (adc_key_in < 555) { btnTempTemp=btnLEFT; }
      if (adc_key_in < 380) { btnTempTemp=btnDOWN; }
      if (adc_key_in < 195) { btnTempTemp=btnUP; }
      if (adc_key_in < 50)  { btnTempTemp=btnRIGHT; }

     //А тут проверим, совпадает ли с btnTemp (была ли нажата ранее)
/*     lcd.setCursor(0, 0); //debug
     lcd.print(btnTempTemp); //debug
     lcd.setCursor(2, 0); //debug
     lcd.print(btnTemp); //debug
     lcd.setCursor(1, 1); //debug
     lcd.print(millis()); //debug
     lcd.setCursor(12, 1); //debug
     digitalWrite (LED_pin,1); //debug
     delay (100);
     lcd.print("    "); //debug
     lcd.setCursor(12, 1); //debug
     lcd.print(adc_key_in); //debug
*/
     if (btnTempTemp==btnTemp) //Кнопка уже БЫЛА нажата
     {
        if (btnCONTpress) //Повторные установки клавиши
          {
            if ((millis()-BtnTime) > btnPAUSEtime)
            {
              BtnTime=millis(); // Ставим стартово время для автоповтора
              return btnTemp;
            }
          } 
        else //Первая установка клавиши
           {
              if ((millis()-BtnTime) > btnDEFtime)
              {
                btnCONTpress=1;
                BtnTime=millis(); // Ставим стартово время для автоповтора
                return btnTemp;
              }
           }
     }
     else //Раньше было другое состояние
     {
//       digitalWrite (LED_pin,1); //debug
//       delay (100);
       btnCONTpress=0;
       BtnTime=millis(); //Устанавливаем ТЕКУЩЕЕ время в переменную BtnTime
       btnTemp=btnTempTemp; //устанавливаем кнопку во временную переменную.
     }
   }
return btnNONE;   // Если ничего не сработало, то ничего не возвращаем
}
//***********************************

void OptoSensor() // функция обработки прерывания. Меняет глобальные прееменные
{//Сработал - значит считаем тики. F njxytt - echtlyztv pf
  OptoToothTimeArray[OptoToothCounter]=TCNT1; //присваиваем элементу массива текущее значение таймера
  TCNT1 = 0; //сбрасываем таймер
  OptoToothCounter++; //инкрементим счетчик
  if (ToothSumm==OptoToothCounter) {OptoToothCounter=0;} //если счетчик равен количеству элементов массива (а индекс массива начинается с 0 - то обнуляем.
}

По поводу оптосенсора - а подключено точно правильно?
Дым, идущий из всех устройств в помещении предвещает появление электрика.
RootAdmin
 
Сообщения: 1725
Зарегистрирован: 07 авг 2010, 21:29
Откуда: КМВ
прог. языки: C
ФИО: Андрей

Re: Arduino pro mini для управления шаговым двигателем

Сообщение Revenger » 21 сен 2015, 17:18

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

Добавлено спустя 1 час 32 минуты 56 секунд:
Кстати, уже спрашивал но напомню.. обороты шаговика не менялись это у меня так где-то косяк или пока это просто незаметно?
Просто обороты экструдера когда падают, это заметно и на слух и визуально (когда попадает пластик например между стенкой и шнеком), тут шаговик должен реагировать ооочень заметно на мой взгляд. Хотя могу конечно ошибаться. Пока не испытал ((
Перепроверю оптодатчик сегодня и если что отпишусь уже после испытания на экструдере.
Аватара пользователя
Revenger
 
Сообщения: 1728
Зарегистрирован: 01 ноя 2014, 00:50
Откуда: Ставрополье
ФИО: Александр

Пред.След.

Вернуться в Arduino и другие Xduino

Кто сейчас на конференции

Сейчас этот форум просматривают: Majestic-12 [Bot] и гости: 2