Как прочитать данные из 32-битного адреса регистра I2C?
У меня возникли проблемы с чтением данных I2C из CPS8200, у которого 32-битный адрес регистра. Поскольку я не слишком хорошо знаком с адресами регистров I2C больше 8 бит, и сейчас не уверен, что делаю это правильно. Моя главная цель — иметь возможность читать и записывать данные с адреса регистра 0x2000 003C, и я не уверен, что делаю это правильно, поскольку при чтении данных в Serial Monitor я получаю 0xC4, хотя должен получать 0x0000 0AFF. Я хотел написать здесь, чтобы понять, что я делаю неправильно, и как правильно считывать данные из 32-битного адреса регистра.

#include <Wire.h>
uint32_t FUNC_EN_REG = 0x2000003C;
uint32_t FUNC_EN_REG_DATA;
void setup() {
Wire.begin(4, 5);
Serial.begin(115200);
Wire.beginTransmission(0x30);
Wire.write(0x20);
Wire.write(0x00);
Wire.write(0x00);
Wire.write(0x3C);
Wire.endTransmission();
}
void loop() {
Wire.requestFrom(0x30, 4);
FUNC_EN_REG_DATA = Wire.read();
Serial.println(FUNC_EN_REG_DATA, HEX);
delay(500);
}
@J. Street, 👍0
Обсуждение2 ответа
Вы почти у цели. Строка, где у вас Wire.endTransmission();, на самом деле должна быть
Wire.endTransmission(false);
Как описано в документации:
Метод
endTransmission()принимает логический аргумент, изменяя своё поведение для совместимости с определёнными устройствами I2C. Если значение равно true,endTransmission()отправляет сообщение об остановке после передачи, освобождая шину I2C. Если значение равно false,endTransmission()отправляет сообщение о перезапуске после передачи. Шина не будет освобождена, что предотвращает передачу данных другим контроллером между сообщениями. Это позволяет одному контроллеру отправлять несколько передач, находясь под управлением. Значение по умолчанию — true.
endTransmission(false) имеет решающее значение, поскольку позволяет переключиться с операции записи (отправки адреса регистра) на операцию чтения (получения данных) без освобождения шины.
Когда вы считываете ответ с внешнего устройства, вы не позволяете ему вернуть все 4 байта ответа, пока не запросите ответ снова. Wire.read() необходимо запустить 4 раза, чтобы прочитать 4 байта ответа.
Ваша функция цикла должна выглядеть примерно так:
void loop() {
Wire.requestFrom(0x30, 4); // запрос 4 байта с периферийного устройства #0x30
char a = Wire.read(); // получить байт как символ
Serial.print(a, HEX); // вывести символ в шестнадцатеричном формате
char b = Wire.read(); // получить байт как символ
Serial.print(b, HEX); // вывести символ в шестнадцатеричном формате
char c = Wire.read(); // получить байт как символ
Serial.print(c, HEX); // вывести символ в шестнадцатеричном формате
char d = Wire.read(); // получить байт как символ
Serial.print(d, HEX); // вывести символ в шестнадцатеричном формате
Serial.println("");
delay(500);
}
Или вот так, используя цикл while:
void loop() {
Wire.requestFrom(0x30, 4); // запрос 4 байта с периферийного устройства #0x30
while (Wire.available()) { // периферийное устройство может отправить меньше, чем запрошено
char c = Wire.read(); // получить байт как символ
Serial.print(c, HEX); // вывести символ в шестнадцатеричном формате
}
Serial.println("");
delay(500);
}
Примечание:
Если вы хотите сделать свой код немного более изящным и более надежным, проверьте результат endTransmission() следующим образом:
byte endTransmissionStatus = Wire.endTransmission(false);
if (endTransmissionStatus != 0) {
Serial.print("Error sending register address (Status: ");
Serial.print(endTransmissionStatus);
Serial.println(")");
return 0; // Верните 0 или обработайте ошибку соответствующим образом
}
Значение возвращаемого статуса можно найти на странице документации, ссылка на которую указана выше.
В чем разница между вызовом endTransmission(false) и простым отсутствием вызова endTransmission?, @J. Street
@sa_leinad на самом деле это не всегда так. Некоторые ведомые устройства I2C имеют отдельные команды чтения и записи, требующие освобождения шины с помощью endTransmission(), после чего команда чтения извлекает ранее установленный регистр. Правильный способ для этого устройства указан в документации, поэтому автору придётся либо найти его самостоятельно, либо сообщить нам, что это за устройство., @InBedded16
@J.Street endTransmission(false) отправляет сообщение о перезапуске, а отсутствие вызова — нет. Необходимо отправить сообщение об остановке или перезапуске ведомому устройству, чтобы оно знало, что текущая команда чтения/записи завершена. Ознакомьтесь с документацией, чтобы лучше понять этот нюанс., @InBedded16
Часть о цикле для Wire.read() важна, если вы отредактируете ответ, чтобы отразить мой первый комментарий, я поставлю +1 :), @InBedded16
@InBedded16 Некоторые подчинённые устройства I2C имеют отдельные команды чтения и записи, требующие освобождения шины с помощью endTransmission(), после чего команда чтения извлечёт ранее заданный регистр. На самом деле, _все_ подчинённые устройства I2C требуют этого, и _все_ подчинённые устройства I2C должны сохранять текущий номер регистра (возможно, с автоинкрементом) до выключения питания. Запуск без остановки I2C — хорошая и рекомендуемая практика, но она не критична, если только не используется конфигурация шины I2C с несколькими ведущими устройствами., @Matt
Ответ на ваш вопрос содержится на рис. 8 документа, ссылку на который вы дали.
Вот порядок выполнения команд I2C. Сначала AW:30, то есть beginTransmission(0x30), запускающий передачу записи. Затем DW:FF DW:FF DW:FF DW:00, 4 байта адреса регистра, используя MSBFirst. Эту часть вы уже выполнили правильно. Вам нужен endTransmission(), потому что:
Эта функция завершает передачу на периферийное устройство, начатую функцией beginTransmission(), и передает байты, поставленные в очередь функцией write().
Затем, не освобождая линии шины, он переходит прямо в AR:30, что является началом передачи чтения. Поэтому вашей функции endTransmission() следует передать аргумент false, чтобы не освобождать шину в промежутке.
Далее следует DR:0E DR:00 DR:00 DR:00, который вы получаете с помощью wire.requestFrom(0x30, 4), а затем wire.Read() 4 раза, по одному для каждого байта. Обязательно сохраняйте каждый байт или используйте отдельную переменную для каждого чтения, не перезаписывайте их при следующем чтении. Обратите внимание, что правильное значение для этого регистра должно быть 0x0000000E. Данные передаются с использованием нотации LSBFirst. Кстати, если вы хотите записать данные в регистр (см. рис. 7 или 9), вы также должны передавать данные в LSBFirst, но адреса регистров всегда передаются с помощью MSBFirst.
Затем просто завершите с помощью endTransmission()!
Кроме того, немного неясно, нужно ли выполнять шаги 1–3 (рис. 7–9) для чтения регистров или только для записи в них, но если у вас всё ещё нет успеха, то, возможно, проблема именно в этом.
- LCD дисплей не показывает
- Соединение I2C с модулем камеры MT9D111, странные результаты после записи регистров через i2C
- Работа с основной библиотекой Wire
- Проблема с использованием выводов A4 и A5 для ввода и вывода I2C
- Горячая замена датчика
- Как назвать датчики температуры вместо адреса печати с помощью датчиков ds2482-100 и ds18b20 - Справка
- Сколько читаемого текста может поместиться в ATTINY85? Возможность прокрутки буклета на 0,91-дюймовом OLED-дисплее
- Как запретить подчиненному устройству выполнять код до тех пор, пока мастер не отправит новое значение? I2C
Это не часть спецификации I2C. Ознакомьтесь с техническим описанием вашей микросхемы (той, которую вы не называете по какой-то непонятной причине). Она может быть 20-00-00-3c или 3c-00-00-20, кто знает?, @Matt
Чтобы донести мысль Мэтта: пожалуйста, дайте ссылку на документацию или расскажите, что это за устройство! На ваш вопрос нет универсального ответа., @InBedded16
@InBedded16 Принял к сведению, пост обновлен., @J. Street