Перезагружается и иногда зависает Arduino UNO
- Войдите на сайт для отправки комментариев
Вс, 01/05/2022 - 21:30
Добрый день!
Не могу решить что делать с внезапными перезапуском контроллера.
Так как все таки на 80% за такое поведение отвечает программа прошу помощи.
В проекте 2 UNO, одна главная по данным с микрометра вырабатывает PID сигнал скорость для мотора,
и через modbus rtu rs485 передает второй, которая только крутит шаговый двигатель не отвлекаясь.
У главной есть экран и три кнопки и 2 потенциометра для управления.
Драйвер двигателя питается от отдельного БП 12в, ардуины от 5в источника.
Все собрано в металлическом ящике, заземлено.
Собственно зависает главная.
Фото и скетч во вложении.
#define dataIn 3 //шина данных микрометра #define clockIn 2 //шина clock микрометра, attachInterrupt #define StopExtruderPin 9 //реле стоп нагрев и экструдер #define ReleOut_1 10 //реле выхода след катушка #define CalibratingPin 7 //калибровка микрометра #define ResetSpoolPin 5 // сброс только количества катушек #define SetPin 6 // полный сброс #define SetKP A0 // установка длины #define SetKI A1 // установка количества катушек #include <GyverPID.h> #include <ModbusRtu.h> #include <avr/eeprom.h> //данные калибровки будем держать в памяти #include <LiquidCrystal_I2C.h> // Подключение библиотеки LiquidCrystal_I2C lcd(0x27, 16, 2); // Указываем I2C адрес (наиболее распространенное значение), а также параметры экрана (в случае LCD 1602 - 2 строки по 16 символов в каждой int SizeConst = 1750; //Размер в микронах byte Popravka = eeprom_read_byte(0); //поправка к микрометру const int PIDInterval = 400; //интервал обработки в мс GyverPID regulator(1, 0.2, 0.1, PIDInterval); //стартовые разгонные значения unsigned int buswrite[1]; // шина обмена с мотором протяжки/слейв 2 //unsigned int busread[1]; // шина обмена с мотором протяжки/слейв 2 byte u8state; volatile bool isfs = 0; //минус, отрицательное значение с микрометра (в прерывании volatile) volatile byte index = 0; //счётчик битов (в прерывании volatile) volatile unsigned long xData = 0; //новые показания с микрометра (в прерывании volatile) int GetSize = 0; //текущее значение с микрометра float AVGSize = 0; //усредненное значение с микрометра volatile unsigned long previousGetMillis = 0; //(в прерывании volatile) long Timeout = 8; //таймаут чтения битов в мс long LCDTimer = 0; // таймер обновления данных на экране long MicrometrTimer = 0; //таймер опроса микрометра long MotorSpeedUpdateTimer = 0; //таймер вычисления новой скорости мотора протяжки float FilamentLength = eeprom_read_word(2); // текущая длина прутка, берем из памяти если переапустились int SavedFilamentLength = int(FilamentLength); //последняя сохрненная длина int RollVolume = 323; // объем катушки в метрах byte RollNumber = eeprom_read_byte(1); // номер текущей катушки int RollCount = 1; // количество катушек int motorSpeed = 9000; //скорость вращения const int minSpeed = 7000; //минимальная const int maxSpeed = 15000; //максимальная byte Mode = 0; //режим 0- разгон, 1-работа после набранной скорости byte RebootCount = eeprom_read_byte(100); //количество перезагрузок int ErrorCount = 0; //количество ошибок по диаметру Modbus master(0, 0, 4); // 0 this is master, 0 Serial по умолчанию в UNO он один //а 4 будет обозначать, что контакты DE&RE модуля RS-485 подключены к контакту 4 платы Arduino modbus_t telegram[1]; // посылка данных для modbus unsigned long u32wait; //таймер modbus uint8_t u8query=0; //!< pointer to message query void LCD_print() { //вывод на экран надписей lcd.home(); // Установка курсора в начало первой строки lcd.print("L "); // Набор текста на первой строке lcd.setCursor(0, 1); lcd.print("s "); //на второй } void DataToDisp() { //Вывод на экран среднего диаметра, ошибки, скорости lcd.setCursor(2, 0); lcd.print(" "); //стираем lcd.setCursor(2, 0); lcd.print(int(FilamentLength)); //пишем длину прутка lcd.setCursor(5, 0); lcd.print("/"); lcd.setCursor(6, 0); lcd.print(RollVolume); //пишем RollVolume lcd.setCursor(11, 0); lcd.print(RollNumber); // номер катушки lcd.setCursor(13, 0); lcd.print("/"); lcd.setCursor(14, 0); lcd.print(RollCount); // количество катушек //вторая строка lcd.setCursor(2, 1); lcd.print(" "); // стираем lcd.setCursor(2, 1); lcd.print(motorSpeed); //пишем скорость lcd.setCursor(8, 1); lcd.print(ErrorCount); //пишем ошибки lcd.setCursor(9, 1); lcd.print(RebootCount); //пишем количество перезапусков if (int(AVGSize + Popravka - SizeConst) >= 0) { lcd.setCursor(12, 1); } else { lcd.setCursor(11, 1); } lcd.print(int(AVGSize + Popravka - SizeConst)); //пишем ошибку измеренную } void mkmDupt() { // обработка информации с микрометра if ((index != 0) && (millis() - previousGetMillis > Timeout) ) { //обнуление по превышению таймаута index = 0; xData = 0; } if (index > 23) { //если слово считано полностью if (isfs == 0) { // если есть знак "минус" GetSize = int(xData); } else { GetSize = -int(xData); } index = 0; xData = 0; } } void modbussend() { switch ( u8state ) { case 0: if (millis() > u32wait) u8state++; // wait state break; case 1: // telegram 0: write registers telegram[0].u8id = 2; // slave address telegram[0].u8fct = 6; // function code (this one is registers write) telegram[0].u16RegAdd = 0; // start address in slave telegram[0].u16CoilsNo = 1; // number of elements (coils or registers) to write telegram[0].au16reg = buswrite; // pointer to a memory array in the Arduino // telegram 1: read registers //telegram[1].u8id = 2; // slave address //telegram[1].u8fct = 3; // function code (this one is registers read) //telegram[1].u16RegAdd = 1; // start address in slave //telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to read //telegram[1].au16reg = busread; // pointer to a memory array in the Arduino master.query( telegram[u8query] ); // send query (only once) u8state++; //u8query++; //номер запроса телеграммы 0 или 1 //if (u8query > 0) u8query = 0; break; case 2: master.poll(); // check incoming messages if (master.getState() == COM_IDLE) { u8state = 0; u32wait = millis() + 200; } break; } } void setup() { //rs485 master.begin(19200); // baud-rate at 19200 master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over //инициализация микрометра pinMode (dataIn, INPUT_PULLUP); //привязываем шину данных на dataIn pinMode (clockIn, INPUT_PULLUP); //и clock на 2й вход attachInterrupt(0, getBit, FALLING); //и аттачим clock прерывание также на 2й вход pinMode(ReleOut_1, OUTPUT); pinMode(StopExtruderPin, OUTPUT); // второе выходное реле pinMode(CalibratingPin, INPUT); pinMode(ResetSpoolPin, INPUT); pinMode(SetKP, INPUT); //потенциометр pinMode(SetKI, INPUT); //потенциометр pinMode(SetPin, INPUT); digitalWrite(CalibratingPin, HIGH); digitalWrite(ResetSpoolPin, HIGH); digitalWrite(SetPin, HIGH); digitalWrite(ReleOut_1, 0); //выход реле первого реле выключен digitalWrite(StopExtruderPin, 0); //выход остановка экструдера реле выключен //пид regulator.setDirection(REVERSE); //напрвление регуулирования regulator.setLimits(minSpeed, maxSpeed); //пределы regulator.setpoint = SizeConst; //диаметр поддержания lcd.init(); // Инициализация дисплея lcd.backlight(); // Подключение подсветки lcd.setCursor(0, 1); lcd.print("Popravka = "); lcd.setCursor(12, 1); lcd.print(Popravka); delay(200); LCD_print(); RebootCount++; eeprom_write_byte(100, RebootCount); // сохраним } void Stop() { //остановка системы digitalWrite(ReleOut_1, 1); //команда остановить намотку digitalWrite(StopExtruderPin, 1); //остановка экструдера motorSpeed = 0; while (digitalRead(SetPin)) { modbussend(); } //стоим до нажатия кнопки сброс digitalWrite(ReleOut_1, 0); //команда запустить намотку digitalWrite(StopExtruderPin, 0); //запуск экструдера LCD_print(); } void loop() { modbussend(); // если внезапно в процессе работы пруток стал мал или обрыв if (ErrorCount > 10) { lcd.home(); lcd.print("Err="); lcd.print(AVGSize + Popravka); Stop(); } if (!digitalRead(SetPin)) { delay(20); if (!digitalRead(SetPin)) { //кнопка обнуления пробега прутка и режима FilamentLength = 0; eeprom_write_word (2, int(FilamentLength)); SavedFilamentLength = 0; RollNumber = 1; eeprom_write_byte(1, RollNumber); // сохраним RebootCount=0; eeprom_write_byte(100, RebootCount); // сохраним Mode = 0; ErrorCount = 0; LCD_print(); } } if (!digitalRead(ResetSpoolPin)) { delay(20); if (!digitalRead(ResetSpoolPin)) { //кнопка обнуления количества катушек только RollNumber = 1; eeprom_write_byte(1, RollNumber); // сохраним } } if (FilamentLength > RollVolume) { // *** метров следующая катушка, щелкнуть релюшкой FilamentLength = 0; eeprom_write_word (2, int(FilamentLength)); SavedFilamentLength = 0; RollNumber++; eeprom_write_byte(1, RollNumber); // сохраним digitalWrite(ReleOut_1, 1); //выход реле первого реле включен delay(20); digitalWrite(ReleOut_1, 0); //выход реле первого реле выключен if (RollNumber > RollCount) { //становимся на паузу Stop(); } } RollVolume = map(analogRead(SetKP), 0, 1023, 100, 400); RollCount = map(analogRead(SetKI), 0, 1023, 1, 11); if ( millis() - MotorSpeedUpdateTimer > PIDInterval) { // расчет скорости MotorSpeedUpdateTimer = millis(); regulator.input = AVGSize + Popravka; //ПИД вход с датчика motorSpeed = regulator.getResultTimer(); //пид результат if (motorSpeed > 9000) { Mode = 1; //вышли на режим, коэфф P = 0.6 c 20/04/22 lcd.setCursor(0, 1); lcd.print("S "); regulator.Kp = 0.6; } buswrite[0] = motorSpeed; //отправляем в шину FilamentLength = FilamentLength + motorSpeed/1000*0.008207; //вычисление длну if (FilamentLength-SavedFilamentLength>5) { //раз в 5 метров сохраним в память SavedFilamentLength = FilamentLength; eeprom_write_word (2, int(FilamentLength)); } ///1000/6400*3.1415*0.0418 } if ( millis() - MicrometrTimer > 30) { //опрос микрометра и скользящее среднее // раз в 30 мс усредняем за 20 значений, те за 600 мс MicrometrTimer = millis(); mkmDupt(); //запрос микрометра if (GetSize > -20 && GetSize < 2500) { AVGSize = 0.92 * AVGSize + 0.08 * GetSize; //Скользящее среднее, устредняем показания за последние 20 считываний } } if ( millis() - LCDTimer > 500) { // обновление дисплей 2 раз в сек, чтобы не было каши из цифр LCDTimer = millis(); DataToDisp(); // Вывод данных на дисплей if ((Mode && (AVGSize + Popravka < 1600)) || (Mode && (AVGSize + Popravka > 2000))) { ErrorCount++; //ошибки } } if (!digitalRead(CalibratingPin)) { delay (20); if (!digitalRead(CalibratingPin)) { //кнопка обнуления пробега прутка и режима //калибровка микрометра lcd.home(); // Установка курсора в начало первой строки lcd.print("Calibrating..."); // Набор текста на первой строке lcd.setCursor(0, 1); lcd.print("Popravka = "); for (int i = 0; i < 100; i++) { mkmDupt(); //запрос микрометра if (GetSize > -20 && GetSize < 2500) { AVGSize = 0.95 * AVGSize + 0.05 * GetSize; //Скользящее среднее, устредняем показания за последние 20 считываний } delay(25); } Popravka = SizeConst - AVGSize; lcd.setCursor(12, 1); lcd.print(Popravka); eeprom_write_byte(0, Popravka); delay(5000); LCD_print(); } } } void getBit() { //чтение битов и флаги микрометра previousGetMillis = millis(); if (index < 20) { if (digitalRead(dataIn) == 0) { xData |= 1 << index; } } else { if (index == 20) //минус isfs = !digitalRead(dataIn); }; index++; }
GiverPID, эт прям больше 3/4 ...
Не понял). Больше чего?
GiverPID
ПИДов здесь не любят. Особенно Гайверов. Обращайтесь к ним.
Хм. Какая разница откуда библиотека?)
Зачем платить за Феррари, если и на самокате можно ехать?))
Видимо я что то пропустил)
Тем не менее, мне очень нужна помощь.
Даже в разрезе "вот тут возможно ошибка".
Спасибо.
Так тебе уже сказали, или пальцем ткнуть в ту строку с гивером?
Самое интересное, что зависания начались позже чем библиотека pid gyver была подключена.
Попробую заменить.
Хотя там всего пара формул.
Надо вставлять вывод меток в Serial или SoftSerail в критических местах и по логу смотреть/локализовать проблему. Ничего нового для отладки не изобрели.
Я правильно вас понял, что скорее всего перезапуск или зависание происходит в каком то одном месте кода?
Мы пишем в порт метки типа "точка1 точка 2" и тд расставленные по коду, перед и после функций... Подключаем ноутбук с портом и ждем лог.
После очередного фейла можно будет попробовать локализовать проблему?
Да. Позапускайте несколько раз. Как определите где, то выводите уже больше данных для этого куска кода ...
А Вас ни разу не смущает вот такое количество предупреждений компилятора? (это я скомпилировал Ваш код). Вы уверены, что на все эти предупреждения можно плевать?
Прошу прощения, это какие то расширенные уведомления?
У меня компилятор показывает только нижнюю часть "Скетч использует..."
в меню - настройке - поставьте все уведомления и будет Вам счастье
Это самые обычные предупреждения, просто целевая аудитория IDE - беременные доярки. Вот разработчики и выключили их все, что девочкам мозг не выносить - в их положении это вредно.
Зайдите в "Настройки", найдите там "Сообщения компилятора" и вместо "Ничего" выберите "Все". Узнаете много интересного.
Зайдите в "Настройки", найдите там "Сообщения компилятора" и вместо "Ничего" выберите "Все". Узнаете много интересного.
кстати и нечего бочку на Гайвера катить, там всего одно предупреждение в его библиотеке )))
там чуток поправить:
Библиотеку не смотрел, но может лучше задать правильный тип для _dt ?
Спаисбо!
Надеюсь большее поправлю сам, но что он тут хочет от меня?
Да, две ошибки остались.
Помогите пожалуйста понять:
супер! готово:
Протестирую, отпишусь.
Пока библиотеку GyverPID заменил на стандартную.