РУКОДЕЛКИ Проблема с Grove IMU 10DOF - не меняется i2c адрес

rathet

✩✩✩✩✩✩✩
16 Июл 2020
1
0
Добрый день, друзья.
Делаю проект умного костюма, для него необходимо 6 имушек на каждое сочленение. Купил Grove IMU 10DOF, подключается по i2c. Думал быстро сменю адрес и все заработает, но проблема в том что нигде не могу найти как сменить адрес i2c, в конечном счете получается 6 датчиков с одинаковым адресом, есть способ как получить доп адрес для устройства, но это не сильно решает проблему, 3 ардуино на одном человеке слишком много. Сталкивался ли кто с такой проблемой? Спасибо.
 

Fleshdeck

★★★✩✩✩✩
19 Янв 2019
349
175
Киев
У большинства датчиков всего один вывод задающий адрес, он подключается к земле или к питанию, что значит, что на одну шину I2C можно подключить два датчика, если нужно больше нужен микроконтролер который поддерживает несколько шин I2C, но с использованием Arduino такое врядли можно сделать
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,242
1,297
Москва
Изменено:

Fleshdeck

★★★✩✩✩✩
19 Янв 2019
349
175
Киев
@Старик Похабыч, эта штука работает только с Trema-модулями на которых установлен STM32, на других модулях она адрес не может менять, и датчики к этой штуке подключить не получится, STM32 выступает в роли конвертера, например для расширителя портов, реле , энкодеров, и других модулей, суть в том что на Trema модулях STM32 получает команды по I2C, и потом управляет тем что на плате, или наоборот считывает сигналы и отправляет по I2C
 

Fleshdeck

★★★✩✩✩✩
19 Янв 2019
349
175
Киев
@Старик Похабыч, Я вспомнил, есть такая штука как I2C мультиплексор TCA9548A, вот эта штука точно подойти должна
 

Fleshdeck

★★★✩✩✩✩
19 Янв 2019
349
175
Киев
@Wan-Derer, в том то и дело что в модуле используется MPU-9250 и BMP280, а у них только один выход чип селект, только два адреса, а ему нужно шесть модулей, тут только мультиплексор поможет
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,242
1,297
Москва
Если я правильно понял, то @Wan-Derer, предлагает подключить эту подтяжку к свободному пину, тогда подавая на пин Hi или Lo можно на ходу менять адрес. если на 5 из 6 модулей подать HI, а на 1 Lo (там видимо надо будет резистор использовать, что бы выход не висел в воздухе) можно будет обращаться к именно к этому одному модулю.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,242
1,297
Москва
Не правильно, но может сработать т.к. обращение идет адресное и не к тому адресу, у которого дубли. может позже попробую на чем нибудь
Например на памяти EEPROM i2c - у меня должно быть несколько штук
 

Wan-Derer

★★★★★✩✩
Команда форума
31 Июл 2018
2,126
412
Москва
wan-derer.ru

@Fleshdeck,
Chip Select, CS это не выбор адреса, а выбор микросхемы. Когда ЦС не активен, линии физически отключаются от шины и модуль перестаёт воспринимать команды. Как если бы ему питание рубануть :)
 

Fleshdeck

★★★✩✩✩✩
19 Янв 2019
349
175
Киев

@Wan-Derer, да я перепутал чипселект и определение адреса, обычно для микросхем с шинами SPI и I2C для определения адреса при подключении по I2C используется вывод SDO
В даташите MPU9250 написано что вывод CS (SPI mode only), да и на модуле Grove IMU 10DOF выведен только I2C вывод, а CS возможно выведен на резистор, но не думаю что это будет работать
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,242
1,297
Москва
Как обещал сделал некоторые тесты. Сразу скажу, что все получилось.
Дано плата ардуина нано 168. К ней по шине i2c подключены 2 микросхемы внешней EEPROM i2c. Подключение стандартное, кроме ноги А2: у 1-ой она подключена к пину 4 , у второй к пину 5. Меняя состояния соотв. пина адрес микросхемы меняется с 50H на 54H. 50H - выбран неактивным адресом, 54H - активным.
1595071277748.png
Далее скетч. В нем использованы мои наработки по записи и чтению во внешнюю память по i2c.
Весь эксперимент в скетче идет в setup
Сначала ставим рабочий адрес на 1-ой микросхеме и пишем в нее числа и ставим не рабочий адрес.
Потом делаем тоже со второй микросхемой, пишем уже другие числа.
А далее выставляя рабочий адрес на 1-ой и 2-ой считываем то, что записали на 1-ом этапе.

C++:
#include <Wire.h>

#define EEPROM_ADDR_1 0x50
#define EEPROM_ADDR_2 0x54
#define EEPROM_PGSZ 0x20
#define HALF_BUFFER 16
#define TEST_POS 100



//------------------------------------------------------------------------//
bool TestAddres(byte address)
{
  Wire.beginTransmission(address);
  byte error = Wire.endTransmission();
  return (error == 0);
}

//------------------------------------------------------------------------//
bool writeEEPROM_i2c(uint8_t EEPROM_ADDR,uint16_t addr, uint16_t len, const uint8_t * data )
{
  uint16_t data_pntr = 0;

  uint16_t first_page = addr / HALF_BUFFER;                  // начальная страница памяти 
  uint16_t last_page = (addr + len - 1) / HALF_BUFFER + 1;;   // конечная страница памяти

  uint16_t first_byte = addr;
  uint16_t end_byte = min((first_page + 1) * HALF_BUFFER - 1, addr + len - 1) + 1;
 
  for (int i = first_page; i < last_page; i++)
  {
    
    Wire.beginTransmission(EEPROM_ADDR);
    Wire.write(first_byte >> 8);
    Wire.write(first_byte & 0xFF);
    
    
    
    for (int j = first_byte; j < end_byte; j++)
      {   
      Wire.write(data [data_pntr++]);
      }

    bool err = Wire.endTransmission(); // добавить обработчик ошибок
    if (err != 0) return false;
    delay(5);
    first_byte = end_byte;
    end_byte = min(first_byte + HALF_BUFFER- 1, addr + len - 1) + 1;
  }
  return true;
}
//------------------------------------------------------------------------//

bool readEEPROM_i2c(uint8_t EEPROM_ADDR, uint16_t addr,  uint16_t len, uint8_t * data)
{
  Wire.beginTransmission (EEPROM_ADDR);
  Wire.write ((uint8_t ) (addr >> 8));    // старший байт адреса чтения
  Wire.write ((uint8_t ) (addr & 0xFF));  // младший байт адреса чтения
  bool err = Wire.endTransmission();
  if (err != 0)  return false;  // ошибка доступа!

  /* выбираем размер для чтения, либо по буферу либо по странице EEPROM
     предполагаем, что размер буфера кратен размеру страницы памяти
     буфер Wire 32, буфер TinyWireM 16
     Страницы памяти 32 64 128 256 байт
  */

  uint8_t One_Page = min(EEPROM_PGSZ, BUFFER_LENGTH);

  uint16_t first_page = addr / One_Page;; // начальная страница памяти
  uint16_t last_page = (addr + len - 1) / One_Page + 1;; // конечная страница памяти
  uint16_t first_byte;// адрес 1-го байта для чтения
  uint16_t end_byte; // адрес последнего байта для чтения
  uint16_t data_pntr = 0;

  for (int i = first_page; i < last_page; i++)
  {
    first_byte = i * One_Page;
    end_byte = (i + 1) * One_Page - 1;
    first_byte = max(addr, first_byte);
    end_byte = min(end_byte, addr + len - 1);
    
    Wire.requestFrom (EEPROM_ADDR, end_byte - first_byte + 1);
    uint8_t cntr = 0;
    while (Wire.available())
    {
      data [data_pntr] = Wire.read ();
      data_pntr++;
      cntr++;
    }   
    if ((end_byte - first_byte + 1) != cntr) return false; // считано меньше чем надо. Ошибка!
  }
  return true;
}



void setup() {
  Wire.begin();
  Serial.begin(115200);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  digitalWrite(4,0);
  digitalWrite(5,0);


  byte TestArray[10];
 

  Serial.println("Пишем в перувую память");
  digitalWrite(4,1);  // устанавливаем на 1-ой памяти адрес 54H 
  for (byte i=0;i<10;i++)TestArray[i]=i;
  writeEEPROM_i2c(EEPROM_ADDR_2,TEST_POS,10,TestArray); //записываем туда данные от 0 до 9
  digitalWrite(4,0);  // устанавливаем на 1-ой памяти адрес 50H

  Serial.println("Пишем во вторую память");
  digitalWrite(5,1);  // устанавливаем на 2-ой памяти адрес 54H
  for (byte i=0;i<10;i++)TestArray[i]=i+10;
  writeEEPROM_i2c(EEPROM_ADDR_2,TEST_POS,10,TestArray); //записываем туда данные от 10 до 19
  digitalWrite(5,0);  // устанавливаем на 1-ой памяти адрес 50H


  Serial.println("Читаем из перувой  памяти");
  digitalWrite(4,1);  // устанавливаем на 1-ой памяти адрес 54H
  readEEPROM_i2c(EEPROM_ADDR_2,TEST_POS,10,TestArray);
  for (byte i=0;i<10;i++) Serial.print(TestArray[i]);
  Serial.println();
  digitalWrite(4,0);  // устанавливаем на 1-ой памяти адрес 50H

  Serial.println("Читаем из второй памяти");
  digitalWrite(5,1);  // устанавливаем на 1-ой памяти адрес 54H
  readEEPROM_i2c(EEPROM_ADDR_2,TEST_POS,10,TestArray);
  for (byte i=0;i<10;i++) Serial.print(TestArray[i]);
  Serial.println();
  digitalWrite(5,0);  // устанавливаем на 1-ой памяти адрес 50H
 
}

void loop() { 

}
Результат:
Код:
Пишем в перувую память
Пишем во вторую память
Читаем из перувой  памяти
0123456789
Читаем из второй памяти
10111213141516171819


Да, если включить оба на 0, то сканером будет определен один адрес. Что будет если писать когда оба адреса активны не пробовал. Это раздрай логики. Но можете попробовать сами.
Таким образом имея пару возможных адресов на датчик можно динамически меняя подтяжку менять сам адрес i2c на лету и использую доп. пин можно подключить несколько одинаковых датчиков к плате. если же использовать что то типа дешифратора, то кол-во занятых пинов можно сократить.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,242
1,297
Москва
1595078745078.png
На фото есть выбор адреса. если я правильно понял, то одна из площадок идет к +, вторая к - а третья к ноге микросхемы. Вот если перерезать связь между 3-ей ногой и одной и первых двух и ее завести на от ноги на свободный пин, то получиться управляемый адрес модуля.

Итого на 6 модулей надо 8 пинов (включая 2 для i2c). Если использовать softi2c, то на каждую пару модулей надо 2 пина. т.е. 3*2 будет 6.
Т.е. для 6 модулей выгоднее использовать библиотеку типа "SoftwareI2C.h" .Но несколько потеряется удобство обращение и скорее всего увеличиться размер используемой памяти.