Убил две меги, что делать?

rene
Offline
Зарегистрирован: 21.01.2014

Доброго времени суток!

Есть вот такой девайс:

Внизу mega2560, на нем ethernet shield w5500, сбоку в гребенке часы реального времени на базе чипа 1302 (задействованы пины с 16 по 20, причем 19 и 20 в качестве питания). Питание внешнее 9 в.

В течении недели писал код, вроде все было нормально, но вчера перестали заливаться скетчи. Порт определяется но в момент заливки разово всыхивают диоды rx/tx и все зависает. Пробовал подключать к другим портам и к другому ПК и отдельно плату МК, результат тот же.

Решил либо внешнее питание либо статика убило мой МК.

Достал новую mega2560, собрал тот же бутерброд, но подключил к ноутбуку с гарантированно рабочими портами. Питание исключительно через USB. Запустил, залил тестовую прошивку, которая blink диодом на 13 пине. Работает.

Начал заливать свою прошивку - девайс умер с точно такими же симптомами, один раз вспыхивают диоды rx/tx и зависает.

В скетче ничего такого нет, размер 600 строк. Неужели скетчем можно убить девайс? Или же проблема в модулях? Есть еще одна mega, но что то страшно стало, решил с вами посоветоваться, что делать дальше?

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Без всяких плат  проверить питание ( хотя-бы на  ISP коннекторе).
Если 5 Вольт есть - прошить blink with delay

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

а если вытащить шилд ардуина также не прошивается?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rene пишет:

Неужели скетчем можно убить девайс? 

Можно. Точно не помню всех танцев, но идея была в том, что пины rx/tx используешь как обычные - подаёшь постоянный HIGH сразу же при загрузке (может на один HIGH, а на другой LOW - не помню) - кирдык, больше ничего не прошивается. Может там чего-то ещё было (не в одном HIGH дело) - не знаю, но был у меня скетч. который убивал nano'ки на раз.

rene
Offline
Зарегистрирован: 21.01.2014

trembo пишет:

Без всяких плат  проверить питание ( хотя-бы на  ISP коннекторе).
Если 5 Вольт есть - прошить blink with delay

Прошивка через ISP имеете ввиду? Не пробовал еще, надо почитать

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

При том скетче, что у меня, прошивка через ICSP не помогала.

rene
Offline
Зарегистрирован: 21.01.2014

jeka_tm пишет:

а если вытащить шилд ардуина также не прошивается?


Нет

rene
Offline
Зарегистрирован: 21.01.2014

ЕвгенийП пишет:

rene пишет:

Неужели скетчем можно убить девайс? 

Можно. Точно не помню всех танцев, но идея была в том, что пины rx/tx используешь как обычные - подаёшь постоянный HIGH сразу же при загрузке (может на один HIGH, а на другой LOW - не помню) - кирдык, больше ничего не прошивается. Может там чего-то ещё было (не в одном HIGH дело) - не знаю, но был у меня скетч. который убивал nano'ки на раз.

// Класс для работы с часами реального времени
//DS1307 clock; // Объявляем объект класса DS1307

const byte RST = 16;  // Chip Enable
const byte DAT = 17;  // Input/Output
const byte CLK = 18;  // Serial Clock
const byte GND = 19;  // Ground
const byte VCC = 20;  // Voltage Collector Collector
DS1302 rtc(RST, DAT, CLK); // Объявляем объект класса DS1302

void setup() {

  // RTC DS1302
  // Настраиваем подачу питания на часы реального времени
  pinMode(VCC, OUTPUT);
  pinMode(GND, OUTPUT);
  digitalWrite(VCC, HIGH);
  digitalWrite(GND, LOW);
...

Вот тут у меня для питания используется пин 19 и 20, но это вроде дополнительные последовательные порты UART, а заливка идет через пины 0, 1? Неужели это может быть причиной?

 

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Попробуйте все лишнее отключить, открыть блинк, нажать заливку и в этот момент несколько раз нажать кнопку ресет на плате. Если не пойдёт заливка, секцию сетап из скетча сюда.

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

ЕвгенийП пишет:

Можно. Точно не помню всех танцев, но идея была в том, что пины rx/tx используешь как обычные - подаёшь постоянный HIGH сразу же при загрузке (может на один HIGH, а на другой LOW - не помню) - кирдык, больше ничего не прошивается. Может там чего-то ещё было (не в одном HIGH дело) - не знаю, но был у меня скетч. который убивал nano'ки на раз.

идея в том, что

передающая  линия – TX (Transmitted Data)
принимающая линия - RX (Received    Data)
 
соответсвенно, если запрограммировать RX как OUTPUT и начать им дёргать, то конечно спалишь, т.к. пин подключен к микросхеме USB-сомпорта.
rene
Offline
Зарегистрирован: 21.01.2014

А этот скетч еще остался? Хотелось бы посмотреть.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не знаю, я честно использовал 0 и 1 и этим убил Nano. 

На отдельно стоящей микросхеме (не на ардуино, а на голой atmega328) пользую 0 и 1 пины в хост и в гриву - всё отлично.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нет, нету. давно дело было. Я тогда даже разбираться не стал что именно произошло, просто запомнил на будущее, что если скетч для ардуино, а не для голой микросхемы, то пинов 1 и 2 у меня "просто нет". С тех пор так и живу :)

rene
Offline
Зарегистрирован: 21.01.2014

ЕвгенийП пишет:

Нет, нету. давно дело было. Я тогда даже разбираться не стал что именно произошло, просто запомнил на будущее, что если скетч для ардуино, а не для голой микросхемы, то пинов 1 и 2 у меня "просто нет". С тех пор так и живу :)

Наверное имеете ввиду пины 0, 1?

rene
Offline
Зарегистрирован: 21.01.2014

Gres пишет:
Попробуйте все лишнее отключить, открыть блинк, нажать заливку и в этот момент несколько раз нажать кнопку ресет на плате. Если не пойдёт заливка, секцию сетап из скетча сюда.

Нет, не получилось. Вот код, там довольно много:

// ==========================================================================================================================================================
// ==================================== -------------------------------- INITIALIZATION -------------------------------- ====================================
// ==========================================================================================================================================================



//#include <EEPROM.h> // библиотека энергонезависимой памяти
#include <SD.h> // библиотека управления картой памяти
#include <SPI.h> // библиотека управления шиной SPI, нужна для Ethernet библиотеки
#include <Ethernet.h> // библиотека управления сетевой картой
#include <EthernetUdp.h> // библиотека для работы с UDP протоколом
#include <Wire.h> // библиотека управления I2C/TWI устройствами, в данном случае часами реального времени
//#include <DS1307.h> // библиотека управления часами реального времени на базе чипа 1307
#include <DS1302.h> // библиотека управления часами реального времени на базе чипа 1302
//#include <string.h> // библиотека для обработки строк

//===================================-------------------------------------- КАРТА ПАМЯТИ
File myFile; // переменная для хранения объектов типа файл
boolean SDError = false; // переменная для хранения ошибки доступа к SD карте

//===================================-------------------------------------- СЕТЬ
// Инициализация библиотеки Ethernet Server на порту 80
EthernetServer server(80);
EthernetClient client;

// UDP экземпляр объекта позволить отправлять и получать пакеты через UDP
EthernetUDP Udp;

byte clIP[] = {
  0, 0, 0, 0
}; // массив для хранения IP адреса клиента
unsigned long sequenceId = 0; // счетчик событий
byte* ptrSeqID = (byte*)&sequenceId; // указатель на счетчик, для возможности разбиения по байтно
char serverName[] = "192.168.1.100"; // имя сервера
int serverPort = 8888; // порт сервера
byte deviceId = 1; // номер устройства
unsigned long heartBeat; // пульс, ежеминутная отсылка пакета о нормальной работе

//===================================-------------------------------------- ДАТЧИК
// Датчик индуктивности
volatile unsigned long inductDuration; // длительность срабатывания датчика
byte interrupt = 1; // прерывания 0 (pin2), 1 (pin3), 2 (pin21), 3 (pin20), 4 (pin19), 5 (pin18)
volatile byte interruptPIN;
volatile boolean inductChange = false; // изменение состояния датчика индуктивности с высокого на низкое
volatile boolean inductPreviousState = false; // предыдущее состояние датчика индуктивности
volatile boolean inductError = false; // флаг наличия ошибки в работе датчика индуктивности
volatile boolean inductHigh = false; // флаг срабатывания датчика индуктивности

//===================================-------------------------------------- ЧАСЫ
// Класс для работы с часами реального времени
//DS1307 clock; // Объявляем объект класса DS1307

const byte RST = 16;  // Chip Enable
const byte DAT = 17;  // Input/Output
const byte CLK = 18;  // Serial Clock
const byte GND = 19;  // Ground
const byte VCC = 20;  // Voltage Collector Collector
DS1302 rtc(RST, DAT, CLK); // Объявляем объект класса DS1302


// =================================================================================================================================================
// ==================================== -------------------------------- SETUP -------------------------------- ====================================
// =================================================================================================================================================



void setup() {

  //===================================-------------------------------------- COM порт
  Serial.begin(9600);
  Serial.print("\nFree RAM: ");
  Serial.print(_FreeRam());
  Serial.println(" KB");

  //===================================-------------------------------------- ЧАСЫ

  // Массив, содержащий время и дату компиляции
  /*
  char compileTime[] = __TIME__;
  char compileDate[] = __DATE__;

  // Разбиваем время компиляции на части
  byte hour = (compileTime[0] - 48) * 10 + (compileTime[1] - 48);
  byte minute = (compileTime[3] - 48) * 10 + (compileTime[4] - 48);
  byte second = (compileTime[6] - 48) * 10 + (compileTime[7] - 48);
  */

  // RTC DS1307
  //clock.begin(); // запускаем метод управления часами

  // Установка текущего времени и даты
  /*
  clock.fillByYMD(2015,02,03); //дата
   clock.fillByHMS(hour, minute, second); //время
   clock.fillDayOfWeek(TUE); //день недели
   clock.setTime(); //записываем все это в RTC чип
   */

  // RTC DS1302
  // Настраиваем подачу питания на часы реального времени
  pinMode(VCC, OUTPUT);
  pinMode(GND, OUTPUT);
  digitalWrite(VCC, HIGH);
  digitalWrite(GND, LOW);

  rtc.writeProtect(false); // защита от записи
  rtc.halt(false); // запуск часов

  // Установка текущего времени и даты
  /*
  Time t(2015, 12, 15, hour, minute, second, Time::kSunday); // год, месяц, день, час, минута, секунда, день недели
  rtc.time(t); //записываем все это в RTC чип
  */

  Serial.println(_Date(0) + " " + _Time());

  //===================================-------------------------------------- КАРТА ПАМЯТИ
  // Подключаем SD Card

  // Распиновка SD card шины SPI:
  // MOSI (Master Out Slave In) - pin 11 или 51 Mega
  // MISO (Master In Slave Ou) - pin 12 или 50 Mega
  // SCLK (Serial Clock) - pin 13 или 52 Mega
  // CS или SS (Chip Select, Slave Select)  - pin 10 или 53 Mega

  // Аппаратный SS pin (10 для Arduino, 53 для Mega) должен быть настроен как выход
  // Мы с этого выхода будем отправлять сигнал управления устройством, например на пин 4 Ethernet shield
  pinMode(53, OUTPUT);

  // Вывод CS для различных типов карт
  // Arduino Ethernet shield: pin 4
  // Adafruit SD shields and modules: pin 10
  // Sparkfun SD shield: pin 8
  Serial.print("\nInitializing SD card...");
  digitalWrite(10, HIGH); // для корректной работы с SD картой необходимо отключить сетевку (pin 10)
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    SDError = true;
  }
  else {
    Serial.println("initialization done.");
  }

  //===================================-------------------------------------- ДАТЧИК
  // Пин подключен к светодиоду для индикации срабатывания сенсора
  pinMode(8, OUTPUT); // светодиод для индикации ошибок
  attachInterrupt(interrupt, _InductionOn, CHANGE); // привязываем прерывание к функции _InductionOn()
  // Привязываем прерываение к пину
  if (interrupt < 2) interruptPIN = interrupt + 2;
  else interruptPIN = 23 - interrupt;
  Serial.println();
  Serial.print("Interrupt: ");
  Serial.println(interrupt);
  Serial.print("InterruptPIN: ");
  Serial.println(interruptPIN);


  //===================================-------------------------------------- СЕТЬ
  // Сетевые параметры mac, ip, dns, gateway, subnet
  byte mac[] = {
    0x00, 0x08, 0xDC, 0x00, 0x00, 0x00
  };
  byte ip[] = {
    192, 168, 1, 177
  };
  //10, 201, 167, 240
  //};
  byte gateway[] = {
    10, 201, 167, 1
  };
  byte subnet[] = {
    255, 255, 255, 0
  };
  byte dns[] = {
    10, 1, 77, 83
  };

  switch (deviceId) {
    case 1:
      {
        mac[5] = 0x01;
        ip[3] = 241;
        break;
      }
    case 2:
      {
        mac[5] = 0x02;
        ip[3] = 242;
        break;
      }
    case 3:
      {
        mac[5] = 0x03;
        ip[3] = 243;
        break;
      }
    case 4:
      {
        mac[5] = 0x04;
        ip[3] = 244;
        break;
      }
    case 5:
      {
        mac[5] = 0x05;
        ip[3] = 245;
        break;
      }
    case 6:
      {
        mac[5] = 0x06;
        ip[3] = 246;
        break;
      }
    default: {}
  }

  // Старт Ethernet платы и Web сервера
  Ethernet.begin(mac, ip, dns, gateway, subnet);
  server.begin();
  Udp.begin(11118); // локальный порт для отправки udp пакетов
  Serial.print("\nWeb server is at ");
  Serial.println(Ethernet.localIP());
  Serial.println();
  delay(50);
  heartBeat = millis(); // запускаем пульс
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rene пишет:

Наверное имеете ввиду пины 0, 1?

Да, конечно, очепятка.

rene
Offline
Зарегистрирован: 21.01.2014

Там в ходе инициализации используется функция для проверки свободной памяти, вот ее код:

// >>>>>>>>>>>>>>> функция возвращает объем свободной памяти ОЗУ
int _FreeRam() {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

 

rene
Offline
Зарегистрирован: 21.01.2014

Вот мои наблюдения:

1. В момент заливки переодически загорается диод rx с переодичностью 10 сек.

2. Иногда, примерно 50 на 50 при подключении меги к usb порту компьютер уходит в ребут.

3. При подключении к USB порту ноутбука выходит сообщение о нехватке питания для устройства

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

И что? Пусть используется. Она никого не обижает. Она, кстати, не объём свободной памяти возвращает, а расстояние от стека до кучи.

rene
Offline
Зарегистрирован: 21.01.2014

ЕвгенийП пишет:

И что? Пусть используется. Она никого не обижает. Она, кстати, не объём свободной памяти возвращает, а расстояние от стека до кучи.

это продолжение предыдущего поста с кодом, там есть вызов функции Serial.print(_FreeRam()); поэтому и привел код данной функции. То что она безобидна, я это знаю. А вот то что это место между стеком и кучей не знал, спасибо!

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

rene пишет:

2. Иногда, примерно 50 на 50 при подключении меги к usb порту компьютер уходит в ребут.

3. При подключении к USB порту ноутбука выходит сообщение о нехватке питания для устройства


Это похоже на аппаратную проблемму, защита usb срабатывает при токе на шине более 0,5 А.
Так быть не должно.
У Вас похоже проблема с прерываниями, вкуривать лень мне сейчас, но в скетче каша полная.
Теперь по сути, нужно успеть залить скетч до того, как произойдёт инициализация.
Попробуйте открыть блинк, при отключенной Дуне, нажать залить и когда пойдёт процесс воткнуть Дуню и так, пока не пойдет процесс заливки. Нажатие ресета тут может не помочь, только подача питания, важно, чтоб устройство своевременно определилось системой и иде не успело сообщить об ошибке.
Можно ещё попробовать втыкать Дуню в usb с зажатой кнопкой ресет и отпускать её втнужный момент.

rene
Offline
Зарегистрирован: 21.01.2014

Клапауций 321 пишет:

ЕвгенийП пишет:

Можно. Точно не помню всех танцев, но идея была в том, что пины rx/tx используешь как обычные - подаёшь постоянный HIGH сразу же при загрузке (может на один HIGH, а на другой LOW - не помню) - кирдык, больше ничего не прошивается. Может там чего-то ещё было (не в одном HIGH дело) - не знаю, но был у меня скетч. который убивал nano'ки на раз.

идея в том, что

передающая  линия – TX (Transmitted Data)
принимающая линия - RX (Received    Data)
 
соответсвенно, если запрограммировать RX как OUTPUT и начать им дёргать, то конечно спалишь, т.к. пин подключен к микросхеме USB-сомпорта.

Что то логика от меня ускользает. Т.е. если пин RX выставить как OUTPUT, а затем тыкать в него 5 вольт он сгорит? Тогда вопрос, а чем отличается OUTPUT от INTPUT на физическом уровне?

rene
Offline
Зарегистрирован: 21.01.2014

Gres пишет:
rene пишет:

2. Иногда, примерно 50 на 50 при подключении меги к usb порту компьютер уходит в ребут.

3. При подключении к USB порту ноутбука выходит сообщение о нехватке питания для устройства

Это похоже на аппаратную проблемму, защита usb срабатывает при токе на шине более 0,5 А. Так быть не должно. У Вас похоже проблема с прерываниями, вкуривать лень мне сейчас, но в скетче каша полная. Теперь по сути, нужно успеть залить скетч до того, как произойдёт инициализация. Попробуйте открыть блинк, при отключенной Дуне, нажать залить и когда пойдёт процесс воткнуть Дуню и так, пока не пойдет процесс заливки. Нажатие ресета тут может не помочь, только подача питания, важно, чтоб устройство своевременно определилось системой и иде не успело сообщить об ошибке. Можно ещё попробовать втыкать Дуню в usb с зажатой кнопкой ресет и отпускать её втнужный момент.

Хм, сработало! Причем оба способа, но с удержанием ресета проще. Огромное человеческое спасибо! Осталось только определить место в коде, убивающим МК. Как найду, обязательно отпишусь.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Не верно прерывания назначены на ноги, посмотрите распиновку, там полный атас... У Вас начинает обрабатывать прерывания на уарте, когда проходит инициализация. Отработайте код частями, тупо куском провода симулируйте срабатывание прерывания и скажем зажигайте светодиод, как отработаете его стабильное срабатывание, можете использовать в готовом проекте.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Gres, спасибо, я тоже попробую свою нанку оживить как доберусь до неё.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вау! Пасибки, включать дуньку с зажатым ресетом оказывается штатный способ залить скетч до инициализации .. тоже, когда обнаружил что накосячил с прерыванием не мог залить нифига. Потом дошло зажать ресет. Пасибки. Я думал мне просто свезло, что залился блинк.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Тут на сайте косяк в референсмануалах по прерываниями меги, инфа не соответствует расспиновке, вот и шагают все массово по этим граблям.

rene
Offline
Зарегистрирован: 21.01.2014

Gres пишет:
Не верно прерывания назначены на ноги, посмотрите распиновку, там полный атас... У Вас начинает обрабатывать прерывания на уарте, когда проходит инициализация. Отработайте код частями, тупо куском провода симулируйте срабатывание прерывания и скажем зажигайте светодиод, как отработаете его стабильное срабатывание, можете использовать в готовом проекте.

У меня заливка скетча идет примерно 8 сек. Если в этот момент завершится инициализация прерывания, все МК умирает. Поставил задержку перед инициализацией на 15 сек. Теперь все нормализовалось.

delay(15000);
  attachInterrupt(interrupt, _InductionOn, CHANGE); // инициализируем прерывание _InductionOn()

Но появилась другая проблема, первый раз заливается нормально, на второй зависает, приходится передергивать USB

UPD. Девайс работает до первой перезагрузки, т.е. открыл окно терминала - работает, закрыл, снова открыл - нет. Передернул USB, опять работает. Буду копать.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Когда МК стартует, при наличии бутлоадера, начинается с его выполнения, он в свою очередь проверяет есть ли данные в уарте, на то он и бутлоадер. Если данные есть, он загружает прошивки, если нет, переходит к выполнению секции сетап Вашего скетча. А в сетапе у Вас начинается каша с прерываниями и МК уже не до уарта и прошивки, он занят глухим циклом обработки прерываний. Потому и нужно успеть залить скетч до того, как дойдёт до исполнения кода прерываний. Его и отрабатывайте отдельно, отдельным, маленьким скетчем, потом встаавите в свой проект.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Косяк Ваш в том, что у Вас инициализированно прерывание, а функция его обработки отсутствует, он поймал прерывание, просто встал и тихо курит незная, что ему делать дальше.
Хотя..., для точной причины нужно видеть весь скетч и схему подключений.
В общем поигайтесь с этим отдельно, отладьте, а потом в рабочий проект.

rene
Offline
Зарегистрирован: 21.01.2014

Gres пишет:
Косяк Ваш в том, что у Вас инициализированно прерывание, а функция его обработки отсутствует, он поймал прерывание, просто встал и тихо курит незная, что ему делать дальше. Хотя..., для точной причины нужно видеть весь скетч и схему подключений. В общем поигайтесь с этим отдельно, отладьте, а потом в рабочий проект.

Ну почему, функция у меня есть

// >>>>>>>>>>>>>>> функция прерывания, изменение уровеня на датчике индуктивности
void _InductionOn() {
  
  boolean inductState = digitalRead(interruptPIN);

  if (inductState == HIGH) {
    inductDuration = millis(); // запоминаем время включения датчика
    inductHigh = true; // запоминаем сработку датчика
  }
  else if (inductPreviousState != LOW) { // проверяем что предыдущее состояние не равно текущему
    inductDuration = millis() - inductDuration; // вычисляем длительность срабатывания датчика
    inductChange = true;
  }
  else inductError = true; // иначе ошибка в работе датчика
  inductPreviousState = inductState;
}

и она нормально отрабатывала. И каши с прерыванием у меня нет, там всего одна строчка отвечающая за инициализацию прерывания

attachInterrupt(interrupt, _InductionOn, CHANGE)

Все остальное это для вывода на экран текущего прерывания и его пина, исключительно в информативных целях

  // Привязываем прерываение к пину
  if (interrupt < 2) interruptPIN = interrupt + 2;
  else interruptPIN = 23 - interrupt;
  Serial.println();
  Serial.print("Interrupt: ");
  Serial.println(interrupt);
  Serial.print("InterruptPIN: ");
  Serial.println(interruptPIN);

Есть подозрение что в данный момент у меня пин прерывания не подтянут к земле и потому куча срабатываний прерывания. Завтра все причешу и попробую еще раз.

rene
Offline
Зарегистрирован: 21.01.2014

А вообще, вот полный скетч, может кому пригодится. Это файловый веб сервер с функцией отправки срабатывания датчика на сервер сбора данных. Скетч не полный, на данный момент дописываю функционал для сбора данных во временный буфер при недоступности сервера.

// ==========================================================================================================================================================
// ==================================== -------------------------------- INITIALIZATION -------------------------------- ====================================
// ==========================================================================================================================================================



//#include <EEPROM.h> // библиотека энергонезависимой памяти
#include <SD.h> // библиотека управления картой памяти
#include <SPI.h> // библиотека управления шиной SPI, нужна для Ethernet библиотеки
#include <Ethernet.h> // библиотека управления сетевой картой
#include <EthernetUdp.h> // библиотека для работы с UDP протоколом
#include <Wire.h> // библиотека управления I2C/TWI устройствами, в данном случае часами реального времени
//#include <DS1307.h> // библиотека управления часами реального времени на базе чипа 1307
#include <DS1302.h> // библиотека управления часами реального времени на базе чипа 1302
//#include <string.h> // библиотека для обработки строк

//===================================-------------------------------------- КАРТА ПАМЯТИ
File myFile; // переменная для хранения объектов типа файл
boolean SDError = false; // переменная для хранения ошибки доступа к SD карте

//===================================-------------------------------------- СЕТЬ
// Инициализация библиотеки Ethernet Server на порту 80
EthernetServer server(80);
EthernetClient client;

// UDP экземпляр объекта позволить отправлять и получать пакеты через UDP
EthernetUDP Udp;

byte clIP[] = {
  0, 0, 0, 0
}; // массив для хранения IP адреса клиента
unsigned long sequenceId = 0; // счетчик событий
byte* ptrSeqID = (byte*)&sequenceId; // указатель на счетчик, для возможности разбиения по байтно
char serverName[] = "192.168.1.100"; // имя сервера
int serverPort = 8888; // порт сервера
byte deviceId = 1; // номер устройства
unsigned long heartBeat; // пульс, ежеминутная отсылка пакета о нормальной работе

//===================================-------------------------------------- ДАТЧИК
// Датчик индуктивности
volatile unsigned long inductDuration; // длительность срабатывания датчика
byte interrupt = 2; // прерывания 0 (pin2), 1 (pin3), 2 (pin21), 3 (pin20), 4 (pin19), 5 (pin18)
volatile byte interruptPIN;
volatile boolean inductChange = false; // изменение состояния датчика индуктивности с высокого на низкое
volatile boolean inductPreviousState = false; // предыдущее состояние датчика индуктивности
volatile boolean inductError = false; // флаг наличия ошибки в работе датчика индуктивности
volatile boolean inductHigh = false; // флаг срабатывания датчика индуктивности

//===================================-------------------------------------- ЧАСЫ
// Класс для работы с часами реального времени
//DS1307 clock; // Объявляем объект класса DS1307

const byte RST = 16;  // Chip Enable
const byte DAT = 17;  // Input/Output
const byte CLK = 18;  // Serial Clock
const byte GND = 19;  // Ground
const byte VCC = 20;  // Voltage Collector Collector
DS1302 rtc(RST, DAT, CLK); // Объявляем объект класса DS1302


// =================================================================================================================================================
// ==================================== -------------------------------- SETUP -------------------------------- ====================================
// =================================================================================================================================================



void setup() {

  //===================================-------------------------------------- COM порт
  Serial.begin(9600);
  Serial.print("\nFree RAM: ");
  Serial.print(_FreeRam());
  Serial.println(" KB");

  //===================================-------------------------------------- ЧАСЫ

  // Массив, содержащий время и дату компиляции
  /*
  char compileTime[] = __TIME__;
  char compileDate[] = __DATE__;

  // Разбиваем время компиляции на части
  byte hour = (compileTime[0] - 48) * 10 + (compileTime[1] - 48);
  byte minute = (compileTime[3] - 48) * 10 + (compileTime[4] - 48);
  byte second = (compileTime[6] - 48) * 10 + (compileTime[7] - 48);
  */

  // RTC DS1307
  //clock.begin(); // запускаем метод управления часами

  // Установка текущего времени и даты
  /*
  clock.fillByYMD(2015,02,03); //дата
   clock.fillByHMS(hour, minute, second); //время
   clock.fillDayOfWeek(TUE); //день недели
   clock.setTime(); //записываем все это в RTC чип
   */

  // RTC DS1302
  // Настраиваем подачу питания на часы реального времени
  pinMode(VCC, OUTPUT);
  pinMode(GND, OUTPUT);
  digitalWrite(VCC, HIGH);
  digitalWrite(GND, LOW);

  rtc.writeProtect(false); // защита от записи
  rtc.halt(false); // запуск часов

  // Установка текущего времени и даты
  /*
  Time t(2015, 12, 15, hour, minute, second, Time::kSunday); // год, месяц, день, час, минута, секунда, день недели
  rtc.time(t); //записываем все это в RTC чип
  */

  Serial.println(_Date(0) + " " + _Time());

  //===================================-------------------------------------- КАРТА ПАМЯТИ
  // Подключаем SD Card

  // Распиновка SD card шины SPI:
  // MOSI (Master Out Slave In) - pin 11 или 51 Mega
  // MISO (Master In Slave Ou) - pin 12 или 50 Mega
  // SCLK (Serial Clock) - pin 13 или 52 Mega
  // CS или SS (Chip Select, Slave Select)  - pin 10 или 53 Mega

  // Аппаратный SS pin (10 для Arduino, 53 для Mega) должен быть настроен как выход
  // Мы с этого выхода будем отправлять сигнал управления устройством, например на пин 4 Ethernet shield
  pinMode(53, OUTPUT);

  // Вывод CS для различных типов карт
  // Arduino Ethernet shield: pin 4
  // Adafruit SD shields and modules: pin 10
  // Sparkfun SD shield: pin 8
  Serial.print("\nInitializing SD card...");
  digitalWrite(10, HIGH); // для корректной работы с SD картой необходимо отключить сетевку (pin 10)
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    SDError = true;
  }
  else {
    Serial.println("initialization done.");
  }

  //===================================-------------------------------------- ДАТЧИК
  // Пин подключен к светодиоду для индикации срабатывания сенсора
  pinMode(8, OUTPUT); // светодиод для индикации ошибок
  delay(15000);
  attachInterrupt(interrupt, _InductionOn, CHANGE); // инициализируем прерывание _InductionOn()
  // Привязываем прерываение к пину
  if (interrupt < 2) interruptPIN = interrupt + 2;
  else interruptPIN = 23 - interrupt;
  Serial.println();
  Serial.print("Interrupt: ");
  Serial.println(interrupt);
  Serial.print("InterruptPIN: ");
  Serial.println(interruptPIN);


  //===================================-------------------------------------- СЕТЬ
  // Сетевые параметры mac, ip, dns, gateway, subnet
  byte mac[] = {
    0x00, 0x08, 0xDC, 0x00, 0x00, 0x00
  };
  byte ip[] = {
    192, 168, 1, 177
  };
  //10, 201, 167, 240
  //};
  byte gateway[] = {
    10, 201, 167, 1
  };
  byte subnet[] = {
    255, 255, 255, 0
  };
  byte dns[] = {
    10, 1, 77, 83
  };

  switch (deviceId) {
    case 1:
      {
        mac[5] = 0x01;
        ip[3] = 241;
        break;
      }
    case 2:
      {
        mac[5] = 0x02;
        ip[3] = 242;
        break;
      }
    case 3:
      {
        mac[5] = 0x03;
        ip[3] = 243;
        break;
      }
    case 4:
      {
        mac[5] = 0x04;
        ip[3] = 244;
        break;
      }
    case 5:
      {
        mac[5] = 0x05;
        ip[3] = 245;
        break;
      }
    case 6:
      {
        mac[5] = 0x06;
        ip[3] = 246;
        break;
      }
    default: {}
  }

  // Старт Ethernet платы и Web сервера
  Ethernet.begin(mac, ip, dns, gateway, subnet);
  server.begin();
  Udp.begin(11118); // локальный порт для отправки udp пакетов
  Serial.print("\nWeb server is at ");
  Serial.println(Ethernet.localIP());
  Serial.println();
  delay(50);
  heartBeat = millis(); // запускаем пульс
}



// ================================================================================================================================================
// ==================================== -------------------------------- LOOP -------------------------------- ====================================
// ================================================================================================================================================



void loop() {

Serial.println(millis());
  // Обнаружено срабатывание датчика индуктивности, шлем пакет
  if (inductHigh) {
    _SendData(1);
    inductHigh = false; // сбрасываем флаг сработки датчика
  }

  // Обнаружено завершение срабатывания датчика индуктивности, шлем пакет и записываем событие на SD карту
  if (inductChange) {
    _SendData(2);
    inductChange = false;
    if (!SDError) _SDWrite(); // если запись возможна, записываем событие на SD карту
  }

  // Обнаружена ошибка в работе датчика индуктивности
  if (inductError) {
    Serial.println("\nError in sensor inductance.\n");
    inductError = false;
    digitalWrite(8, HIGH);
  }

  // Проверяем необходимость посылки пульса
  if (heartBeat <= millis()) _SendData(0);

  // Прослушиваем входящие подключенния на порту 80
  client = server.available(); // функция при наличии подключения возвращает объект client, иначе возвращает false
  if (client) {

    // Самописный класс для извлечения адреса клиента
    /*
    client.clientIP(clIP);
    Serial.print("Client ");
    for (int i = 0; i < 4; i ++)
    {
      Serial.print(clIP[i], DEC);
      if (i < 3) Serial.print(".");
    }
    Serial.println(" connected");
    */

    // Перемeнные для хранения клиентских запросов
    char headerLine[100]; // буфер формирования строк запроса от клиента
    char getFileName[13] = {
      0
    }; // переменная для хранения имени запрошенного файла
    boolean currentLineIsBlank = true; // флаг отслеживания пустых строк запроса
    byte i = 0; // счетчик входящих символов

    while (client.connected()) { // цикл пока есть соединение
      if (client.available()) { // если от клиента поступают данные
        headerLine[i] = client.read(); // читаем поступающие от клиента символы
        // Serial.write(headerLine[i]);
        // последовательность кодов 13, 10 (CF, LF) - окончание HTTP заголовка
        if (headerLine[i] == '\n' && currentLineIsBlank) {

          // -------------------------------------------->> Обработка запроса GET <<-----------------------------------------------------
          if (getFileName[0] != 0 && !SDError) { // обнаружен запрос файла GET

            Serial.print("Get file: ");
            Serial.println(getFileName);

            // Открываем файл для отправки клиенту
            digitalWrite(10, HIGH);
            myFile = SD.open(getFileName);
            if (myFile) {

              myFile.rewindDirectory();
              unsigned long fileSize = myFile.size();

              // Заголовок для открытия окна сохранения файла
              client.println("HTTP/1.1 200 OK");
              client.println("Content-Description: File Transfer");
              client.println("Content-Type: application/octet-stream");
              client.print("Content-Disposition: attachment; filename=");
              client.println(getFileName);
              client.println("Content-Transfer-Encoding: binary");
              client.println("Expires: 0");
              client.println("Cache-Control: must-revalidate");
              client.println("Pragma: public");
              client.print("Content-Length: ");
              client.println(fileSize);
              client.println();

              Serial.print("Transfer file: ");
              Serial.print(getFileName);
              Serial.print("\t");
              //Serial.print(myFile.size(), DEC);
              Serial.print(fileSize);
              Serial.println(" byte");

              if (fileSize > 33554432) Serial.println("File is too large!\n"); // слишком бошльшой файл
              else {
                // Передаем пока есть что передавать
                while (myFile.available()) {
                  client.write(myFile.read());
                }
              }

              myFile.close();
            }
            else {

              // Ошибка открытия файла
              Serial.print("Error opening: ");
              Serial.println(getFileName);
            }
            break;
          }

          // -------------------------------------------->> Создание WEB страницы <<-----------------------------------------------------
          else {
            // Отправляем клиенту стандартный HTTP заголовок
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connection: close");  // соединение будет закрыто после завершения ответа
            client.println("Refresh: 60");  // автоматическое обновление страницы каждые 1 мин.
            client.println();

            // Тело страницы
            myFile = SD.open("/");
            myFile.rewindDirectory();
            client.println("<!DOCTYPE HTML>");
            client.print("<html>");
            client.print("<h1><b>NKT-Service. Device: ");
            client.print(sequenceId);
            client.print("</b></h1><hr>");
            while (true) {
              File entry =  myFile.openNextFile();
              if (! entry) break;
              String fileName = entry.name();
              client.print("<a href=");
              client.print(fileName);
              client.print(">");
              client.print(fileName);
              client.print("<br>");
              client.print("</a>");
              entry.close();
            }
            myFile.close();
            client.print("</html>");

            break;
          }
        }

        if (headerLine[i] == '\n') { // обнаружен символ LF - конец строки
          currentLineIsBlank = true;
          if (headerLine[0] == 'G' && // ищем запрос GET /?
              headerLine[1] == 'E' &&
              headerLine[2] == 'T' &&
              headerLine[3] == 32 &&
              headerLine[4] == '/' &&
              headerLine[5] > 32) {
            byte i = 0;
            while (true) { // формируем имя файла
              if (headerLine[i + 5] == 32) {
                getFileName[i] = 0;
                break;
              }
              getFileName[i] = headerLine[i + 5];
              i ++;
            }
          }
          i = 0; // сбрасываем счетчик входящих символов
        }
        else if (headerLine[i] != '\r') { // иначе если не CF, значит идут данные заголовка
          currentLineIsBlank = false;
        }

        if (i < sizeof(headerLine)) i ++; // увеличиваем счетчик входящих символов
      }
    }
    delay(50);
    client.stop(); // закрывем HTTP сессию
    Serial.println("Client disonnected\n");
  }
}



// ====================================================================================================================================================
// ==================================== -------------------------------- FUNCTION -------------------------------- ====================================
// ====================================================================================================================================================



// >>>>>>>>>>>>>>> функция возвращает отформатированную текущую дату
String _Date(byte format) {

  String date;

  // Запрос данных с модуля часов DS1307
  /*
  clock.getTime();
   if (format == 1) {
   date += clock.year; //год
   date += "-";
   if (clock.month < 10) date += "0";
   date += clock.month; //месяц
   date += "-";
   if (clock.dayOfMonth < 10) date += "0";
   date += clock.dayOfMonth; //день
   }
   else{
   if (clock.dayOfMonth < 10) date += "0";
   date += clock.dayOfMonth; //день
   date += "/";
   if (clock.month < 10) date += "0";
   date += clock.month; //месяц
   date += "/";
   date += clock.year+2000; //год
   }
   */

  // Запрос данных с модуля часов DS1302
  Time clock = rtc.time();
  if (format == 1) {
    date += clock.yr % 100; //год
    date += "-";
    if (clock.mon < 10) date += "0";
    date += clock.mon; //месяц
    date += "-";
    if (clock.date < 10) date += "0";
    date += clock.date; //день
  }
  else {
    if (clock.date < 10) date += "0";
    date += clock.date; //день
    date += "/";
    if (clock.mon < 10) date += "0";
    date += clock.mon; //месяц
    date += "/";
    date += clock.yr; //год
  }

  return date;
}

// >>>>>>>>>>>>>>> функция возвращает отформатированное текущее время
String _Time() {
  String time;

  // Запрос данных с модуля часов DS1307
  /*
  clock.getTime();
   if (clock.hour < 10) time += "0";
   time += clock.hour; //часы
   time += ":";
   if (clock.minute < 10) time += "0";
   time += clock.minute; //минуты
   time += ":";
   if (clock.second < 10) time += "0";
   time += clock.second; //секунды
   */

  // Запрос данных с модуля часов DS1302
  Time clock = rtc.time();
  if (clock.hr < 10) time += "0";
  time += clock.hr; //часы
  time += ":";
  if (clock.min < 10) time += "0";
  time += clock.min; //минуты
  time += ":";
  if (clock.sec < 10) time += "0";
  time += clock.sec; //секунды

  return time;
}

// >>>>>>>>>>>>>>> функция записывает данные на SD карту
void _SDWrite() {
  String path = _Date(1).substring(0, 8) + ".csv"; //приводим в соответствие с форматом FAT32
  // Конвертируем String to char array myFile
  char fileName[15];
  path.toCharArray(fileName, 15);

  String time = _Time() + ";" + inductDuration;
  char content[17];
  time.toCharArray(content, 17);

  digitalWrite(10, HIGH);
  if (!SD.exists(fileName)) {
    myFile = SD.open(fileName, FILE_WRITE); //создаем файл
    if (myFile) { //записываем в файл заголовки
      myFile.println("Time;Duration(ms)");
      myFile.close();
      Serial.println(time + "\t\tCreate file " + path + " successful.");
    }
    else { //ошибка создания файла
      Serial.println(time + "\t\tError create " + path);
    }
  }
  myFile = SD.open(fileName, FILE_WRITE); //открываем файл
  if (myFile) { //записываем в файл время сработки датчика
    myFile.println(content);
    myFile.close();
    Serial.println(time + "\t" + "\tSave to file " + path + " successful.");
  }
  else { //ошибка открытия файла
    Serial.println(time + "\t\t " + path);
  }
}

// >>>>>>>>>>>>>>> функция прерывания, изменение уровеня на датчике индуктивности
void _InductionOn() {
  
  boolean inductState = digitalRead(interruptPIN);

  if (inductState == HIGH) {
    inductDuration = millis(); // запоминаем время включения датчика
    inductHigh = true; // запоминаем сработку датчика
  }
  else if (inductPreviousState != LOW) { // проверяем что предыдущее состояние не равно текущему
    inductDuration = millis() - inductDuration; // вычисляем длительность срабатывания датчика
    inductChange = true;
  }
  else inductError = true; // иначе ошибка в работе датчика
  inductPreviousState = inductState;
}

// >>>>>>>>>>>>>>> функция отправки пакета серверу
void _SendData(byte eventType) {
  heartBeat = millis() + 60000; // откладываем посылку следующего пульса на минуту
  Udp.beginPacket(serverName, serverPort); // подключаемся к серверу по порту serverPort

  Serial.print("\nSend to: ");
  Serial.print(serverName);
  Serial.print(":");
  Serial.println(serverPort);
  Serial.print("Package: ");

  byte packHeader[5]; // массив для хранения заголовка пакета
  packHeader[0] = deviceId; // номер устройства
  for (byte i = 1; i < 5; i ++) packHeader[i] = ptrSeqID[i]; // номер пакета
  packHeader[5] = eventType; // тип события: 0-HeartBeat, 1-InputPipe, 2-OutputPipe
  String dataTime = _Date(1) + " " + _Time() + "." + millis() % 1000; // временя генерации события

  //Udp.write(packHeader, 5);
  //Udp.println(dataTime);

  Serial.print(" ");
  Serial.println(dataTime);
  Serial.println();

  //Udp.endPacket(); // пакет сформирован, отправляем

  // Если запись возможна, записываем отправленный пакет на SD карту
  if (!SDError && eventType) {
    if (!SD.exists("Temp")) SD.mkdir("Temp");
    Serial.println("marker");

    myFile = SD.open(sequenceId + ".tmp", FILE_WRITE); //создаем файл
    if (myFile) { //записываем в файл заголовки
      myFile.write(packHeader, 5);
      myFile.println(dataTime);
      myFile.close();
      //Serial.println('Create file Temp/' + sequenceId + ".tmp successful.");
    }
    else { //ошибка создания файла
      //Serial.println("Error create Temp/" + sequenceId + '.tmp');
    }
  }
}

// >>>>>>>>>>>>>>> функция возвращает объем свободной памяти ОЗУ
int _FreeRam() {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

 

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

rene пишет:

Ну почему, функция у меня есть

// >>>>>>>>>>>>>>> функция прерывания, изменение уровеня на датчике индуктивности
void _InductionOn() {
  
  boolean inductState = digitalRead(interruptPIN);

  if (inductState == HIGH) {
    inductDuration = millis(); // запоминаем время включения датчика
    inductHigh = true; // запоминаем сработку датчика
  }
  else if (inductPreviousState != LOW) { // проверяем что предыдущее состояние не равно текущему
    inductDuration = millis() - inductDuration; // вычисляем длительность срабатывания датчика
    inductChange = true;
  }
  else inductError = true; // иначе ошибка в работе датчика
  inductPreviousState = inductState;
}
Есть подозрение что в данный момент у меня пин прерывания не подтянут к земле и потому куча срабатываний прерывания. Завтра все причешу и попробую еще раз.

rene пишет:
Есть подозрение что в данный момент у меня пин прерывания не подтянут к земле и потому куча срабатываний прерывания. Завтра все причешу и попробую еще раз.

Блин ну пошто народ так любит в полной темноте бродить с растопыренными по сторонам руками и предполагать, что там впереди?

Закоментируйте все в этой функции, вставьте включение и выключение светодиода встроенного на плате и Вы точно будете знать, что у Вас происходит.

StrangerM
Offline
Зарегистрирован: 02.11.2013

Подобным образом (играми с ногами 0,1) я спалил мегу. Через ISP она прошивается легко. Исследовал ноги (резал плату) они сдохли именно у меги. Ну и ничего - нет одного СОМ порта и  заливать сложнее... но жива в остальном.

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

rene пишет:

Что то логика от меня ускользает. Т.е. если пин RX выставить как OUTPUT, а затем тыкать в него 5 вольт он сгорит? Тогда вопрос, а чем отличается OUTPUT от INTPUT на физическом уровне?

Свойства порта вводы/вывода (pin), сконфигурированного как порт ввода

Выводы Arduino (Atmega) стандартно настроены как порты ввода, таким образом, не требуется явной декларации в функции pinMode(). Сконфигурированные порты ввода находятся в высокоимпедансном состоянии. Это означает то, что порт ввода дает слишком малую нагрузки на схему, в которую он включен. Эквивалентом внутреннему сопротивлению будет резистор 100 МОм подключенный к выводу микросхемы.

Свойства порта ввода/вывода, сконфигурированного как порт вывода

Выводы, сконфигурированные как порты вывода, находятся в низкоимпедансном состоянии. Данные выводы могут пропускать через себя достаточно большой ток. Выводы микросхемы Atmega могут быть источником (положительный) или приемником (отрицательный) тока до 40 мА для других устройств. Такого значения тока достаточно чтобы подключить светодиод (обязателен последовательно включенный резистор), датчики, но недостаточно для большинства реле, соленоидов и двигателей.

Короткие замыкания выводов Arduino или попытки подключить энергоемкие устройства могут повредить выходные транзисторы вывода или весь микроконтроллер Atmega.

http://arduino.ru/Tutorial/DigitalPins

rene
Offline
Зарегистрирован: 21.01.2014

Проблема с зависаниями оказалась в глючном модуле W5500, а не в прерываниях. После замены модуля все заработало. Всем спасибо!

Technolog
Offline
Зарегистрирован: 19.11.2014

Gres</p> <p>[/quote пишет:
  Можно ещё попробовать втыкать Дуню в usb с зажатой кнопкой ресет и отпускать её втнужный момент.

Спасибо Gres. Помогло залить рабочий но "незаливаемый" скетч. Буду пользовать.