Соединение I2C зависает Ведущий если ведомый отключается

Я использую I2C для успешного запроса критических данных от ведомого устройства (я должен использовать requestFrom() в ведущем устройстве), но если по какой-то причине питание отключается от ведомого устройства, то ведущее устройство полностью зависает. Я и не подозревал, что I2C настолько проблематичен в этом отношении, и уже несколько недель перепробовал все типы альтернативных установок, чтобы попытаться избежать этой ситуации. Кажется, я не могу найти способ, например, просто проверить, существует ли проводное соединение (), прежде чем запрашивать данные и замораживать все. Оба устройства-это Arduino Uno r3. Есть какие-нибудь идеи, которые я, возможно, еще не пробовал? Любой способ отправить одно значение Мастеру и обновлять его каждые 30 секунд. На самом деле данные поступают от емкостного датчика, так что можно было бы просто прочитать оба arduino с одного и того же датчика?

В ответ на предложения Ника Гэммонса (ниже): Я отредактировал приведенные вами примеры, чтобы они соответствовали тому, чего я пытаюсь достичь. Я хотел бы сделать следующее. Поймайте ситуацию, когда ведущий (Arduino Yun Shield) не может связаться с ведомым ( Arduino r3 - с датчиком) из-за сбоя питания и/или считывания датчика. Другими словами, Arduino контролирует другой Arduino. Вот мои отредактированные версии, однако я все еще не могу поймать "другое" состояние недоступности slave:

ВЕДОМЫЙ

#include <Wire.h>

const byte MY_ADDRESS = 42;

// various commands we might get

enum {
    CMD_ID = 1,
    CMD_READ_A0  = 2
    };

char command;

void setup() 
  {
  command = 0;
  pinMode (A0, INPUT);

  Wire.begin (MY_ADDRESS);
  Wire.onReceive (receiveEvent);  // interrupt handler for incoming  messages
  Wire.onRequest (requestEvent);  // interrupt handler for when data is wanted

  }  // end of setup

void loop() 
  {
  // all done by interrupts
  }  // end of loop

void receiveEvent (int howMany)
  {
  command = Wire.read ();  // remember command for when we get request
  } // end of receiveEvent

void sendSensor (const byte which)
  {
  //int val = analogRead (which);
  int val = 125;//test only
  byte buf [2];
    buf [0] = val >> 8;
    buf [1] = val & 0xFF;
    Wire.write (buf, 2);
  }

void requestEvent ()
  {
  switch (command)
     {
     case CMD_ID:      Wire.write (0x55); break;   // send our ID 
     case CMD_READ_A0: sendSensor (A0); break;  // send A0 value
     }
  }

МАСТЕР

#include <Process.h>
#include <I2C.h>
#include <Bridge.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C  lcd(0x27, 16, 2);

const int SLAVE_ADDRESS = 42;

// various commands we might send
enum {
  CMD_ID = 1,
  CMD_READ_A0  = 2,
};

void setup ()
{
  Bridge.begin();
  Console.begin();
  //while (!Console);
  I2c.begin ();
  I2c.timeOut(100);  // milliseconds

  lcd.init();
  lcd.backlight();
}

void loop()
{
  int value = getA0();
  Console.print("A0 value:");
  Console.println(value);
  lcd.setCursor(0, 0);
  lcd.print("A0 value:");
  lcd.print(value);//displays first '0' reading then freezes code
  delay (5000);
}

int getA0() {
  if (I2c.write (SLAVE_ADDRESS, CMD_READ_A0) == 0) {
    byte buf [2];
    int val;
    if (I2c.read (SLAVE_ADDRESS, sizeof buf, buf) == 0)
   {
      val = buf [0] << 8 | buf [1];
      return val;
    }
    else {
      return 0;
    }
  }
  else {
    return 0;
  }
  delay(500);
}

, 👍4

Обсуждение

Являются ли обе линии I2C " низкими` после отключения подчиненного источника питания?, @KIIV

Покажите код, схему и какие модули вы используете, @Dat Ha

"Я все еще не могу уловить состояние" else", когда ведомый объект недоступен "- попробуйте также протестировать вызовы функций "I2c.write", чтобы увидеть, возвращают ли они ноль (что и должно быть)., @Nick Gammon

@NickGammon - Я могу подтвердить, что он ловит правильно даже с помощью I2c.read (как указано выше), однако он работал только в том случае, если ЖК-дисплей I2C также не был подключен. Нет никакого столкновения адресов устройств, но код замораживается, если ЖК-дисплей подключен. Есть ли что-то, о чем я должен знать при подключении жидкокристаллических устройств I2C одновременно с использованием библиотек Wire() и I2c ()?, @Daryl

Вы хотите сказать, что он зависает, если вы подключаете жидкокристаллический дисплей "в прямом эфире", но работает нормально, если все это подключено изначально?, @Nick Gammon

@NickGammon - Я обновил мастер-код, чтобы отразить, где я нахожусь в данный момент. Я имитирую неисправного ведомого, подключая к нему источник питания. Если я комментирую весь ЖК-код, я получаю постоянные показания консоли при выключении ведомого устройства. При использовании ЖК - дисплея он выводит первое значение '0', а затем замерзает. Похоже, что библиотека lcd нуждается в аналогичном тайм-ау для вашей библиотеки I2c?, @Daryl

Является ли ЖК-дисплей I2C? Наверное, так оно и есть. Возможно, вам потребуется углубиться в эту библиотеку и заставить ее использовать библиотеку I2c, внося соответствующие изменения (и добавляя строку тайм-аута)., @Nick Gammon

@NickGammon - Да, ЖК-дисплей I2C. Что касается изменения библиотеки ЖК-дисплеев, мне придется оставить этот проект лучшему человеку C++, чем я! Я принял решение, что в любом случае мне нужны данные гораздо больше, чем считывание, и успешно справился с этим с помощью вашей улучшенной библиотеки I2c. Спасибо за это и за вашу помощь., @Daryl

Это было бы не так уж плохо. В основном вы избавляетесь от "beginTransaction", "endTransaction" и "requestFrom", заменяете "Wire" на "I2c и меняете" #include в начале. Это, вероятно, достижимо с небольшим экспериментированием. Во всяком случае, рад, что помог., @Nick Gammon

Домен dsscircuits отбрасывается на слом. Вы можете найти мастер-библиотеку i2c здесь: https://github.com/DSSCircuits/I2C-Master-Library/blob/master/examples/HMC5883L/HMC5883L.pde, @Tinker Ben


4 ответа


Лучший ответ:

3

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

Смотрите мою страницу о I2C. На этой странице я упоминаю, что существует альтернативная библиотека. Это написано немного по-другому, поэтому время ожидания истекает, если передача не завершается вовремя. Альтернативная загрузка в библиотеку находится здесь - I2C_Rev5.zip - 6,6 КБ.


Как намекнул КИИВ в комментарии, возможно, у вас нет подтягивающих резисторов. Убедитесь, что у вас есть подтягивающие резисторы как от SDA, так и от SCL к линии +5V. Разумное значение было бы 4,7 к (каждый).


Пример кода

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

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

Slave

#include <Wire.h>

const byte MY_ADDRESS = 42;

// various commands we might get

enum {
    CMD_ID = 1,
    CMD_READ_A0  = 2,
    CMD_READ_D8 = 3
    };

char command;

void setup() 
  {
  command = 0;
  
  pinMode (8, INPUT_PULLUP);
  pinMode (A0, INPUT);

  Wire.begin (MY_ADDRESS);
  Wire.onReceive (receiveEvent);  // interrupt handler for incoming messages
  Wire.onRequest (requestEvent);  // interrupt handler for when data is wanted

  }  // end of setup

void loop() 
  {
  // all done by interrupts
  }  // end of loop

void receiveEvent (int howMany)
  {
  command = Wire.read ();  // remember command for when we get request
  } // end of receiveEvent

void sendSensor (const byte which)
  {
  int val = analogRead (which);
  byte buf [2];
  
    buf [0] = val >> 8;
    buf [1] = val & 0xFF;
    Wire.write (buf, 2);
  }  // end of sendSensor

void requestEvent ()
  {
  switch (command)
     {
     case CMD_ID:      Wire.write (0x55); break;   // send our ID 
     case CMD_READ_A0: sendSensor (A0); break;  // send A0 value
     case CMD_READ_D8: Wire.write (digitalRead (8)); break;   // send D8 value
    
     }  // end of switch
  
  }  // end of requestEvent

Приведенный выше код отвечает на запросы на отправку одного из:

  • Его ИДЕНТИФИКАТОР (0x55)
  • Аналоговое считывание (A0)
  • Цифровое считывание (D8)

Мастер с помощью библиотеки проводов

Мастер с моей страницы был такой:

#include <Wire.h>

const int SLAVE_ADDRESS = 42;

// various commands we might send
enum {
    CMD_ID = 1,
    CMD_READ_A0  = 2,
    CMD_READ_D8 = 3
    };

void sendCommand (const byte cmd, const int responseSize)
  {
  Wire.beginTransmission (SLAVE_ADDRESS);
  Wire.write (cmd);
  Wire.endTransmission ();
  
  Wire.requestFrom (SLAVE_ADDRESS, responseSize);  
  }  // end of sendCommand
  
void setup ()
  {
  Wire.begin ();   
  Serial.begin (115200);  // start serial for output
  
  sendCommand (CMD_ID, 1);
  
  if (Wire.available ())
    {
    Serial.print ("Slave is ID: ");
    Serial.println (Wire.read (), DEC);
    }
  else
    Serial.println ("No response to ID request");
  
  }  // end of setup

void loop()
  {
  int val;
  
  sendCommand (CMD_READ_A0, 2);
  val = Wire.read ();
  val <<= 8;
  val |= Wire.read ();
  Serial.print ("Value of A0: ");
  Serial.println (val, DEC);

  sendCommand (CMD_READ_D8, 1);
  val = Wire.read ();
  Serial.print ("Value of D8: ");
  Serial.println (val, DEC);

  delay (500);   
  }  // end of loop

Мастер с использованием библиотеки I2C

beginTransmission() ... write() ... endTransmission() были заменены просто write (), а requestFrom () - read() следующим образом:

#include <I2C.h>

const int SLAVE_ADDRESS = 42;

// various commands we might send
enum {
    CMD_ID = 1,
    CMD_READ_A0  = 2,
    CMD_READ_D8 = 3
    };

void setup ()
  {
  I2c.begin ();  
  I2c.timeOut(100);  // milliseconds
  Serial.begin (115200);  // start serial for output
  
  I2c.write (SLAVE_ADDRESS, CMD_ID);
  byte id;
    
  if (I2c.read (SLAVE_ADDRESS, sizeof id, &id) == 0)
    {
    Serial.print ("Slave is ID: ");
    Serial.println (id, DEC);
    }
  else
    Serial.println ("No response to ID request");
  
  }  // end of setup

void loop()
  {

  I2c.write (SLAVE_ADDRESS, CMD_READ_A0);

  byte buf [2];
  int val;
  if (I2c.read (SLAVE_ADDRESS, sizeof buf, buf) == 0)
    {
    val = buf [0] << 8 | buf [1];
    Serial.print ("Value of A0: ");
    Serial.println (val, DEC);
    }
  else
    Serial.println ("No response to CMD_READ_A0 request");
    
    
  I2c.write (SLAVE_ADDRESS, CMD_READ_D8);
  uint8_t digitalVal;
  if (I2c.read (SLAVE_ADDRESS, sizeof digitalVal, &digitalVal) == 0)
    {
    Serial.print ("Value of D8: ");
    Serial.println (digitalVal, DEC);
    }
  else
    Serial.println ("No response to CMD_READ_D8 request");
    
  delay (500);   
  }  // end of loop
,

В самом деле, хозяин, который вечно ждет от раба ответа, будет повешен, если этот ответ никогда не придет., @Chris Stratton

Спасибо Нику, сначала я избегал этой библиотеки из-за отсутствия подчиненных функций (requestFrom), но сегодня днем я еще раз посмотрю и обновлю ее здесь, если что-нибудь сработает., @Daryl

Кроме того, если устройство имеет защитные диоды, оно может быть "запитано" через линии I2C (и мастерские подтягивания) или удерживать линии под ВЫСОКИМ порогом уровня. Тогда мастер не сможет начать общение. Или это распознается как растяжение часов, и мастер не может продолжать общение., @KIIV

@Nick - подтягивающие резисторы предотвратили скремблирование данных после разрыва соединения. Однако я все еще получаю зависший скрипт на своем мастере при разорванном соединении, пока он снова не подключится. Я решил попробовать альтернативную библиотеку I2c, но хотел бы получить еще несколько примеров, чтобы показать аналогичную настройку для замены моих текущих настроек requestFrom - Master и onRequest(requestEvent)-Slave. Я так понимаю, что мне все равно придется включить библиотеку Wire() в Slave?, @Daryl

Раб будет выглядеть точно так же. Разве библиотека не снабжена примерами?, @Nick Gammon

В Библиотеке есть только один пример, Ник., @Daryl

См. исправленный ответ., @Nick Gammon

@Nick - смотрите правки к исходному вопросу..., @Daryl

Спасибо, что все его раскопали. Этот парень сужает его до: twi.cpp https://github.com/panStamp/arduino_avr/issues/1 У вас случайно нет новых ссылок на то, где я могу найти эти альтернативные библиотеки?, @Pete

@Pete Я сделал копию некоторое время назад в http://gammon.com.au/Arduino/I2C_Rev5.zip, @Nick Gammon

Или смотрите [здесь на Github](https://github.com/DSSCircuits/I2C-Master-Library), @Nick Gammon


1

Была та же проблема при использовании двух мегабайт, взаимодействующих через I2C, наряду с другими элементами, работающими на шине I2C. Мне нужно было, чтобы система продолжала работать, если одна Мега выйдет из строя из-за потери питания. Другой Мега должен обнаружить это и выполнить некоторые команды прерывания.

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

Решение состояло в использовании буфера I2C с возможностью горячей замены. Я использовал этот, и все работало сразу же без проблем после подключения: http://www.mouser.com/search/ProductDetail.aspx?r=595-TCA4311ADR

Чтобы интегрировать пакет SOIC-8 в стандартную макетную плату DIP для ардуино, я использовал адаптер от Adafruit.

,

1

Что касается буфера с возможностью горячей замены TI I2C:

По-видимому, есть аналогичный от Analog Devices, но я его не тестировал: http://www.mouser.com/search/ProductDetail.aspx?r=584-ADUM1250SRZ

Чтобы интегрировать пакет SOIC-8 в стандартную макетную плату DIP для ардуино, я использовал этот адаптер от Adafruit: https://www.adafruit.com/product/1212

Извините за еще один пост - ограничение в 2 гиперссылки на пост из-за отсутствия репутации: -)

Крис

,

2

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

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

bool Would_I2C_Hang(int SDAPin, int SCLPin) {
  bool wouldHang = false;
  pinMode(SDAPin,INPUT);
  pinMode(SCLPin,INPUT);
  if ( digitalRead(SDAPin) != 1 ) {
    Serial.println("SDA pin is low");
    wouldHang = true;
  }
  if ( digitalRead(SCLPin) != 1 ) {
    Serial.println("SCL pin is low");
    wouldHang = true;
  }
  return wouldHang;
}

void setup() {
  Serial.begin(115200);
  for(int i=5;i>0;i--) {
    Serial.print(i);
    delay(1000); // Иногда мне требуется несколько секунд, чтобы перезапустить последовательную консоль.
  }
  Serial.println();
  if ( Would_I2C_Hang(20, 21) ) { //  Adafruit Перо M0 sda и sdl arduino контакты.
    Serial.println("Шина I2C зависнет, если мы попытаемся ее использовать. Отключите его".);
  } else {
    Serial.println("Отметьте устройства I2C как готовые к тестированию").;
  }
}
void loop() {
}
,