Запись в определенный регистр I2C
Я пытаюсь записать в определенный регистр, но значение не меняется.
Работа с SenseAir Sunrise CO2: Информация из руководства i2c от производителя датчиков https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE5531.pdf
Период измерения (EE) 0x96 (MSB) 0x97 (LSB)
Период измерения в секундах (диапазон от 2 до 65534). Нечетные числа будут округлены до ближайшего четного числа. После изменения конфигурации требуется сброс системы. Значение по умолчанию равно 16.
И это мой фрагмент кода
#include <Wire.h>
/* ПРЕДУПРЕЖДЕНИЕ!!!
Некоторые реализации драйверов проводов не полностью реализуют функцию Wire.endTransmission(false),
поэтому, пожалуйста, проверьте это, прежде чем отключать WIRE_WORKAROUND!
Например, известными "плохими" реализациями являются: Nucleo STM32
*/
#define WIRE_WORKAROUND (0)
#define LED_BUILTIN 2
/* Определение последовательного EN pin */
const int SUNRISE_EN = 4;
/* Адрес связи Sunrise, как для Modbus, так и для I2C */
const uint8_t SUNRISE_ADDR = 0x68;
/* Количество попыток пробуждения до тайм-аута */
const int ATTEMPTS = 5;
/* It takes 25ms to write one EE register */
const int EEPROM_UPDATE_DELAY_MS = 25;
/* Register Addresses */
const uint8_t ERROR_STATUS = 0x01;
const uint8_t MEASUREMENT_MODE = 0x95;
const uint8_t METER_CONTROL = 0xA5;
/* Период считывания, в миллисекундах. Значение по умолчанию-4 секунды */
int readPeriodMs = 2000;
void setup()
{
/* I2C */
/* Инициализируйте I2C и используйте контакты по умолчанию, определенные для платы */
reInitI2C();
Serial.begin(115200);
Serial.println("Initialization complete\n");
change_measurement_period(SUNRISE_ADDR);
/* Прочитать настройки датчика */
Serial.println("Конфигурации датчиков измерения:");
read_sensor_config(SUNRISE_ADDR);
Serial.println();
}
void change_measurement_period(uint8_t target) {
/* Function variables */
int error;
/* Пробуждение */
if(!(_wakeup(target))) {
Serial.print("Не удалось разбудить датчик".);
/* FATAL ERROR */
while(true);
}
Serial.println("Changing Measurement Period...");
uint16_t mPeriod = 12;
Wire.beginTransmission(target);
Wire.write(0x96);
Wire.write(highByte(mPeriod)); // отправляет MSB
Wire.write(lowByte(mPeriod)); // отправляет LSB
error = Wire.endTransmission(true);
delay(EEPROM_UPDATE_DELAY_MS);
if(error != 0) {
Serial.print("Не удалось отправить запрос. Код ошибки: ");
Serial.println(error);
/* FATAL ERROR */
while(true);
}
Serial.println("Для применения изменений требуется перезапуск датчика\n\n\n");
}
/**
* @brief Считывает и печатает текущий режим измерения датчика,
* период измерения и количество образцов.
*
* @param target: адрес связи датчика.
* @note В этом примере показан простой способ считывания показаний датчика.
* конфигурации измерений.
* @retval Нет
*/
void read_sensor_config(uint8_t target) {
/* Переменные функции */
int error;
int numBytes = 7;
/* Пробуждение */
if(!(_wakeup(target))) {
Serial.print("Не удалось разбудить датчик".);
return;
}
/* Запрашиваемые значения */
error = WireRequestFrom(target, numBytes, MEASUREMENT_MODE /* from address*/, true /* STOP*/);
if(error != numBytes ) {
Serial.print("Не удалось записать в цель. Код ошибки : ");
Serial.println(error);
return;
}
/* Считывание значений */
/* Режим измерения */
uint8_t measMode = Wire.read();
/* Период измерения */
uint8_t byteHi = Wire.read();
uint8_t byteLo = Wire.read();
uint16_t measPeriod = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
/* Количество образцов */
byteHi = Wire.read();
byteLo = Wire.read();
uint16_t numSamples = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
/* ABCPeriod */
byteHi = Wire.read();
byteLo = Wire.read();
uint16_t abcPeriod = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;
/* Наиболее вероятно, что датчик не перейдет в спящий режим, но будет отключен...*/
/* Пробуждение */
if(!(_wakeup(target))) {
Serial.print("Не удалось разбудить датчик".);
return;
}
/* Запрашивать значения */
error = WireRequestFrom(target, 1, METER_CONTROL /* from address*/, true /* STOP*/);
if(error != 1 ) {
Serial.print("Не удалось записать в цель. Код ошибки : ");
Serial.println(error);
return;
}
uint8_t meterControl = Wire.read();
Serial.print("Measurement Mode: ");
Serial.println(measMode);
readPeriodMs = measPeriod * 1000;
Serial.print("Measurement Period, sec: ");
Serial.println(measPeriod);
Serial.print("Number of Samples: ");
Serial.println(numSamples);
if((0U == abcPeriod) || (0xFFFFU == abcPeriod) || (meterControl & 0x02U)) {
Serial.println("ABCPeriod: disabled");
} else {
Serial.print("ABCPeriod, hours: ");
Serial.println(abcPeriod);
}
Serial.println("MeterControl: ");
Serial.println("Bit [0]:" + (String)bitRead(meterControl,0));
Serial.println("Bit [1]:" + (String)bitRead(meterControl,1));
Serial.println("Bit [2]:" + (String)bitRead(meterControl,2));
Serial.println("Bit [3]:" + (String)bitRead(meterControl,3));
Serial.println("Bit [4]:" + (String)bitRead(meterControl,4));
Serial.println("Bit [5]:" + (String)bitRead(meterControl,5));
Serial.println("Bit [6]:" + (String)bitRead(meterControl,6));
Serial.println("Bit [7]:" + (String)bitRead(meterControl,7));
Serial.println(meterControl, BIN);
}
/* Initialize I2C bus and pins */
void reInitI2C() {
/* Initialize I2C and use default pins defined for the board */
Wire.begin();
/* Setup I2C clock to 100kHz */
Wire.setClock(100000);
}
/* Обходной путь, касающийся НЕПРАВИЛЬНОЙ реализации функции Wire.endTransmission(false) */
int WireRequestFrom(uint8_t dev_addr, uint8_t bytes_numbers, uint8_t offset_to_read, bool stop) {
int error;
#if (WIRE_WORKAROUND == 1)
error = Wire.requestFrom((uint8_t)dev_addr, (uint8_t)bytes_numbers /* сколько байтов*/, (uint32_t)offset_to_read /* from address*/, (uint8_t)1/* Address size - 1 byte*/, stop /* STOP*/);
#else
Wire.beginTransmission(dev_addr);
Wire.write(offset_to_read); //адрес начального регистра, с которого считываются данные
Wire.endTransmission(false);
error = Wire.requestFrom((uint8_t)dev_addr, (uint8_t)bytes_numbers /* сколько байтов */, (uint8_t)stop /* STOP*/);
#endif
return error;
}
/**
* @brief Пробуждает датчик, инициализируя операцию записи
* без каких-либо данных.
*
* @param target: адрес связи датчика
* @note В этом примере показан простой способ разбудить датчик.
* @retval true в случае успеха, false в случае неудачи
*/
bool _wakeup(uint8_t target)
{
int attemps = ATTEMPTS;
int error;
do {
uint8_t byte_0;
/* */
Wire.beginTransmission(target);
error = Wire.endTransmission(true);
} while(((error != 0 /*success */) && (error != 2 /*Получен NACK при передаче адреса*/) && (error != 1 /* BUG in STM32 library*/)) && (--attemps > 0));
/* Драйвер STM32 может складываться при некоторых условиях */
if(error == 4) {
/* Повторная инициализация I2C*/
reInitI2C();
return false;
}
return (attemps > 0);
}
void loop() {
// put your main code here, to run repeatedly:
}
В основном я использую этот код, но в нем нет фрагмента с записью случайного числа в регистре MSB&LSB: https://github.com/Senseair-AB/Sunrise-Examples---Arduino/blob/master/examples/i2c/sunrise_i2c_continuous/sunrise_i2c_continuous.ino
я сбрасываю аппаратное обеспечение датчика(физически нажимаю кнопку сброса и отключаю питание от датчика) после записи данных, а также жду, пока не будут записаны все байты
delay(EEPROM_UPDATE_DELAY_MS);
Также я прочитал конфигурацию датчика после запуска и вижу, что значения регистров не меняются.
Проблема в функции change_measurement_period
Что я делаю не так? может быть, кто-нибудь может помочь с этим?
@Dmytro Kochuk, 👍1
Обсуждение2 ответа
Как написано на странице 14 связанного документа (выделено мной):
Все новые записанные данные для регистрации (EE) могут быть считаны обратно после завершения сброса датчика.
Но кажется, что вы не сбрасываете датчик.
Вам нужно будет подождать, как описано в документации, пока не будут записаны все байты.
я сбрасываю аппаратное обеспечение датчика(физически нажимаю кнопку сброса и отключаю питание от датчика) после записи данных, а также жду, пока все байты будут записаны с задержкой(EEPROM_UPDATE_DELAY_MS); Также я прочитал конфигурацию датчика после запуска и вижу, что значения регистров не меняются. Я использую этот код: https://github.com/Senseair-AB/Sunrise-Examples---Arduino/blob/master/examples/i2c/sunrise_i2c_continuous/sunrise_i2c_continuous.ino, @Dmytro Kochuk
Ну, вы не показали этого в своем вопросе. Пожалуйста, добавьте эту важную информацию, отредактировав свой вопрос. -- Впрочем, тогда это уже другая проблема., @the busybee
ОК. Я думаю, что это может быть проблемой в моей функции записи для регистрации, я никогда не буду об этом позже., @Dmytro Kochuk
я добавил полный пример кода, может быть, вы видите, в чем может быть проблема, @Dmytro Kochuk
Моей главной целью было использовать возможность получить значение, компенсируемое атмосферным давлением. Но для этого нужно было понять, как писать в регистрах.
И не понял, в чем была проблема в примерах кода, предоставленных производителем, он неправильно работал с записью для регистрации данных в 2 байта или я сделал что-то не так (скорее всего). Поэтому я переписал его с помощью человека с форума Arduino, и теперь все работает нормально.
//Запишите значение давления в регистр BAROMETRIC_PRESSURE_VALUE
int16_t co2Pre = 9772; //977.2 hPa
// Пробуждение - команда записи на адрес I2C датчика.
Wire.beginTransmission(SUNRISE_ADDR);
Wire.endTransmission(); // игнорируем возвращаемое значение
Wire.beginTransmission(SUNRISE_ADDR);
Wire.write(BAROMETRIC_PRESSURE_VALUE);
Wire.write(highByte(co2Pre)); // отправляет MSB
Wire.write(lowByte(co2Pre)); // отправляет LSB
error = Wire.endTransmission(true);
delay(50);
if( error != 0) Serial.println( "ОШИБКА, не удается найти датчик");
int WireRequest(byte target, byte regAddr, int numBytes) {
// Пробуждение - команда записи на адрес I2C датчика.
Wire.beginTransmission(target);
Wire.endTransmission(); // игнорируйте возвращаемое значение
// Установить адрес регистра датчика
Wire.beginTransmission(target);
Wire.write(regAddr); // адрес регистра
int error = Wire.endTransmission(false); // ложь при повторном запуске
if( error != 0) {
Serial.println( "ОШИБКА, не удается найти датчик");
return 1;
}
// Считать температуру (два байта, представляющие целое число со знаком)
int n = Wire.requestFrom(target, numBytes); // запрос 2 байта
if( n != numBytes) {
Serial.println( "ОШИБКА, не удается прочитать данные");
return 1;
}
return 0;
}
//read BAROMETRIC_PRESSURE_VALUE
error = WireRequest(SUNRISE_ADDR, BAROMETRIC_PRESSURE_VALUE, 2);
if(error != 0) {
Serial.println( "Failed to write to target.");
while(true);
}
msb = (byte) Wire.read();
lsb = (byte) Wire.read();
int16_t co2Pressure = word( msb, lsb);
float co2PressureValue = float( co2Pressure) / 10;
Serial.println("ЗНАЧЕНИЕ БАРОМЕТРИЧЕСКОГО ДАВЛЕНИЯ:" + (String) co2PressureValue);
А также рабочие фрагменты кода для записи регистров, которым требуется перезапуск датчика, позже я физически перезапущу датчик, но теперь делаю это в коде.
// Пробуждение - команда записи на адрес I2C датчика.
Wire.beginTransmission(SUNRISE_ADDR);
Wire.endTransmission(); // игнорируйте возвращаемое значение
Wire.beginTransmission(SUNRISE_ADDR);
Wire.write(MEASUREMENT_PERIOD);
Wire.write(highByte(mPeriod)); // отправляет MSB
Wire.write(lowByte(mPeriod)); // отправляет LSB
error = Wire.endTransmission(true);
delay(50);
if( error != 0) Serial.println( "ОШИБКА, не удается найти датчик");
//ПЕРЕЗАПУСК ДАТЧИКА
// Пробуждение - команда записи на адрес I2C датчика.
Wire.beginTransmission(SUNRISE_ADDR);
Wire.endTransmission(); // игнорируйте возвращаемое значение
Wire.beginTransmission(SUNRISE_ADDR);
Wire.write(SENSOR_RESTART_SCR);
Wire.write(0xFF);
error = Wire.endTransmission(true);
if( error != 0) Serial.println( "ОШИБКА, не удается найти датчик");
Serial.println ("Перезапуск датчика\n\n для применения изменений\n\n");
delay(2500); //Подождать после перезапуска датчика
- Как выбрать альтернативные контакты I2C на ESP32?
- I2C связь между Arduino Uno и Nodemcu32-s (ESP32)
- PN532 не обнаруживает RFID-карту при подключении к ESP32 в режиме I2C, но отлично работает с Arduino Uno
- Как закрыть/закончить Wire.begin()?
- Лучший способ отправки команд I2C между Arduino и ESP32
- Не могу отключить внутренний интерфейс I2C подтягиваний на ESP32
- DS3231 RTC не работает с ESP32
- Как установить подсветку дисплея 16x2 LCD I2C?
Вы не проверяете возвращаемую ошибку. И вы, похоже, не перезагружаете систему, как указано в документации, которую вы цитировали, вы должны это сделать., @romkey
это не полный код, я проверяю код ошибки, и все в порядке. И выполните сброс системы, но значение в регистре не меняется, @Dmytro Kochuk
Это поможет, если вы поделитесь полным кодом. По крайней мере, поделитесь всем кодом, имеющим отношение к вопросу, полным минимальным жизнеспособным примером, демонстрирующим проблему. Не просто фрагмент., @romkey
@romkey я в основном использую этот код, но в нем нет фрагмента с записью случайного числа в регистре MSB и LSB: https://github.com/Senseair-AB/Sunrise-Examples---Arduino/blob/master/examples/i2c/sunrise_i2c_continuous/sunrise_i2c_continuous.ino, @Dmytro Kochuk
Можете ли вы составить минимальный пример, который фактически компилируется и запускается, который устанавливает регистр, а затем проверяет регистр. Мы действительно не можем отлаживать "я в основном использую этот код" и тому подобное., @Nick Gammon
@NickGammon я добавил пример кода, который компилируется и запускается в esp32., @Dmytro Kochuk