Официальный сайт компании Arduino по адресу arduino.cc
Реализация I2C
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Привет
Имеются следующие компоненты:
- 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 микросекунд)
Но протестировав код, я увидел, что от датчика приходит какая-то ерунда, причём иногда (очень редко) показания правильные. Есть подозрение, что что-то с таймингами или неверно/не в нужное время посылаю какой-то сигнал.
Прошу помощи, не знаю что пробовать, в какую сторону смотреть.
Примеры ответов, чтобы не быть голословным:
- 00000000
- 11111111
- 00000001
- 01111111
- 10000000
библиотэка Wire тебя чем не устраивает?
Я хочу реализовать свои библиотеки, поэтому не использую другие
Мне одному показалось, что как-то Великим запахло? Никак очередной "апостол" будет "письма" постить.
digitalWrite() с лёгкостью испоганит любые критичные тайминги.
digitalWrite() с лёгкостью испоганит любые критичные тайминги.
В данном протоколе тайминг определяется линией scl, и нет жёстких требований по таймингам, поэтому некритично.
В данном протоколе тайминг определяется линией scl, и нет жёстких требований по таймингам, поэтому некритично.
в данном протоколе, матьтваю, если он реализован ПРАВИЛЬНО, еще и прерывания есть.
возьми да ознакомься https://playground.arduino.cc/Main/SoftwareI2CLibrary/
digitalWrite() с лёгкостью испоганит любые критичные тайминги.
В данном протоколе тайминг определяется линией scl, и нет жёстких требований по таймингам, поэтому некритично.
Тогда берёте логический анализатор и пишете логи: закидывания через I2C данных стандартным Wire и своими процедурами. Потом сравниваете и смотрите где несовпадение.
в данном протоколе, матьтваю, если он реализован ПРАВИЛЬНО, еще и прерывания есть.
возьми да ознакомься https://playground.arduino.cc/Main/SoftwareI2CLibrary/
Я под таймингом имел ввиду длительность тактового синхроимпульса, может этим запутал.
Про прерывания знаю, я вообще про это не спрашивал :-)
ну так посмотри, как сделан SoftI2C у буржуев, ссылку я тебе дал, да поймешь, мошт, чо ты не так делаешь.
В теме про тиньку13 Влад давал его реализацию i2c. Работает хорошо.
Для реализации каждого этапа написал небольшие функции:
СТАРТ сигнал
..........
От вот это - неверный подход. И дело даже не в digitalWrite, которые медленные, на что правильно указано. Дело в том, что I2C построенна на логике состояний линия либо в пассивном - подтянута к + резистором, либо активна - один из участников обмена открытым ключем притянул к земле. А для этого нужно чтоб линия устанавливалась не в 1 и 0, а в пассивное состояние или 0. Обычно это делается так: пассивное ставится pinMode(sda, INPUT);, а активное - pinMode(sda, OUTPUT) при том что digitalWrite(..., LOW);. Так как у Вас только в наипростейшем случае будет работать, пока не потребуется контроль ACK и/или мультимастер.
Для реализации каждого этапа написал небольшие функции:
СТАРТ сигнал
..........
От вот это - неверный подход. И дело даже не в digitalWrite, которые медленные, на что правильно указано. Дело в том, что I2C построенна на логике состояний линия либо в пассивном - подтянута к + резистором, либо активна - один из участников обмена открытым ключем притянул к земле. А для этого нужно чтоб линия устанавливалась не в 1 и 0, а в пассивное состояние или 0. Обычно это делается так: пассивное ставится pinMode(sda, INPUT);, а активное - pinMode(sda, OUTPUT) при том что digitalWrite(..., LOW);. Так как у Вас только в наипростейшем случае будет работать, пока не потребуется контроль ACK и/или мультимастер.
Я понял, попробую
В теме про тиньку13 Влад давал его реализацию i2c. Работает хорошо.
вот она, сообщение 966 в ветке про Тиньку13.
Можно и сюда скопировать, дисков на форуме пока хватает... ;))
С тех пор прошли годы... где-то есть с атомарными чтениями, с ожидаем медленного слейва и прочим... но эта была первая, нужно было впихнуть ее в тиньку и чтобы еще место осталось...
При использовании с Уно-Нано нужно убрать "тиньковые" особенности: delayMicroseconds() - совершенно нормально идет вместо самописного, digitalWrite() - тоже нормально пойдет для низкой скорости. Часы и 1602 экран - работают. И2С вообще довольно некритичный протокол на низких скоростях. Но софт И2С на 328 контроллере - это полная дурь, потому, что на нем есть АППАРАТНЫЙ и2с. Только если какой нибудь хаб городить... Короче - число мастурбационные задачи, не практические.
В теме про тиньку13 Влад давал его реализацию i2c. Работает хорошо.
вот она, сообщение 966 в ветке про Тиньку13.
Можно и сюда скопировать, дисков на форуме пока хватает... ;))
С тех пор прошли годы... где-то есть с атомарными чтениями, с ожидаем медленного слейва и прочим... но эта была первая, нужно было впихнуть ее в тиньку и чтобы еще место осталось...
При использовании с Уно-Нано нужно убрать "тиньковые" особенности: delayMicroseconds() - совершенно нормально идет вместо самописного, digitalWrite() - тоже нормально пойдет для низкой скорости. Часы и 1602 экран - работают. И2С вообще довольно некритичный протокол на низких скоростях. Но софт И2С на 328 контроллере - это полная дурь, потому, что на нем есть АППАРАТНЫЙ и2с. Только если какой нибудь хаб городить... Короче - число мастурбационные задачи, не практические.
Я чисто попробовать реализовать самому, всегда успею перейти на аппаратную работу по i2c. Эксперементировал по разному, ни в какую не работал, в итоге решил взять код выше. Датчик не отвечает после отправки адреса. Цепанул к цифровым пинам 8 и 9, меняю их статусы напрямую.
Что ещё может быть? Могу выложить весь текущий код.
Никто не ответит? :(
Если это поможет решить проблему, то датчик покупал тут https://ru.aliexpress.com/item/VL53L0X-ToF-940nm-GY-VL53L0XV2/32848271367.html?spm=a2g0s.9042311.0.0.274233edRFsX8Y
Такой ощущение, что датчик притянул линию SDA для подтверждения того, что адрес был его и он готов писать, но дальше дело не пошло. Вот код отправки байта и чтения бита подтверждения
А вот код для чтения байта и отправки бита подтверждения
Повторюсь, функции взял из кода выше, а результат такой же, как и был.
Если использовать библиотеку для работы с этим датчиком от adafruit, то показания снимаются правильные, то есть датчик рабочий, но эта библиотека работает с аппаратной реалзиацией протокола.
NoAlex, провенрьте сначала на аппаратном I2C, и только когда будете уверены, что датчик работает, переводите его на программный.
NoAlex, провенрьте сначала на аппаратном I2C, и только когда будете уверены, что датчик работает, переводите его на программный.
Проверено, работает
Напиши код с использованием стандартных аппаратных фунций И2С requestFrom(), beginTranmission() и т.д. А когда заработает, замени их на софтверные.
Повторю: НЕ с библиотекой адафрут, а только с голым Wire.h. И еще - будь внимателен с задержками. Свое понимание проверь на чем-то проще датчика - на часах 3132 или экране 1602 на И2С. Оба очень нетребовательны. Там же, в ветке про тиньку, пример работы с часами (вроде был), это похоже на твой датчик - тоже нужно вычитывать из регистра.
------------------------------------
Если что, у меня где-то есть такой датчик. Не обещаю, но может и повожусь с ним, я его еще не распаковыавал даже, потому как не могу понять, нафиг он мне нужен... ;)))))
Кстати, по поводу регистра (хорошо, что ты сказал про это :)), я верно понял, что для работы с этим датчиком не надо слать адрес регистра после подтверждения адреса датчиком, чтобы считать данные? По даташиту есть адреса, но для считывания расстояния они вроде не нужны.
Появилось время для продолжения экспериментов :-).
wdrakula, к сожалению у меня нет ни часов, ни экрана, поэтому продолжаю работать с датчиком.
Посмотрел методы библиотеки wire, написал вот такой кусок кода:
Данные выдаются такие же, что я писал в самом начале. Пробовал отправлять адреса регистров, которые указаны в даташите (скрин ниже), но результата нет.
Заметил такую вещь, что данные датчик отправляет одинаковые
Скетч i2c Scanner видит девайс ?
Да, находит устройство
Значит Ваш код кривоват :-) а железка жива ещё.
Вот где я мог ошибиться?
Ваш полный код мы так и не увидели ...
На текущий момент вот весь код
Если это весь, то он не компилируется :(
Ты уверен, что между строками 5 и 7 данные успевают прийтие? Не хоцешь поставить между ними
while
(!Wire.available());
Подключена библиотека Wire, #include "Wire.h". Больше нет причин, чтобы не запускаться.
Добавлял ожидание (вот сейчас добавил твой кусок кода "
while
(!Wire.available());"
), результат тот же.Попробуйте такое:
Вам надо курить документацию и чужой код на этот модуль - там всё не тривиально !!!
Wire.requestFrom( 0x29, 1,
true
);
Что означает 1 ? Для меня и для железки это звучит как запросить информацию из регистра 0x01, а для Вас ?
Вам надо курить документацию и чужой код на этот модуль - там всё не тривиально !!!
Wire.requestFrom( 0x29, 1,
true
);
Что означает 1 ? Для меня и для железки это звучит как запросить информацию из регистра 0x01, а для Вас ?
Ты бы хоть описание на нее почитал, прежде чем ляпать.
Первоисточнег
https://www.arduino.cc/en/Reference/WireRequestFrom
Вам надо курить документацию и чужой код на этот модуль - там всё не тривиально !!!
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ми битный)
Я про то что сначала надо сообщить железке из какого регистра она должна выслать данные, а потом уже что то читать.
ТС просить прислать "то, не знаю что" ...
2ТС:
Вот сцылко на github с библиотекой под дальномер. Посмотри на инициализацию и реши: точно ли тебе нужно строить велосипед самому? Может стоит воспользоваться готовым? Если это вдруг, зачем-то, будет надо, то wire легко заменяется на software i2c в теле библиотеки.
Я по этому поводу уже писал... В документации написано, что при запросе данных номер регистра можно не указывать, в таком случае датчик вернёт расстояние до объекта, но если запросить (адреса регистров я прислал выше), то он выдаёт данные. Я пробовал запрашивать, по некоторым регистрам данные шлёт одни и те же, слдеовательно, датчик отвечает. Если для получения расстояния нужно запросить данные из регистра, то почему его адреса нет в даташите? Может я просмотрел...
Вывод
begin exch()
Error requestFrom
end exch()
Для решения проблемы мне было бы достаточно адреса регистра с расстоянием, так как логику обмена данными я понял. Если будет не сложно, то можно источник, где этот адрес написан. Даташит читал тут https://www.st.com/resource/en/datasheet/vl53l0x.pdf , но в упор не вижу адреса регистра для чтения расстояния :(
Это не весь даташит, отдельно есть описание API на 700 с с гаком страниц. Там несколько больше ;)))) регистров.
Почему ты никак не можешь открыть код библиотеки, любой... и посмотреть КАК используется дальномер. Для получения расстояний в непрерывном режиме, его нужно в этот режим перевести, откалибровать и т.п. Вот зачем велосипед строить??? Посмотри код. Изучи API, возможно это даст тебе знания, чтобы уменьшить размер кода инициализации.
Табличка, которую ты привел, это табличка для проверки работоспособности I2C. ;) о чем в ДШ и написано. Будь внимателен! ДШ, который ты читаешь - это коротенький рассказ о железке и ее электрических и геометрических параметрах. Не об алгоритме работы с ней.
Для решения проблемы мне было бы достаточно адреса регистра с расстоянием, так как логику обмена данными я понял.
Счасливчик;) Я там выше писал про особенность софтового i2c, но не глянул что оно для VL53L0X пишется. То правда что я писал, оно универсально. Но VL53L0X... Я и сам любитель свои либы писать для железяк разных, форум не даст мне соврать. Как правило оно оправдывается. Но VL53L0X очень не прост. Я запускал его, он работал. Я "курил" распостраненную либу и даташит, и как докурил (а это совсем не с первого раза вышло) и понял чего делается, то мне четокакто расхателось свою либу на него писать. И это имея свою софтовую и сто раз провереную либу на просто шину i2c. Еще раз - VL53L0X очень не прост. Но если охота пуще неволи, то надо какбы декомпозировать задачку: сразу таки сделать либу на просто шину i2c, с экранами и датчиками всякими потестить, а потом уж за VL53L0X братся.
А можешь кинуть ссылку на полное api?
Отбой, нашёл вроде, но там всего 157 страниц :-)
А можешь кинуть ссылку на полное api?
Отбой, нашёл вроде, но там всего 157 страниц :-)
Не успел ;)). Регистрируешься на ST и качаешь оттуда - самый верный путь.
Значит скачал с нужного источника, тоже регистрировался и т.д...
Да, находит устройство
страно, даташит говорит другое
https://pdf1.alldatasheet.com/datasheet-pdf/view/948120/STMICROELECTRONICS/VL53L0X.html
VL53L0X I2C device address: 0x52
это я к чему - адрес задаете со смещением, так и обращайтесь к регистрам со смещением, а не по даташиту...