Запись в определенный регистр 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

Что я делаю не так? может быть, кто-нибудь может помочь с этим?

, 👍1

Обсуждение

Вы не проверяете возвращаемую ошибку. И вы, похоже, не перезагружаете систему, как указано в документации, которую вы цитировали, вы должны это сделать., @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


2 ответа


0

Как написано на странице 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


1

Моей главной целью было использовать возможность получить значение, компенсируемое атмосферным давлением. Но для этого нужно было понять, как писать в регистрах.

И не понял, в чем была проблема в примерах кода, предоставленных производителем, он неправильно работал с записью для регистрации данных в 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); //Подождать после перезапуска датчика
,