Реализация I2C

NoAlex
Offline
Зарегистрирован: 11.06.2019

Привет
Имеются следующие компоненты:
- arduino nano
- 2 резистора номиналом 6кОм
- лазерный дальномер VL53L0X

Подключено следующим образом:
- SCL к D9 (подтягивающий резистор)
- SDA к D8 (подтягивающий резистор)
- XSHUT к D7

Я хочу реализовать свою библиотеку для считывания данных с этого датчика. Для этого изучил протокол I2C и начал заниматься "ногодрыгом" на ардуинке :-) Разбил отправку на следующие этапы:
- СТАРТ сигнал
- Отправка адреса (0x29), 1 для установки режима чтения
- Считывание ACK (0 на sda) или NACK (1 на sda) сигнала с датчика
- Считывание байта данных
- Отправка NACK сигнала
- СТОП сигнал

Для реализации каждого этапа написал небольшие функции:
СТАРТ сигнал

pinMode( sda, OUTPUT );
digitalWrite( sda, HIGH );
digitalWrite(scl, HIGH);
delayMicroseconds(TIME / 2);
digitalWrite(sda, LOW);
delayMicroseconds(TIME / 2);
digitalWrite(scl, LOW);
delayMicroseconds(TIME / 2);

ОТПРАВКА бита

digitalWrite(sda, bit); //устанавливаем бит в дату. 1 - читаем данные с сенсора, 0 - передаём данные сенсору
delayMicroseconds(TIME / 2);
digitalWrite(scl, HIGH); //включаем линию времени
//если датчик насильно прижал время, то ждём, пока он отпустит
while (digitalRead(scl) == LOW) { {Serial.println(F("Wait sensor sendBit"));} }
delayMicroseconds(TIME);
digitalWrite(scl, LOW);
digitalWrite(sda, LOW);
delayMicroseconds(TIME / 2);

ACK (0 в sda) и NACK (1 в sda) сигналы, по своей сути обычные биты, поэтому я вызываю функцию по отправке битов с нужным параметром

СТОП сигнал

pinMode(sda, OUTPUT);
delayMicroseconds(TIME / 2);
digitalWrite(scl, HIGH);
delayMicroseconds(TIME / 2);
digitalWrite(sda, HIGH);
delayMicroseconds( TIME / 2 );

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Но протестировав код, я увидел, что от датчика приходит какая-то ерунда, причём иногда (очень редко) показания правильные. Есть подозрение, что что-то с таймингами или неверно/не в нужное время посылаю какой-то сигнал.
Прошу помощи, не знаю что пробовать, в какую сторону смотреть.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Примеры ответов, чтобы не быть голословным:
- 00000000
- 11111111
- 00000001
- 01111111
- 10000000

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

библиотэка Wire тебя чем не устраивает? 

NoAlex
Offline
Зарегистрирован: 11.06.2019

Я хочу реализовать свои библиотеки, поэтому не использую другие

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Мне одному показалось, что как-то Великим запахло? Никак очередной "апостол" будет "письма" постить.

sadman41
Offline
Зарегистрирован: 19.10.2016

digitalWrite() с лёгкостью испоганит любые критичные тайминги.

NoAlex
Offline
Зарегистрирован: 11.06.2019

sadman41 пишет:

digitalWrite() с лёгкостью испоганит любые критичные тайминги.

В данном протоколе тайминг определяется линией scl, и нет жёстких требований по таймингам, поэтому некритично.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

NoAlex пишет:

В данном протоколе тайминг определяется линией scl, и нет жёстких требований по таймингам, поэтому некритично.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

в данном протоколе, матьтваю, если он реализован ПРАВИЛЬНО, еще и прерывания есть.

возьми да ознакомься   https://playground.arduino.cc/Main/SoftwareI2CLibrary/

sadman41
Offline
Зарегистрирован: 19.10.2016

NoAlex пишет:

sadman41 пишет:

digitalWrite() с лёгкостью испоганит любые критичные тайминги.

В данном протоколе тайминг определяется линией scl, и нет жёстких требований по таймингам, поэтому некритично.

Тогда берёте логический анализатор и пишете логи: закидывания через I2C данных стандартным Wire и своими процедурами. Потом сравниваете и смотрите где несовпадение.

NoAlex
Offline
Зарегистрирован: 11.06.2019

DetSimen пишет:

в данном протоколе, матьтваю, если он реализован ПРАВИЛЬНО, еще и прерывания есть.

возьми да ознакомься   https://playground.arduino.cc/Main/SoftwareI2CLibrary/

Я под таймингом имел ввиду длительность тактового синхроимпульса, может этим запутал.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Про прерывания знаю, я вообще про это не спрашивал :-)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

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

nik182
Онлайн
Зарегистрирован: 04.05.2015

В теме про тиньку13 Влад давал его реализацию i2c. Работает хорошо. 

Logik
Offline
Зарегистрирован: 05.08.2014

NoAlex пишет:

Для реализации каждого этапа написал небольшие функции:

СТАРТ сигнал

pinMode( sda, OUTPUT );
digitalWrite( sda, HIGH );
digitalWrite(scl, HIGH);
delayMicroseconds(TIME / 2);
digitalWrite(sda, LOW);
delayMicroseconds(TIME / 2);
digitalWrite(scl, LOW);
delayMicroseconds(TIME / 2);

..........

От вот это - неверный подход. И дело даже не в digitalWrite, которые медленные, на что правильно указано. Дело в том, что I2C построенна на логике состояний линия либо в пассивном - подтянута к + резистором, либо активна - один из участников обмена открытым ключем притянул к земле. А для этого нужно чтоб линия устанавливалась не в 1 и 0, а в пассивное состояние или 0. Обычно это делается так: пассивное ставится pinMode(sda, INPUT);, а активное - pinMode(sda, OUTPUT) при том что digitalWrite(..., LOW);. Так как у Вас только в наипростейшем случае будет работать, пока не потребуется контроль ACK и/или мультимастер.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Logik пишет:

NoAlex пишет:

Для реализации каждого этапа написал небольшие функции:

СТАРТ сигнал

pinMode( sda, OUTPUT );
digitalWrite( sda, HIGH );
digitalWrite(scl, HIGH);
delayMicroseconds(TIME / 2);
digitalWrite(sda, LOW);
delayMicroseconds(TIME / 2);
digitalWrite(scl, LOW);
delayMicroseconds(TIME / 2);

..........

От вот это - неверный подход. И дело даже не в digitalWrite, которые медленные, на что правильно указано. Дело в том, что I2C построенна на логике состояний линия либо в пассивном - подтянута к + резистором, либо активна - один из участников обмена открытым ключем притянул к земле. А для этого нужно чтоб линия устанавливалась не в 1 и 0, а в пассивное состояние или 0. Обычно это делается так: пассивное ставится pinMode(sda, INPUT);, а активное - pinMode(sda, OUTPUT) при том что digitalWrite(..., LOW);. Так как у Вас только в наипростейшем случае будет работать, пока не потребуется контроль ACK и/или мультимастер.

Я понял, попробую

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

nik182 пишет:

В теме про тиньку13 Влад давал его реализацию i2c. Работает хорошо. 

вот она, сообщение 966 в ветке про Тиньку13.

Можно и сюда скопировать, дисков на форуме пока хватает... ;))

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

При использовании с Уно-Нано нужно убрать "тиньковые" особенности: delayMicroseconds() - совершенно нормально идет вместо самописного, digitalWrite() - тоже нормально пойдет для низкой скорости. Часы и 1602 экран - работают. И2С вообще довольно некритичный протокол на низких скоростях. Но софт И2С на 328 контроллере - это полная дурь, потому, что на нем есть АППАРАТНЫЙ и2с. Только если какой нибудь хаб городить... Короче - число мастурбационные задачи, не практические.

#define _NOP() asm volatile("nop")

// delay() для тиньки и свой нормальный, а delayMicroseconds() дурной
//поэтому свой нужен

//а вот глобального такого дефайна может и не быть, как ни странно, так для страховки завел
#define F_CPU 4800000UL
#define LOOP_CYCLES 8
#define us(num) (num/(LOOP_CYCLES*(1/(F_CPU/1000000.0))))

inline __attribute__((gnu_inline)) void asm_delay(uint16_t delay){
  uint16_t u=us(delay);
do _NOP(); while(delay--);
}

#define wdelay_us(x)  asm_delay(us(x))
#define wdelay(x) delay(x)


/*****************************************************/
//дальше мелкий вариант i2c на любых ногах
//если работать через функции (закоментировано) то будет переносимость хоть куда

#define IICR  1
#define IICW  0
// на 100КГц примерно по 4 мкс нужно держать уровни SCL - со скоростью сами экспериментируйте, мне не нужно было
#define I2CDelay()  wdelay_us(5)
#define SDA_Pin   0
#define SCL_Pin   1

/*
#define SDA_In()    digitalRead(SDA_Pin)
#define SCL_In()    digitalRead(SCL_Pin)

     void SDA_Hi()  { digitalWrite(SDA_Pin, HIGH); pinMode(SDA_Pin,  INPUT);}
     void SDA_Lo()  { digitalWrite(SDA_Pin, LOW ); pinMode(SDA_Pin, OUTPUT);}
     void SCL_Hi()  { digitalWrite(SCL_Pin, HIGH); pinMode(SCL_Pin,  INPUT);}
     void SCL_Lo()  { digitalWrite(SCL_Pin, LOW ); pinMode(SCL_Pin, OUTPUT);}
*/
#define SDA_In()    (PINB & 0B00000001)
#define SCL_In()    (PINB & 0B00000010)

     inline void SDA_Hi()  { PORTB |= 0B00000001; DDRB &= 0B11111110;}
     inline void SDA_Lo()  { PORTB &= 0B11111110; DDRB |= 0B00000001;}
     inline void SCL_Hi()  { PORTB |= 0B00000010; DDRB &= 0B11111101;}
     inline void SCL_Lo()  { PORTB &= 0B11111101; DDRB |= 0B00000010;}

void I2CInit (void)
{
    SDA_Hi();
    SCL_Hi();
}

void I2CStart (void)
{
  SCL_Hi();
  //while (!SCL_In());
  I2CDelay();
  SDA_Lo();
  I2CDelay();
  SCL_Lo();  
  
}
 
void I2CStop (void)
{
  SCL_Lo();
  SDA_Lo();
  I2CDelay();
  SCL_Hi();
  //while(!SCL_In());
  I2CDelay();
  SDA_Hi();
  I2CDelay();
}

boolean I2CWrite(byte b)
{
  byte i = 1<<7;
  boolean ack=0;

  while(i)
    {
      //I2CDelay();
      if (b & i) SDA_Hi(); else SDA_Lo();
      I2CDelay();
      SCL_Hi();
      I2CDelay();
      SCL_Lo();
      
      i>>=1; 
    }
  SDA_Hi();
  I2CDelay();
  SCL_Hi();
 // while(!SCL_In());

  if ( SDA_In()==0 ) ack = 1;
  I2CDelay();
  SCL_Lo();
  SDA_Hi();
  return ack;
}

byte I2CRead(boolean ack)
{
  byte i=8;
  byte b=0;
  
  while(i--)
  {
   b <<= 1;
   SCL_Lo();
   SDA_Hi();
   I2CDelay();
   SCL_Hi();
   //while(!SCL_In());
   if (SDA_In()) b |= 1;
   
   I2CDelay();
   SCL_Lo();
  }
  if (ack) SDA_Lo();
  else SDA_Hi();
  I2CDelay();
  SCL_Hi();
  //while(!SCL_In());
  I2CDelay();
  SCL_Lo();
  return (b);
}

 

NoAlex
Offline
Зарегистрирован: 11.06.2019

wdrakula пишет:

nik182 пишет:

В теме про тиньку13 Влад давал его реализацию i2c. Работает хорошо. 

вот она, сообщение 966 в ветке про Тиньку13.

Можно и сюда скопировать, дисков на форуме пока хватает... ;))

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

При использовании с Уно-Нано нужно убрать "тиньковые" особенности: delayMicroseconds() - совершенно нормально идет вместо самописного, digitalWrite() - тоже нормально пойдет для низкой скорости. Часы и 1602 экран - работают. И2С вообще довольно некритичный протокол на низких скоростях. Но софт И2С на 328 контроллере - это полная дурь, потому, что на нем есть АППАРАТНЫЙ и2с. Только если какой нибудь хаб городить... Короче - число мастурбационные задачи, не практические.

#define _NOP() asm volatile("nop")

// delay() для тиньки и свой нормальный, а delayMicroseconds() дурной
//поэтому свой нужен

//а вот глобального такого дефайна может и не быть, как ни странно, так для страховки завел
#define F_CPU 4800000UL
#define LOOP_CYCLES 8
#define us(num) (num/(LOOP_CYCLES*(1/(F_CPU/1000000.0))))

inline __attribute__((gnu_inline)) void asm_delay(uint16_t delay){
  uint16_t u=us(delay);
do _NOP(); while(delay--);
}

#define wdelay_us(x)  asm_delay(us(x))
#define wdelay(x) delay(x)


/*****************************************************/
//дальше мелкий вариант i2c на любых ногах
//если работать через функции (закоментировано) то будет переносимость хоть куда

#define IICR  1
#define IICW  0
// на 100КГц примерно по 4 мкс нужно держать уровни SCL - со скоростью сами экспериментируйте, мне не нужно было
#define I2CDelay()  wdelay_us(5)
#define SDA_Pin   0
#define SCL_Pin   1

/*
#define SDA_In()    digitalRead(SDA_Pin)
#define SCL_In()    digitalRead(SCL_Pin)

     void SDA_Hi()  { digitalWrite(SDA_Pin, HIGH); pinMode(SDA_Pin,  INPUT);}
     void SDA_Lo()  { digitalWrite(SDA_Pin, LOW ); pinMode(SDA_Pin, OUTPUT);}
     void SCL_Hi()  { digitalWrite(SCL_Pin, HIGH); pinMode(SCL_Pin,  INPUT);}
     void SCL_Lo()  { digitalWrite(SCL_Pin, LOW ); pinMode(SCL_Pin, OUTPUT);}
*/
#define SDA_In()    (PINB & 0B00000001)
#define SCL_In()    (PINB & 0B00000010)

     inline void SDA_Hi()  { PORTB |= 0B00000001; DDRB &= 0B11111110;}
     inline void SDA_Lo()  { PORTB &= 0B11111110; DDRB |= 0B00000001;}
     inline void SCL_Hi()  { PORTB |= 0B00000010; DDRB &= 0B11111101;}
     inline void SCL_Lo()  { PORTB &= 0B11111101; DDRB |= 0B00000010;}

void I2CInit (void)
{
    SDA_Hi();
    SCL_Hi();
}

void I2CStart (void)
{
  SCL_Hi();
  //while (!SCL_In());
  I2CDelay();
  SDA_Lo();
  I2CDelay();
  SCL_Lo();  
  
}
 
void I2CStop (void)
{
  SCL_Lo();
  SDA_Lo();
  I2CDelay();
  SCL_Hi();
  //while(!SCL_In());
  I2CDelay();
  SDA_Hi();
  I2CDelay();
}

boolean I2CWrite(byte b)
{
  byte i = 1<<7;
  boolean ack=0;

  while(i)
    {
      //I2CDelay();
      if (b & i) SDA_Hi(); else SDA_Lo();
      I2CDelay();
      SCL_Hi();
      I2CDelay();
      SCL_Lo();
      
      i>>=1; 
    }
  SDA_Hi();
  I2CDelay();
  SCL_Hi();
 // while(!SCL_In());

  if ( SDA_In()==0 ) ack = 1;
  I2CDelay();
  SCL_Lo();
  SDA_Hi();
  return ack;
}

byte I2CRead(boolean ack)
{
  byte i=8;
  byte b=0;
  
  while(i--)
  {
   b <<= 1;
   SCL_Lo();
   SDA_Hi();
   I2CDelay();
   SCL_Hi();
   //while(!SCL_In());
   if (SDA_In()) b |= 1;
   
   I2CDelay();
   SCL_Lo();
  }
  if (ack) SDA_Lo();
  else SDA_Hi();
  I2CDelay();
  SCL_Hi();
  //while(!SCL_In());
  I2CDelay();
  SCL_Lo();
  return (b);
}

 

Я чисто попробовать реализовать самому, всегда успею перейти на аппаратную работу по i2c. Эксперементировал по разному, ни в какую не работал, в итоге решил взять код выше. Датчик не отвечает после отправки адреса. Цепанул к цифровым пинам 8 и 9, меняю их статусы напрямую.

inline void LaserDistanceSensor::SDA_H() { PORTB |= 1 << 0; DDRB &= ~(1 << 0);}
inline void LaserDistanceSensor::SDA_L() { PORTB &= ~(1 << 0); DDRB |= 1 << 0; }
inline void LaserDistanceSensor::SCL_H() { PORTB |= 1 << 1; DDRB &= ~(1 << 1); }
inline void LaserDistanceSensor::SCL_L() { PORTB &= ~(1 << 1); DDRB |= 1 << 1; }

Что ещё может быть? Могу выложить весь текущий код.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Никто не ответит? :(

NoAlex
Offline
Зарегистрирован: 11.06.2019

Если это поможет решить проблему, то датчик покупал тут https://ru.aliexpress.com/item/VL53L0X-ToF-940nm-GY-VL53L0XV2/32848271367.html?spm=a2g0s.9042311.0.0.274233edRFsX8Y

Такой ощущение, что датчик притянул линию SDA для подтверждения того, что адрес был его и он готов писать, но дальше дело не пошло. Вот код отправки байта и чтения бита подтверждения

      {Serial.println("Byte " + String(bt));}
	byte i = 1 << 7;
	boolean ack = 0;

	while (i)
	{
		if (bt & i) SDA_H(); else SDA_L();
		delayMicroseconds(TIME);
		SCL_H();
		delayMicroseconds(TIME);
		SCL_L();
		i >>= 1;
	}
	SDA_H();
	delayMicroseconds(TIME);
	SCL_H();
	if (SDA_G() == 0) ack = 1;
	delayMicroseconds(TIME);
	SCL_L();
	SDA_H();

	return ack;

А вот код для чтения байта и отправки бита подтверждения

        byte i = 8;
	byte b = 0;

	while (i--) {
		{Serial.println(F("GET BYTE"));}
		{Serial.println("SCL " + String(SCL_G()) + " SDA " + String(SDA_G()));}
		b <<= 1;
		SCL_L();
		delayMicroseconds(TIME);
		SCL_H();
		if (SDA_G()) b |= 1;
		delayMicroseconds(TIME);
		{Serial.println("SCL " + String(SCL_G()) + " SDA " + String(SDA_G()));}
		SCL_L();
		{Serial.println(F("GET SEND BYTE\n"));}
	}

	if (isACK) SDA_L();
	else SDA_H();
	delayMicroseconds(TIME);
	SCL_H();
	delayMicroseconds(TIME);
	SCL_L();
	SDA_H();

	{Serial.println("Distance " + String(b));}

	return (b);

Повторюсь, функции взял из кода выше, а результат такой же, как и был.

NoAlex
Offline
Зарегистрирован: 11.06.2019

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

NoAlex, провенрьте сначала на аппаратном I2C, и только когда будете уверены, что датчик работает, переводите его на программный.

NoAlex
Offline
Зарегистрирован: 11.06.2019

andriano пишет:

NoAlex, провенрьте сначала на аппаратном I2C, и только когда будете уверены, что датчик работает, переводите его на программный.

Проверено, работает

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Напиши код с использованием стандартных аппаратных фунций И2С requestFrom(), beginTranmission() и т.д. А когда заработает, замени их на софтверные.

Повторю: НЕ с библиотекой адафрут, а только с голым Wire.h. И еще - будь внимателен с задержками. Свое понимание проверь на чем-то проще датчика - на часах 3132 или экране 1602 на И2С. Оба очень нетребовательны. Там же, в ветке про тиньку, пример работы с часами (вроде был), это похоже на твой датчик - тоже нужно вычитывать из регистра.

------------------------------------

Если что, у меня где-то есть такой датчик. Не обещаю, но может и повожусь с ним, я его еще не распаковыавал даже, потому как не могу понять, нафиг он мне нужен... ;)))))

NoAlex
Offline
Зарегистрирован: 11.06.2019

Кстати, по поводу регистра (хорошо, что ты сказал про это :)), я верно понял, что для работы с этим датчиком не надо слать адрес регистра после подтверждения адреса датчиком, чтобы считать данные? По даташиту есть адреса, но для считывания расстояния они вроде не нужны.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Появилось время для продолжения экспериментов :-). 
wdrakula, к сожалению у меня нет ни часов, ни экрана, поэтому продолжаю работать с датчиком.

Посмотрел методы библиотеки wire, написал вот такой кусок кода:

Wire.begin(); //подключаемся к шине как мастер
Wire.beginTransmission( 0x29 ); //инициализируем отправку ведомому по адресу
//ВОТ ТУТ ПРОБОВАЛ С АДРЕСАМИ И БЕЗ
Wire.endTransmittion(); //завершаем отправку
Wire.requestFrom( 0x29, 1, true ); //запрашиваем 1 байт у ведомого

while (Wire.available()) {
   {Serial.print( Wire.read() );} //выводим то, что прислал ведомый
}

Данные выдаются такие же, что я писал в самом начале. Пробовал отправлять адреса регистров, которые указаны в даташите (скрин ниже), но результата нет.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Заметил такую вещь, что данные датчик отправляет одинаковые

Komandir
Offline
Зарегистрирован: 18.08.2018

Скетч i2c Scanner видит девайс ?

 

NoAlex
Offline
Зарегистрирован: 11.06.2019

Да, находит устройство

Komandir
Offline
Зарегистрирован: 18.08.2018

Значит Ваш код кривоват :-) а железка жива ещё.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Вот где я мог ошибиться?

Komandir
Offline
Зарегистрирован: 18.08.2018

Ваш полный код мы так и не увидели ...

NoAlex
Offline
Зарегистрирован: 11.06.2019

На текущий момент вот весь код 

Wire.begin(); //подключаемся к шине как мастер
Wire.beginTransmission( 0x29 ); //инициализируем отправку ведомому по адресу
//ВОТ ТУТ ПРОБОВАЛ С АДРЕСАМИ И БЕЗ
Wire.endTransmittion(); //завершаем отправку
Wire.requestFrom( 0x29, 1, true ); //запрашиваем 1 байт у ведомого

while (Wire.available()) {
   {Serial.print( Wire.read() );} //выводим то, что прислал ведомый
}

 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Если это весь, то он не компилируется :(

Ты уверен, что между строками 5 и 7 данные успевают прийтие? Не хоцешь поставить между ними 

while (!Wire.available());

NoAlex
Offline
Зарегистрирован: 11.06.2019

Подключена библиотека Wire, #include "Wire.h". Больше нет причин, чтобы не запускаться.

Добавлял ожидание (вот сейчас добавил твой кусок кода "while (!Wire.available());"), результат тот же.

Feofan
Offline
Зарегистрирован: 28.05.2017

Попробуйте такое:

#include <Wire.h>

void exch() {
  Serial.println("begin exch()");
  Wire.begin();
  Wire.beginTransmission(0x29);
  if (Wire.endTransmission() == 0) {
    if (Wire.requestFrom(0x29, 1) == 0) {
      Serial.println("OK requestFrom");
      while (Wire.available()) {
        Serial.println(Wire.read(), HEX);
      }
    } else Serial.println("Error requestFrom");
  } else Serial.println("Error endTransmission");
  Serial.println("end exch()");
}

void setup() {
  Serial.begin(38400);
  while (!Serial);
  Serial.println("Go");
  exch();
}

void loop() {}

 

Komandir
Offline
Зарегистрирован: 18.08.2018

Вам надо курить документацию и чужой код на этот модуль - там всё не тривиально !!!

Wire.requestFrom( 0x29, 1, true );

Что означает 1 ? Для меня и для железки это звучит как запросить информацию из регистра 0x01, а для Вас ?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Komandir пишет:

Вам надо курить документацию и чужой код на этот модуль - там всё не тривиально !!!

Wire.requestFrom( 0x29, 1, true );

Что означает 1 ? Для меня и для железки это звучит как запросить информацию из регистра 0x01, а для Вас ?

Ты бы хоть описание на нее почитал, прежде чем ляпать. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017
wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Komandir пишет:

Вам надо курить документацию и чужой код на этот модуль - там всё не тривиально !!!

Wire.requestFrom( 0x29, 1, true );

Что означает 1 ? Для меня и для железки это звучит как запросить информацию из регистра 0x01, а для Вас ?

 

Для умеющих читать... пусть медленно и по-сло-гам, "1" обозначает количество запрашиваемых байт. ;)))

============================

Syntax

Wire.requestFrom(address, quantity)
Wire.requestFrom(address, quantity, stop)

Parameters

address: the 7-bit address of the device to request bytes from

quantity: the number of bytes to request

stop : boolean. true will send a stop message after the request, releasing the bus. false will continually send a restart after the request, keeping the connection active.

===========================

Другое дело, что ТС никак не читает, то что запрашивает ;)))

Вот пример из библиотеки для этого прибора, как читать регистр (8ми битный)

uint8_t VL53L0X::readReg(uint8_t reg)
{
  uint8_t value;

  Wire.beginTransmission(address);
  Wire.write(reg);
  last_status = Wire.endTransmission();

  Wire.requestFrom(address, (uint8_t)1);
  value = Wire.read();

  return value;
}

 

Komandir
Offline
Зарегистрирован: 18.08.2018

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

uint8_t VL53L0X::readReg(uint8_t reg)
{
  uint8_t value;

  Wire.beginTransmission(address);
  Wire.write(reg);
  last_status = Wire.endTransmission();

  Wire.requestFrom(address, (uint8_t)1);
  value = Wire.read();

  return value;
}

ТС просить прислать "то, не знаю что" ...

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

2ТС:

Вот сцылко на github с библиотекой под дальномер. Посмотри на инициализацию и реши: точно ли тебе нужно строить велосипед самому? Может стоит воспользоваться готовым? Если это вдруг, зачем-то, будет надо, то wire легко заменяется на software i2c в теле библиотеки.

NoAlex
Offline
Зарегистрирован: 11.06.2019

Я по этому поводу уже писал... В документации написано, что при запросе данных номер регистра можно не указывать, в таком случае датчик вернёт расстояние до объекта, но если запросить (адреса регистров я прислал выше), то он выдаёт данные. Я пробовал запрашивать, по некоторым регистрам данные шлёт одни и те же, слдеовательно, датчик отвечает. Если для получения расстояния нужно запросить данные из регистра, то почему его адреса нет в даташите? Может я просмотрел...

NoAlex
Offline
Зарегистрирован: 11.06.2019

Вывод

begin exch()
Error requestFrom
end exch()

NoAlex
Offline
Зарегистрирован: 11.06.2019

Для решения проблемы мне было бы достаточно адреса регистра с расстоянием, так как логику обмена данными я понял. Если будет не сложно, то можно источник, где этот адрес написан. Даташит читал тут https://www.st.com/resource/en/datasheet/vl53l0x.pdf , но в упор не вижу адреса регистра для чтения расстояния :(

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Это не весь даташит, отдельно есть описание API на 700 с с гаком страниц. Там несколько больше ;)))) регистров.

Почему ты никак не можешь открыть код библиотеки, любой... и посмотреть КАК используется дальномер. Для получения расстояний в непрерывном режиме, его нужно в этот режим перевести, откалибровать и т.п. Вот зачем велосипед строить??? Посмотри код. Изучи API, возможно это даст тебе знания, чтобы уменьшить размер кода инициализации.

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

 

Logik
Offline
Зарегистрирован: 05.08.2014

NoAlex пишет:

Для решения проблемы мне было бы достаточно адреса регистра с расстоянием, так как логику обмена данными я понял. 

Счасливчик;) Я там выше писал про особенность софтового i2c, но не глянул что оно для VL53L0X пишется. То правда что я писал, оно универсально. Но VL53L0X... Я и сам любитель свои либы писать для железяк разных, форум не даст мне соврать. Как правило оно оправдывается. Но VL53L0X  очень не прост. Я запускал его, он работал. Я "курил" распостраненную либу и даташит, и как докурил (а это совсем не с первого раза вышло) и понял чего делается,  то мне четокакто расхателось свою либу на него писать. И это имея свою софтовую и сто раз провереную либу на просто шину i2c.  Еще раз - VL53L0X  очень не прост. Но если охота пуще неволи, то надо какбы декомпозировать задачку: сразу таки сделать либу на просто шину i2c, с экранами и датчиками всякими потестить, а потом уж за VL53L0X братся.

 

NoAlex
Offline
Зарегистрирован: 11.06.2019

А можешь кинуть ссылку на полное api?

Отбой, нашёл вроде, но там всего 157 страниц :-)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

NoAlex пишет:

А можешь кинуть ссылку на полное api?

Отбой, нашёл вроде, но там всего 157 страниц :-)

Не успел ;)). Регистрируешься на ST и качаешь оттуда - самый верный путь.

 

NoAlex
Offline
Зарегистрирован: 11.06.2019

Значит скачал с нужного источника, тоже регистрировался и т.д...

DIVGENY
Offline
Зарегистрирован: 23.08.2016

NoAlex пишет:

Да, находит устройство

страно, даташит говорит другое

https://pdf1.alldatasheet.com/datasheet-pdf/view/948120/STMICROELECTRONICS/VL53L0X.html

 VL53L0X I2C device address: 0x52

это я к чему - адрес задаете со смещением, так и обращайтесь к регистрам со смещением, а не по даташиту...