Здравствуйте.
Собирал немного изменённый многоканальный авто полив растений от Гайвера.ULN2003
Использовал Digispark, Pcf8574, CD74HC4067 и 5 ULN2003.
Использовал отдельное питание для ULN2003 с помпами.
При включении, через некоторое время, микроконтроллер по неизвестной причине зависал, оставляя последнюю запущенную помпу включённой. Других проблем на от момент не было. В какой-то момент Digispark перестал загружаться по USB . Попытки восстановить загрузчик не получились , но плата прошивалась. Через еще десяток попыток решить проблему зависания Digispark вообще перестал прошиваться.
инструкции с сайтов:
“Про Ардуино и не только: Прошиваем загрузчик micronucleus в ATtiny85”,
и снятие фьюзов “Снятие проклятия с фьюзов ATtiny85 / Хабр” не дали результат. результатом записи и чтения стали FF (использовал мосфет IRF540N)
При использовании новой платы Digispark всё повторилось .
Подскажите, как исправить повторения перезагрузок и окирпичивания Digispark?
#define LCD_BACKL 1 // автоотключение подсветки дисплея (1 - разрешить)
#define BACKL_TOUT 30 // таймаут отключения дисплея, секунды
#define ENCODER_TYPE 1 // тип энкодера (0 или 1). Если энкодер работает некорректно (пропуск шагов), смените тип
#define ENC_REVERSE 1 // 1 - инвертировать энкодер, 0 - нет
//#define DRIVER_VERSION 0 // 0 - маркировка драйвера дисплея кончается на 4АТ, 1 - на 4Т
#define PUPM_AMOUNT 16 // количество помп, подключенных через реле/мосфет
#define START_PIN 0 // подключены начиная с пина
#define PUMP_PIN 1 // это реле, ведущее на общую помпу
#define SWITCH_LEVEL 1 // реле: 1 - высокого уровня (или мосфет), 0 - низкого
#define PARALLEL 0 // 1 - параллельный полив, 0 - полив в порядке очереди
#define TIMER_START 1 // 1 - отсчёт периода с момента ВЫКЛЮЧЕНИЯ помпы, 0 - с момента ВКЛЮЧЕНИЯ помпы
#define VOLDER_PIN 13 //СИГНАЛЬНЫЙ ПИН SIG ДЛЯ УПРАВЛЕНИЯ УРОВНЕМ ПОМПЫ ВКЛ ВЫКЛ
#define PPIN pcf, 7 // на пин EN выключение мультиплексора
bool setmrn = false; // замена значений в меню (старт стоп и ресет)
#include <EEPROMex.h>
#include <EEPROMVar.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#include <pcf8574.h>
PCF8574 pcf(0x20);
//#include <CD74HC4067.h>
//#define muxSIG A0
#define muxS0 pcf, 3
#define muxS1 pcf, 2
#define muxS2 pcf, 1
#define muxS3 pcf, 0
//CD74HC4067 my_mux((s0), (s1), (s2), (s3));
#define CLK pcf, 4
#define DT pcf, 5
#define SW pcf, 6
#include "GyverEncoder.h"
//Encoder enc1(CLK, DT, SW);
Encoder enc1;
//#include "LCD_1602_RUS.h"
// -------- АВТОВЫБОР ОПРЕДЕЛЕНИЯ ДИСПЛЕЯ-------------
// Если кончается на 4Т - это 0х27. Если на 4АТ - 0х3f
/*#if (DRIVER_VERSION)
LiquidCrystal_I2C lcd(0x27, 16, 2);
#else
LiquidCrystal_I2C lcd(0x3f, 16, 2);
#endif*/
// -------- АВТОВЫБОР ОПРЕДЕЛЕНИЯ ДИСПЛЕЯ-------------
uint32_t pump_timers[PUPM_AMOUNT];
uint32_t pumping_time[PUPM_AMOUNT];
uint32_t period_time[PUPM_AMOUNT];
boolean pump_state[PUPM_AMOUNT];
byte pump_pins[PUPM_AMOUNT];
int8_t current_set;
int8_t current_pump;
boolean now_pumping;
int8_t thisH, thisM, thisS;
long thisPeriod;
boolean startFlag = true;
//bool setmrn = true;
boolean backlState = true;
uint32_t backlTimer;
int SetMuxChannel(byte channel) {
for (byte i = 0, r = 3; i < 4; i++, r--) {
digitalWrite(pcf, r, bitRead(channel, i));
/*digitalWrite(pcf, 3, bitRead(channel, 0));
digitalWrite(pcf, 2, bitRead(channel, 1));
digitalWrite(pcf, 1, bitRead(channel, 2));
digitalWrite(pcf, 0, bitRead(channel, 3));*/
}
}
void setup() {
//pinMode(muxSIG, OUTPUT);
for (byte i = 0; i < 4; i++) {
pinMode(pcf, i, OUTPUT);
}
/*pinMode(muxS0, OUTPUT);
pinMode(muxS1, OUTPUT);
pinMode(muxS2, OUTPUT);
pinMode(muxS3, OUTPUT);*/
//pcf.begin(0x20);
/*for (byte i = 0; i < 5; i++) {
pinMode(pcf, i, OUTPUT);
digitalWrite(pcf, i, LOW);
}*/
//pinMode(s1, OUTPUT); //OUTPUT INPUT_PULLUP
//pinMode(s2, OUTPUT);
//pinMode(s3, OUTPUT);
for (byte i = 5; i < 7; i++) {
pinMode(pcf, i, INPUT_PULLUP);
//pinMode(DT, INPUT_PULLUP);
//pinMode(SW, INPUT_PULLUP);
}
//pinMode(pcf, 7, OUTPUT);
// --------------------- КОНФИГУРИРУЕМ ПИНЫ ---------------------
pinMode(PUMP_PIN, OUTPUT);
digitalWrite(PUMP_PIN, !SWITCH_LEVEL); // выключаем от греха
digitalWrite(PPIN, SWITCH_LEVEL);
for (byte i = 0; i < PUPM_AMOUNT; i++) { // пробегаем по всем помпам
pump_pins[i] = START_PIN + i; // настраиваем массив пинов
// pinMode(START_PIN + i, OUTPUT); // настраиваем пины
//digitalWrite(VOLDER_PIN, !SWITCH_LEVEL); // выключаем от греха
}
digitalWrite(VOLDER_PIN, !SWITCH_LEVEL);
// --------------------- ИНИЦИАЛИЗИРУЕМ ЖЕЛЕЗО ---------------------
//Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.clear();
//enc1.setStepNorm(1);
//attachInterrupt(0, encISR, CHANGE);
enc1.setType(ENCODER_TYPE);
if (ENC_REVERSE) enc1.setDirection(REVERSE);
// --------------------- СБРОС НАСТРОЕК ---------------------
if (!digitalRead(SW)) { // если нажат энкодер, сбросить настройки до 1
lcd.setCursor(0, 0);
lcd.print(setmrn); // "reset"
for (byte i = 0; i < 500; i++) {
EEPROM.writeLong(i, 0);
}
}
while (!digitalRead(SW))
; // ждём отпускания кнопки"
lcd.clear(); // очищаем дисплей, продолжаем работу
// --------------------------- НАСТРОЙКИ ---------------------------
// в ячейке 1023 должен быть записан флажок, если его нет - делаем (ПЕРВЫЙ ЗАПУСК)
if (EEPROM.read(511) != 5) { //1023
EEPROM.writeByte(511, 5);
// для порядку сделаем 1 ячейки с 0 по 500
for (byte i = 0; i < 500; i += 4) {
EEPROM.writeLong(i, 0);
}
}
for (byte i = 0; i < PUPM_AMOUNT; i++) { // пробегаем по всем помпам
period_time[i] = EEPROM.readLong(8 * i); // читаем данные из памяти. На чётных - период (ч)
pumping_time[i] = EEPROM.readLong(8 * i + 4); // на нечётных - полив (с)
if (SWITCH_LEVEL) // вырубить все помпы
pump_state[i] = 0;
else
pump_state[i] = 1;
}
// ---------------------- ВЫВОД НА ДИСПЛЕЙ ------------------------
drawLabels();
changeSet();
}
void loop() {
encoderTick();
periodTick();
flowTick();
backlTick();
}
void backlTick() {
if (LCD_BACKL && backlState && millis() - backlTimer >= BACKL_TOUT * 1000) {
backlState = false;
lcd.noBacklight();
}
}
void backlOn() {
backlState = true;
backlTimer = millis();
lcd.backlight();
}
void periodTick() {
for (byte i = 0; i < PUPM_AMOUNT; i++) { // пробегаем по всем помпам
if (startFlag || (period_time[i] > 0 && millis() - pump_timers[i] >= period_time[i] * 1000 && (pump_state[i] != SWITCH_LEVEL) && !(now_pumping * !PARALLEL))) {
pump_state[i] = SWITCH_LEVEL;
SetMuxChannel(i); // открыть КЛАПАН , SWITCH_LEVEL
digitalWrite(VOLDER_PIN, SWITCH_LEVEL);
pump_timers[i] = millis();
now_pumping = true;
digitalWrite(PUMP_PIN, SWITCH_LEVEL); // включить общую ПОМПУ
digitalWrite(PPIN, !SWITCH_LEVEL);
//Serial.println("Pump #" + String(i) + " ON");
}
}
startFlag = false;
}
void flowTick() {
for (byte i = 0; i < PUPM_AMOUNT; i++) { // пробегаем по всем помпам
if (pumping_time[i] > 0
&& millis() - pump_timers[i] >= pumping_time[i] * 1000
&& (pump_state[i] == SWITCH_LEVEL)) {
pump_state[i] = !SWITCH_LEVEL;
//my_mux.channel(i);
digitalWrite(VOLDER_PIN, !SWITCH_LEVEL); // закрыть КЛАПАН
if (TIMER_START) pump_timers[i] = millis();
now_pumping = false;
digitalWrite(PUMP_PIN, !SWITCH_LEVEL); // выключить общую ПОМПУ
digitalWrite(PPIN, SWITCH_LEVEL);
//Serial.println("Pump #" + String(i) + " OFF");
}
}
}
/*
void encISR() {
enc1.tick(); // отработка энкодера
}
*/
void encoderTick() {
//bool stateCLK = digitalRead(CLK);
// bool stateDT = digitalRead(DT);
//bool stateSW = digitalRead(SW);
enc1.tick(digitalRead(CLK), digitalRead(DT), digitalRead(SW));
//enc1.tick(); // отработка энкодера
if (enc1.isTurn()) { // если был совершён поворот
if (backlState) {
backlTimer = millis(); // сбросить таймаут дисплея
if (enc1.isRight()) {
if (++current_set >= 7) current_set = 6;
} else if (enc1.isLeft()) {
if (--current_set < 0) current_set = 0;
}
if (enc1.isRightH())
changeSettings(1);
else if (enc1.isLeftH())
changeSettings(-1);
changeSet();
} else {
backlOn(); // включить дисплей
}
}
}
// тут меняем номер помпы и настройки
void changeSettings(int increment) {
if (current_set == 0) {
current_pump += increment;
if (current_pump > PUPM_AMOUNT - 1) current_pump = PUPM_AMOUNT - 1;
if (current_pump < 0) current_pump = 0;
s_to_hms(period_time[current_pump]);
drawLabels();
} else {
if (current_set == 1 || current_set == 4) {
thisH += increment;
} else if (current_set == 2 || current_set == 5) {
thisM += increment;
} else if (current_set == 3 || current_set == 6) {
thisS += increment;
}
if (thisS > 59) {
thisS = 0;
thisM++;
if (thisM > 59) {
thisM = 0;
thisH++;
}
}
if (thisS < 0) {
thisS = 59;
thisM--;
if (thisM < 0) {
thisM = 59;
thisH--;
if (thisH < 0) thisH = 0;
}
}
if (current_set < 4) period_time[current_pump] = hms_to_s();
else pumping_time[current_pump] = hms_to_s();
}
}
// вывести название реле
void drawLabels() {
lcd.setCursor(1, 0);
lcd.print(" ");
lcd.setCursor(1, 0);
lcd.print(current_pump);
}
// изменение позиции стрелки и вывод данных
void changeSet() {
switch (current_set) {
case 0:
drawArrow(0, 0);
update_EEPROM();
break;
case 1:
drawArrow(7, 1);
break;
case 2:
drawArrow(10, 1);
break;
case 3:
drawArrow(13, 1);
break;
case 4:
drawArrow(7, 1);
break;
case 5:
drawArrow(10, 1);
break;
case 6:
drawArrow(13, 1);
break;
}
lcd.setCursor(0, 1);
if (current_set < 4) {
lcd.print(setmrn);
s_to_hms(period_time[current_pump]);
} else {
lcd.print(!setmrn);
s_to_hms(pumping_time[current_pump]);
}
lcd.setCursor(8, 1);
if (thisH < 10) lcd.print(0);
lcd.print(thisH);
lcd.setCursor(11, 1);
if (thisM < 10) lcd.print(0);
lcd.print(thisM);
lcd.setCursor(14, 1);
if (thisS < 10) lcd.print(0);
lcd.print(thisS);
}
// перевод секунд в ЧЧ:ММ:СС
void s_to_hms(uint32_t period) {
thisH = floor((long)period / 3600); // секунды в часы
thisM = floor((period - (long)thisH * 3600) / 60);
thisS = period - (long)thisH * 3600 - thisM * 60;
}
// перевод ЧЧ:ММ:СС в секунды
uint32_t hms_to_s() {
return ((long)thisH * 3600 + thisM * 60 + thisS);
}
// отрисовка стрелки и двоеточий
void drawArrow(byte col, byte row) {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(7, 1);
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print(":");
lcd.setCursor(13, 1);
lcd.print(":");
lcd.setCursor(col, row);
lcd.write(126);
}
// обновляем данные в памяти
void update_EEPROM() {
EEPROM.updateLong(8 * current_pump, period_time[current_pump]);
EEPROM.updateLong(8 * current_pump + 4, pumping_time[current_pump]);
}
![2024-12-25 13.07.59 f539f8394ff8|683x500](upload://vxB5yaPBQvHueNJC9Z1zBTjF3KD.jpeg)