/*
Управление шаговиком (драйвер степ-дир) переменным резистором
теперь с кнопками! :)
А на этот раз - с экраном и резистивными кнопками.
Добавил измерение скорости вращения экструдера. Чуть подправил генерацию импульса на шаговик.
v 0.5 */
/*
Вывод Назначение
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 <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 //сколько усреднять зубьев
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
//************
byte ControlMode = 0; //Переменная определяет режим управления. 0 - резистор. 1 - кнопки.
//Переменная для значения таймера
volatile unsigned char FullTimer0;
//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256;//Переменная для количества холостых 256циклов таймера
volatile unsigned int FullTimer256Count; //внутренный счетчик таймера
//значение минимальной частоты
#define LowFreq 1
//значение МАКСИмальной частоты
#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(50);
//Установки Таймер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, 1); //устанавливаем курсор
lcd.print("R B "); //выводим на него строку
lcd.setCursor(0, 1); //устанавливаем курсор
lcd.cursor(); //Включим курсор
lcd.blink(); //Пусть мигает
delay(10);
}
void loop() {
lcd_key=read_LCD_buttons(); //прочитаем нажатую кнопку
if (lcd_key) //Нажата какая-то кнопка
{
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);
}
}
//выведем скорость
OptoSensorTicks=0;
for (int i=0; i<ToothSumm; i++) {OptoSensorTicks+=OptoToothTimeArray[i];}
OptoSensorTicks*=35/ToothSumm; //разделим тики , получаем тиков на оборот
lcd.setCursor(8, 0);
lcd.print(" ");
lcd.setCursor(8, 0);
lcd.print(OptoSensorTicks);
/*
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 - то обнуляем.
}