/      日本語

Wire (Helper Class Version)

Wire (How to use with helper classes)
The helper class version is a more abstract implementation. Generating objects reader, writer that correspond to reading and writing marks the start of bus usage, and destroying the object performs the bus usage termination procedure.

By creating objects inside the condition expression of an if statement, the object’s lifetime is limited to the scope of the if block. When exiting the if block, the object is destroyed, and at that time, the bus usage termination procedure is performed.

if (auto&& wrt = Wire.get_writer(...)) { // Object creation and device communication check
   // The scope (curly braces) is the lifetime of wrt.
   wrt << 0x00; // Write 0x00 using the mwx::stream interface
}
// wrt is destroyed when exiting the if block, and bus termination is performed

Also, since the read/write objects implement the mwx::stream interface, operators like << can be used.

  • Matching the start and end of bus usage with the object’s lifetime improves code readability and prevents omission of termination procedures.
  • Unified reading and writing procedures via the mwx::stream interface.

Reading

This is a reading method using helper classes to perform reading and its termination procedure within a scope if() { ... }.

const uint8_t DEV_ADDR = 0x70;
uint8_t au8data[6];
if (auto&& rdr = Wire.get_reader(DEV_ADDR, 6)) {
		for (auto&& c: au8data) {
			c = rdr();
		}
}

// same above
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(SHTC3_ADDRESS, 6)) {
		rdr >> u16temp;
		rdr >> u8temp_csum;
		rdr >> u16humd;
		rdr >> u8humd_csum;
}

Above, the rdr object generated by the get_reader() method is used to read one byte at a time. The method parameter specifies the two-wire serial ID to read.

  1. The rdr object is created inside if(...). (The type is resolved as a universal reference auto&& by type inference.)
  2. The created rdr object defines operator bool () and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true.
  3. The rdr object defines int operator () (void), which reads one byte from the two-wire serial bus when called. Returns -1 on failure, or the read byte value on success.
  4. The destructor of rdr is called at the end of the if() { ... } scope, issuing a STOP on the two-wire serial bus.

get_reader(addr, read_count=0)

periphe_wire::reader
get_reader(uint8_t addr, uint8_t read_count = 0)

Obtains a worker object used for I2C reading.

ParameterDescription
addrI2C address for reading
read_countNumber of bytes to read (if specified, a STOP bit is issued at the end of the transfer). If 0 is specified, no STOP bit is issued (some devices may work without it).

Writing (writer)

This is a writing method using helper classes to perform writing and its termination procedure within a scope if() { ... }.

const uint8_t DEV_ADDR = 0x70;
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
	wrt(SHTC3_TRIG_H);
	wrt(SHTC3_TRIG_L);
}

// same above
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
	wrt << SHTC3_TRIG_H; // int type is handled as uint8_t
	wrt << SHTC3_TRIG_L;
}

Above, the wrt object generated by the get_writer() method is used to write one byte at a time. The method parameter specifies the two-wire serial ID to write.

  1. The wrt object is created inside if(...). (The type is resolved as auto to avoid long type names.)
  2. The created wrt object defines operator bool () and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true.
  3. The wrt object defines int operator () (void), which writes one byte to the two-wire serial bus when called. Returns -1 on failure, or the written byte value on success.
  4. The destructor of wrt is called at the end of the if() { ... } scope, issuing a STOP on the two-wire serial bus.

get_writer()

periph_wire::writer
get_writer(uint8_t addr)

Obtains a worker object used for I2C writing.

ParameterDescription
addrI2C address for writing

Worker Object (writer)

<< operator

operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)

int and uint8_t types transfer 8 bits. Data order is big-endian (higher byte transferred first).

() operator

operator() (uint8_t val)
operator() (int val)

Writes one byte.

Worker Object (reader)

>> operator

operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
operator >> (uint8_t(&c)[N]) // fixed array of N bytes

Reads the size of each data type. Data order is big-endian (first transferred byte is stored in the higher byte).

() operator

int operator() (bool b_stop = false)

// example
uint8_t dat[6];
if (auto&& rdr = Wire.get_reader(0x70)) {
  for(uint8_t& x : dat) {
    x = rdr();
  }
}

Reads one byte. Returns -1 on error, or the read byte on success.

If b_stop is set to true, a STOP bit is issued on that read.

Example

The following example is a measurement example for the environmental sensor pal’s temperature and humidity sensor SHTC3.

Wire.begin();
// reset (may not necessary...)
if (auto&& wrt = Wire.get_writer(0x70)) {
	wrt << 0x80; // SHTC3_SOFT_RST_H
	wrt << 0x05; // SHTC3_SOFT_RST_L
}

delay(5); // wait some

// start read
if (auto&& wrt = Wire.get_writer(0x70)) {
	wrt << 0x60; // SHTC3_TRIG_H
	wrt << 0x9C; // SHTC3_TRIG_L
}

delay(10); // wait some

// read result
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(0x70, 6)) {
	rdr >> u16temp;
	rdr >> u8temp_csum;
	rdr >> u16humd;
	rdr >> u8humd_csum;
}

// checksum 0x31, init=0xFF
if (CRC8_u8CalcU16(u16temp, 0xff) != u8temp_csum) {
	Serial << format("{SHTC3 T CKSUM %x}", u8temp_csum); }
if (CRC8_u8CalcU16(u16humd, 0xff) != u8humd_csum) {
	Serial << format("{SHTC3 H CKSUM %x}", u8humd_csum); }

// calc temp/humid (degC x 100, % x 100)
int16_t i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
int16_t i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);

Serial << "temp=" << int(i16Temp)
	   << ",humid=" << int(i16Humd) << mwx::crlf;