/*
Управление шаговиком (драйвер степ-дир) переменным резистором
теперь с кнопками! :)
А на этот раз - с экраном и резистивными кнопками.
Добавил измерение скорости вращения экструдера. Чуть подправил генерацию импульса на шаговик.
Управление преработано. Кнопка 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 =500; //Коэффициент для соотношения скорости шаговика к скорости экструдера
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)OutFreq*(float)OptoSensorTicks)/1000; //Вычислим коэффициент
}
else
{
OutFreq=((float)DivK*1000)/(float)OptoSensorTicks;
if (OutFreq>HighFreq){OutFreq=HighFreq;}
if (OutFreq<LowFreq){OutFreq=LowFreq;}
SetupTimer2(OutFreq); //устанавливаем таймер
}
//Выведем коэффициент
lcd.setCursor(8, 1);
lcd.print(DivK, DEC);
//выведем скорость
lcd.setCursor(10, 0);
lcd.print(" ");
lcd.setCursor(10, 0);
lcd.print(ExtruderConst/OptoSensorTicks, DEC);
// 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.print("OutFreq=");
Serial.println(OutFreq,DEC);
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 - то обнуляем.
}