//Скетч для проверки преобразователя ШИМ-сигнала Ардуино в ток 0...22 мА #include <EEPROM.h> //библиотека работы с памятью #include <LiquidCrystal_I2C.h> //библиотеки работы с LCD через I2C #include <Wire.h> //Не забудь шунтировать выход от кнопок резистором 10 КОм const int BtDownPin = 5 ; //пин кнопки уменьшить. const int BtUpPin = 6 ; //пин кнопки увеличить const int BtSettingPin = 7 ; //пин кнопки настроек const int pwmPinCurrent = 3 ; //пин ШИМ-сигнала //пины модуля I2C LCD-дисплея : //SDA - A4 //SCL - A5 //питание от шилда //подключение преобразователя //пин преобразователя А - пин Arduino D3 (ШИМ-сигнал) //пин преобразователя GND - земля Arduino //питание преобразователя 12В...32В //амперметр или нагрузку подключаем к клеммам "Петля" в соответствии с полярностью int Init_Eeprom_Addr = 1000; //номер ячейки в памяти EEPROM (0-1023), куда мы запишем ключ о //самом первом запуске программы //это нужно, так как при первом запуске в памяти нет наших настроек //и нужно загрузить значения по умолчанию byte Init_Key = 237; //это ключ (0-255). //Если при пуске скетча данные из ячейки Init_Eeprom не совпадают //с Init_Key, вводим в настройки значения по умолчанию, //иначе берем настройки из памяти int SettingNumber = 1; //пункт меню настройки boolean LastButtonSetting = LOW; //предыдущее состояние кнопки Настройки boolean CurrentButtonSetting = LOW; //текущее состояние кнопки Настройки boolean LastButtonDown = LOW; //--- Меньше boolean CurrentButtonDown = LOW; //--- Меньше boolean LastButtonUp = LOW; //--- Больше boolean CurrentButtonUp = LOW; //--- Больше boolean FlagSettingLoop = true; //были ли мы в цикле нажатия кнопки Настройки boolean FlagUpLoop = true; //были ли мы в цикле нажатия кнопки Больше boolean FlagDownLoop = true; //были ли мы в цикле нажатия кнопки Меньше //нужна при нажатии и удерживании кнопки ВВЕРХ unsigned long ButtonHoldTimerUp = 0; //нужна при нажатии и удерживании кнопки ВНИЗ unsigned long ButtonHoldTimerDown = 0; //по прошествии этого времени кнопка ВВЕРХ или ВНИЗ //перейдет в режим удержания, мс const long ButtonHoldIncTime = 500; boolean FlagUpHold = true; //были ли мы в цикле удержания кнопки ВВЕРХ boolean FlagDownHold = true; //были ли мы в цикле удержания кнопки ВНИЗ float pwmValue = 0; //скважность сигнала float CurrentCalculated = 10; //расчетный ток на выходе //ШИМ-сигнал, соответствующий нижней точке токовой характеристики float PWM_Out_Min = 47; //ШИМ-сигнал, соответствующий верхней точке токовой характеристики float PWM_Out_Max = 229; float PWM_Current_Min = 4.05; //нижняя точка токовой характеристики float PWM_Current_Max = 19.76; //верхняя точка токовой характеристики LiquidCrystal_I2C lcd(0x27,16,2); //Задаем адрес и размерность дисплея. void setup() //**************************************************** { pinMode (BtDownPin, INPUT); //прописываем режим пинов pinMode (BtUpPin, INPUT); pinMode (BtSettingPin, INPUT); pinMode (pwmPinCurrent, OUTPUT); //если запись в настройки когда-то была, то читаем их if (EEPROM.read(Init_Eeprom_Addr) == Init_Key) { EEPROM.get(0,PWM_Out_Min); EEPROM.get(4,PWM_Out_Max); EEPROM.get(8,PWM_Current_Min); EEPROM.get(12,PWM_Current_Max); } else //если записи не было, записываем в память значения по умолчанию { EEPROM.put(0,PWM_Out_Min); EEPROM.put(4,PWM_Out_Max); EEPROM.put(8,PWM_Current_Min); EEPROM.put(12,PWM_Current_Max); //и ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); } lcd.init(); //Инициализация lcd lcd.backlight(); //Включаем подсветку lcd.setCursor(0,0); //рисуем первую строчку lcd.print(" PWM "); lcd.setCursor(0,1); //рисуем вторую строчку lcd.print(" CURRENT TEST "); delay(2000); //Пауза в мс. lcd.setCursor(0,1); //рисуем вторую строчку lcd.print("PWM="); lcd.print(pwmValue, 0); lcd.print(" "); } void loop() //********************************************************* { analogWrite(pwmPinCurrent, pwmValue); //посылаем ШИМ-сигнал //рассчитываем выходной ток с задатчика в соответствиии со скважностью ШИМ-сигнала CurrentCalculated = ((pwmValue - PWM_Out_Min) * (PWM_Current_Max - PWM_Current_Min) / (PWM_Out_Max - PWM_Out_Min)) + PWM_Current_Min; lcd.setCursor(0,0); //прописываем первую строку lcd.print("Cur="); lcd.print(CurrentCalculated); //расчетный выходной ток lcd.print(" ("); lcd.print(pwmValue, 0); //скважность сигнала lcd.print(") "); //***************** Нажата кнопка Настройки *********************** //при каждом нажатии кнопки Настройки //перескакиваем на следующий параметр в очереди. И так по кругу //считываем состояние кнопки с устраненным дребезгом CurrentButtonSetting = debounce(LastButtonSetting, BtSettingPin); if(CurrentButtonSetting == HIGH) //кнопка нажата { if (FlagSettingLoop == true) //эта переменная нужна для однократного нажатия кнопки, //даже если ее удерживать { FlagSettingLoop = false; SettingNumber = SettingNumber + 1; if (SettingNumber > 8) { SettingNumber = 1; } } } else //кнопка отжата { FlagSettingLoop = true; } //обновляем текущее значение кнопки LastButtonSetting = CurrentButtonSetting; //******************* Нажата кнопка Увеличить ************************** CurrentButtonUp = debounce(LastButtonUp, BtUpPin); ButtonPlus(CurrentButtonUp); //функция реакции на нажатие кнопки Увеличить LastButtonUp = CurrentButtonUp; //обновляем текущее значение кнопки //******************* Нажата кнопка Уменьшить ************************** CurrentButtonDown = debounce(LastButtonDown, BtDownPin); ButtonMinus(CurrentButtonDown); //функция реакции на нажатие кнопки Уменьшить LastButtonDown = CurrentButtonDown; //обновляем текущее значение кнопки SettingsOnLCD(SettingNumber); //отображаем строку с настройками } //void loop() //********************** ФУНКЦИИ ********************************* //*********** функция отображения настроек на LCD ************* void SettingsOnLCD(int SettingItem) { lcd.setCursor(0,1); switch (SettingItem) { case 1: //Скважность ШИМ-сигнала lcd.print("PWM="); lcd.print(pwmValue, 0); lcd.print(" "); break; //ШИМ-сигнал, соответствующий нижней точке токовой характеристики case 2: lcd.print("PWM_Min="); lcd.print(PWM_Out_Min, 0); lcd.print(" "); break; case 3: //нижняя точка токовой характеристики lcd.print("CurMin="); lcd.print(PWM_Current_Min, 2); lcd.print(" "); break; //ШИМ-сигнал, соответствующий верхней точке токовой характеристики case 4: lcd.print("PWM_Max="); lcd.print(PWM_Out_Max, 0); lcd.print(" "); break; case 5: //верхняя точка токовой характеристики lcd.print("CurMax="); lcd.print(PWM_Current_Max, 2); lcd.print(" "); break; case 6: //запись настроек в EEPROM lcd.print("SaveSet. (UP) "); break; case 7: //чтение настроек из EEPROM lcd.print("ReadSet. (UP) "); break; case 8: //загрузка значений по умолчанию lcd.print("Default (UP) "); break; } } //********* Функция кнопки Больше, Увеличить, Плюс и т.п. ************* void ButtonPlus(boolean BtState) //BtState - состояние кнопки (нажата-отжата) { if(BtState == HIGH) //кнопка нажата { if (SettingNumber >= 1 && SettingNumber <= 5 ) //пункты настроек, где нужен разгон параметра в режиме удержания { if(FlagUpHold == true) //отсечка времени удержания кнопки { ButtonHoldTimerUp = millis(); FlagUpHold = false; } if(millis() - ButtonHoldTimerUp > ButtonHoldIncTime) //сначала однократное нажатие, но если держать кнопку более //ButtonHoldIncTime, она переходит в режим удержания { ButtonPlusAction(SettingNumber, true); } if(FlagUpLoop == true) //однократное нажатие, даже если удерживать кнопку, //пока не прошло ButtonHoldIncTime { FlagUpLoop = false; ButtonPlusAction(SettingNumber, false); } } else //пункты настроек, где нужно только однократное нажатие кнопки { if(FlagUpLoop == true) { FlagUpLoop = false; ButtonPlusAction(SettingNumber, false); } } } else //кнопка отжата { FlagUpLoop = true; FlagUpHold = true; } } //******** Функция изменения параметров при нажатии кнопки Больше ********** void ButtonPlusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //Скважность ШИМ-сигнала pwmValue = pwmValue + 1; if(pwmValue > 255) { pwmValue = 255; } break; case 2: //ШИМ-сигнал, соответствующий нижней точке токовой характеристики PWM_Out_Min = PWM_Out_Min + 1; if(PWM_Out_Min > 255) { PWM_Out_Min = 255; } break; case 3: //нижняя точка токовой характеристики PWM_Current_Min = PWM_Current_Min + Inc_Val_Calc(IncTrigger); break; case 4: //ШИМ-сигнал, соответствующий верхней точке токовой характеристики PWM_Out_Max = PWM_Out_Max + 1; if(PWM_Out_Max > 255) { PWM_Out_Max = 255; } break; case 5: //верхняя точка токовой характеристики PWM_Current_Max = PWM_Current_Max + Inc_Val_Calc(IncTrigger); break; case 6: //запись настроек в EEPROM EEPROM.put(0,PWM_Out_Min); EEPROM.put(4,PWM_Out_Max); EEPROM.put(8,PWM_Current_Min); EEPROM.put(12,PWM_Current_Max); //ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(2000); break; case 7: //чтение настроек из EEPROM EEPROM.get(0,PWM_Out_Min); EEPROM.get(4,PWM_Out_Max); EEPROM.get(8,PWM_Current_Min); EEPROM.get(12,PWM_Current_Max); lcd.setCursor(0,1); lcd.print("Reading... OK "); delay(2000); break; case 8: //загрузка значений по умолчанию. Берем из начала скетча PWM_Out_Min = 47; PWM_Current_Min = 4.05; PWM_Out_Max = 229; PWM_Current_Max = 19.76; lcd.setCursor(0,1); lcd.print("Loading... OK "); delay(2000); break; } } //********* Функция кнопки Меньше, Уменьшить, Минус или т.п. ************* void ButtonMinus(boolean BtState) //BtState - состояние кнопки (нажата-отжата) { if(BtState == HIGH) //кнопка нажата { if (SettingNumber >= 1 && SettingNumber <= 5 ) //пункты настроек, где нужен режим удержания кнопки //для ускоренного изменения параметра { if(FlagDownHold == true) //отсечка времени удержания кнопки { ButtonHoldTimerDown = millis(); FlagDownHold = false; } if(millis() - ButtonHoldTimerDown > ButtonHoldIncTime) //сначала однократное нажатие, но если держать кнопку более //ButtonHoldIncTime, она переходит в режим удержания { ButtonMinusAction(SettingNumber, true); } if(FlagDownLoop == true) //однократное нажатие, даже если удерживать кнопку, //пока не прошло ButtonHoldIncTime { FlagDownLoop = false; ButtonMinusAction(SettingNumber, false); } } else //пункты настроек, где нужно только однократное нажатие кнопки { if(FlagDownLoop == true) { FlagDownLoop = false; ButtonMinusAction(SettingNumber, false); } } } else //кнопка отжата { FlagDownLoop = true; FlagDownHold = true; } } //******** Функция изменения параметров при нажатии кнопки Меньше ********** void ButtonMinusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //Скважность ШИМ-сигнала pwmValue = pwmValue - 1; if(pwmValue < 0) { pwmValue = 0; } break; case 2: //ШИМ-сигнал, соответствующий нижней точке токовой характеристики PWM_Out_Min = PWM_Out_Min - 1; if(PWM_Out_Min < 0) { PWM_Out_Min = 0; } break; case 3: //нижняя точка токовой характеристики PWM_Current_Min = PWM_Current_Min - Inc_Val_Calc(IncTrigger); if(PWM_Current_Min < 0) { PWM_Current_Min = 0; } break; case 4: //ШИМ-сигнал, соответствующий верхней точке токовой характеристики PWM_Out_Max = PWM_Out_Max - 1; if(PWM_Out_Max < 0) { PWM_Out_Max = 0; } break; case 5: //верхняя точка токовой характеристики PWM_Current_Max = PWM_Current_Max - Inc_Val_Calc(IncTrigger); if(PWM_Current_Max < 0) { PWM_Current_Max = 0; } break; } } //*************** Функция устранения дребезга контактов *********** boolean debounce(boolean last, int ButtonPinDebounce) { boolean current = digitalRead(ButtonPinDebounce); //считываем текущее состояние кнопки if(last != current) //если оно иное, чем предыдущее... { delay(50); //ждем 50 мс current = digitalRead(ButtonPinDebounce); //считываем состояние снова } return current; //возвращаем текущее состояние кнопки } //*************** Функция разгона приращения параметра ************ float Inc_Val_Calc(boolean IncTrigger) { static int count_inc = 0; //счетчик итераций if (IncTrigger == true) //пришел сигнал на разгон { count_inc = count_inc + 1; //играя этими числами, вы можете подстраивать разгон параметра под себя if (count_inc > 0 && count_inc <= 30) { return 0.01; } if (count_inc > 30 && count_inc <= 60) { return 0.1; } if (count_inc > 60 && count_inc <= 90) { return 1; } if (count_inc > 90) { return 2; } } else { count_inc = 0; return 0.01; //приращение по умолчанию } }