На досуге разбирался с библиотекой
Wire на предмет возможности работать по
I2C в режиме REPEATED START. Вроде бы, разобрался ;)
I2C привлекательна тем, что в ней всего два сигнальных провода, на которые вешается произвольное количество устройств. Операцию обмена начинает и заканчивает ведущее устройство (Master). Ведомое (Slave) устройство выдает данные на шину только тогда, когда этого хочет Master.
Пример записи:
Пример чтения:
В начале любой операции Master выставляет START CONDITION, передает 7-битный адрес устройства (на одной шине их может быть несколько, и все с разными адресами), а также бит операции (чтение или запись).
В режиме Master Transfer следом передается серия байт согласно протоколу общения с устройством. Например, если Slave - это AT24C512 (EEPROM 512Кбит), то следующие
два байта трактуются как адрес ячейки, куда дальше пойдет чтение/запись. Но это не догма:
DS1307 (часы реального времени) нужен всего
один байт (внутри 64 ячейки, и одного байта с запасом хватает для их адресации). Для некоторых устройств может идти код команды, и так далее.
Все передаваемые
байты подтверждаются встречной стороной через ACK (может быть как позитивный ACK, так и негативный NACK). По завершении приема или передачи Master выставляет STOP CONDITION и освобождает шину.
В Arduino запись со сторны Master программируется так:
Wire.beginTransmission(address)Wire.send(data);Wire.send(data);...Wire.endTransmission();Пусть вас не вводят в заблуждение "интуитивные" названия функций! ;)
beginTransmission не начинает передачу, а просто запоминает адрес устройства и инициализирует буфер передачи,
send ничего в линию не передает, а добавляет в этот буфер байтики (следите, чтобы не было больше 32 байт, именно такого размера буфер в Arduino), и только
endTransmission() дает команду аппаратной части TWI (так ATMEL назвал поддержку I2C в своих МК) передать скопившийся буфер на шину. Учтите, что передача будет "блокирующей", т.е. пока она не закончится, управление не перейдет на следующую строку вашего sketch-а.
В режиме приема со стороны Master достаточно одного вызова (также блокирующего):
Wire.requestFrom(address,quantity);Функция очистит буфер приема, даст команду МК в режиме Master выставить START CONDITION, передать адрес и бит-флаг "чтение", затем будет ждать получения указанного числа байт; Получив их, МК "успокоится", выставит шину в STOP CONDITION и освободит ее.
В sketch-е можно получить данные побайтно
Wire.receive(), на всякий случай осведомившись методом
Wire.available() о размере приемного буфера.
Вроде бы все логично, но, как я говорил - Slave-устройства бывают разные. Иногда они используют режим REPEATED START:
Как видите, все начинается как обычная операция записи, но вместо того, чтобы перевести шину в состояние STOP CONDITION, Master повторно командует START (это называется REPEATED START) и продолжает чтением.
Получается, что сначала передается адрес или команда устройству, а затем устройство отвечает, так сказать "не отходя от кассы". Проблема Arduino заключается в том, что
стандартная библиотека этого не умеет, и по завершению передачи данных она переведет шину в состояние STOP CONDITION, а затем и вообще освободит её. Дальше все зависит от устройства: оно может как
запомнить полученную информацию (так часто и бывает - EEPROM заносит обновляет внутренний счетчик адреса, который использует в последующих операциях чтения), так и
сброситься, оставив вас "с носом". Я уже не говорю о ситуациях, когда на шине несколько Master-устройств, и "коллега" может успеть захватить шину межу записью и чтением.
Что же делать?
Выхода два: отказаться напрочь от использования стандартной библиотеки Wire, например, в пользу либы
Peter Fleury, либо модифицировать стандартную.
В результате изучения кода и медитаций над общей схемой построения библиотеки Wire, родилась дополнительная функция:
uint8_t TwoWire::transmitAndRequest(uint8_t quantity)ее надо использовать
вместо endTransmission():
Wire.beginTransmission(address)Wire.send(data);Wire.send(data);...Wire.transmitAndRequest(quantity);Эта комбинация передаст накопившееся в буфере передачи, а затем будет запущен прием через REPEATED START.
Вернется число прочитанных байт. Об ошибке будет сигнализировать значение > 127, тогда надо сбросить старший бит (0x80) и трактовать код ошибки идентично коду возврата
endTransmission().
Предлагаю заценить результат (модификация библиотеки Wire из Arduino IDE 0014), его надо распаковать в Arduino/hardware/libraries (я исключил examples):
Wire.zip.
К сожалению, под рукой нет устройства, которое бы строго требовало REPEATED START, буду благодарен добровольцам, которые не поленятся проверить ;) Для микросхемы часов
DS1307 - работает.