Arduino и ацп на одной i2c шине

Тема в разделе "Arduino & Shields", создана пользователем KABANPUNK, 15 май 2020.

  1. KABANPUNK

    KABANPUNK Нуб

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

    Имеется:
    • один мастер мк
    • четыре слэйв мк
    • четыре 16-битных АЦП (ads1115).
    Все сидят на одной I2C шине.
    Проблема заключается в том, что когда слэйвы опрашивают АЦП, часть данных теряется с определенной периодичностью (даже когда обмена данных с мастером нет, просто сидят на одной шине).

    Подскажите, что может быть не так и как это можно исправить.

    P.S.
    • Либа под ADS (АЦП опрашиваю в дифференциальном режиме)
    • Все слэйвы arduino nano (у них один I2C порт, было бы два подключал бы АЦП и мастера к разным, но что есть, то есть)
    • Мастер NodeMCU ESP8266 (но это, как мне кажется не имеет значения)
    • Без I2C шины все МК работают прекрасно
     
  2. Un_ka

    Un_ka Гик

    На всех одно напряжение?
     
  3. KABANPUNK

    KABANPUNK Нуб

    Объединена только земля, напряжение везде примерно одинаковое.
     
  4. Un_ka

    Un_ka Гик

    Попробуйте сделать одинаковым, хотя бы эксперементально. Также не плохо бы проверить номиналы подтягивающих резисторов шины i2c.
     
  5. AlexU

    AlexU Гуру

    Есть возможность помониторить шину I2C?
    В своё время игрался с УНО и двумя гироскопами на одной шине I2C. Так Арудуино время от времени зависала, спасала только перезагрузка платы. Зависала абсолютно рандомно. Анализ показал, что на шине происходил сбой -- кто-то в не нужное время прижимал шину данных, из-за чего Ардуина теряла арбитраж шины и зависала (но зависание скорее следствие кривой библиотеки работы с гироскопами).
    Может тоже есть какие проблемы с распределением доступа к шине?
     
  6. KABANPUNK

    KABANPUNK Нуб

    Кажется я понял в чем была проблема. Скорее всего у меня requestEvent наслаивается на опрос АЦП. То бишь, когда я делаю опрос АЦП и он отвечает на запрос, срабатывает requestEvent который отправляет данные мастеру, из-за этого происходит потеря данных. Как думаете, такое возможно?
     
  7. KABANPUNK

    KABANPUNK Нуб

    Slave:
    Код (C++):
    #include <Wire.h>
    #include <Adafruit_ADS1015.h>

    Adafruit_ADS1115 ads(0x48);
    float voltage = 0.0F;
    char buff[7];

    void setup() {
      ads.begin();

      Wire.begin(8);
      Wire.onRequest(requestEvent);
    }

    void loop() {
      voltage = abs(ads.readADC_Differential_2_3() * 0.1875F / 1000.);
    }

    void requestEvent() {
      dtostrf(voltage,7,2, buff);
      Wire.write(buff);
    }
    Master:
    Код (C++):
    #include <Wire.h>

    void setup() {
      Wire.begin(D1, D2);
      Serial.begin(9600);
    }

    void loop() {
        String dataString = "";
        float voltage = 0.0F;
        Wire.requestFrom(8, 7);
        while (Wire.available()) {
            char c = Wire.read();
            dataString = dataString + c;
        }
        voltage = dataString.toFloat();
        Serial.println(voltage);
    }
     
    Последнее редактирование: 15 май 2020
  8. KABANPUNK

    KABANPUNK Нуб

    По логике, нужно поменять их ролями, тогда будет четыре мастера, и один слэйв в виде ESP, но тут возникает проблема - я нигде не могу найти как задать ESP i2c адрес или хотя бы, использовать его как ведомое устройство.
     
  9. b707

    b707 Гуру

    а зачем нужны слэйв-ардуины? Нельзя все АДС опрашивать одной ЕСП-шкой?
     
  10. KABANPUNK

    KABANPUNK Нуб

    Целиком проект гораздо сложнее, я описал упрощенную версию тех частей, которые нужны для понимания проблемы. Один МК там точно не потянет.
     
  11. AlexU

    AlexU Гуру

    Конечно возможно. По сути у Вас два потока:
    первый -- loop(), который читает данные из ADC;
    второй -- requestEvent(), который данные ADC отправляет по запросу.

    Переменная 'voltage' типа float, которая занимает 4-е байта. Пока эти четыре байта заполняются может прийти запрос и заполнение этих данных прервётся для их отправки через I2C, что приведёт к отправке частично новых заполненных и частично старых данных.
    Так что добавляйте блоки синхронизации -- как минимум запрещайте прерывание в loop() на время чтения данных из ADC: ads.readADC_Differential_2_3(). Но именно только на время вызова этой функции, а не все остальные операции с данными.
    Но это может не сработать (точнее подвесит контроллер), если функция ads.readADC_Differential_2_3() зависит от прерываний или, если она выполняется долго, то это грозит пропуском запросов по шине I2C (что может быть очень критичным для работы слейвов).

    Я бы взглянул на библиотеку 'Adafruit_ADS1015' и поправил её для поддержки многопоточности, если потребуется.