Skip to content

Commit c6c6531

Browse files
committed
Speed up transfers
An I2C transmission has a significant overhead of ~10+1 I2C clock cycles. Reducing the number of beginTransmission()/endTransmission() pairs from twice every nibble to once per _send() hence speeds up transmission times significantly. The specified I2C clock rate speed of the PCF8574 chip is 100KHz. Transmitting a single byte over I2C takes 9 clock ticks or 90us. Even if the I2C bus is operated beyond the chip's spec in fast mode at 400 kHz, any additional delays during transmission shorter than this are unnecessary. Commands are only executed after the second nibble is transmitted. The 37us delay after the first nibble is hence unnecessary. We now omit any additional delays and perform a transmission only once per _send() command. This reduces the time from 4x(11+9) = 80 cycles to 4x9 + 11 = 47 cycles. Taking the delays into account, this changes the time per command from 800us + 4*38us = 952us to 470us at the default 100kHz bus speed.
1 parent 528ec5f commit c6c6531

File tree

2 files changed

+36
-10
lines changed

2 files changed

+36
-10
lines changed

src/LiquidCrystal_PCF8574.cpp

+35-10
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ void LiquidCrystal_PCF8574::createChar_P(uint8_t location, const byte *charmap)
267267
byte c = pgm_read_byte(p++);
268268
write(c);
269269
}
270-
} // createCharPgm()
270+
} // createChar_P()
271271
#endif
272272

273273

@@ -282,33 +282,58 @@ inline size_t LiquidCrystal_PCF8574::write(uint8_t ch)
282282
// write either command or data
283283
void LiquidCrystal_PCF8574::_send(uint8_t value, bool isData)
284284
{
285+
// An I2C transmission has a significant overhead of ~10+1 I2C clock
286+
// cycles. We consequently only perform it only once per _send().
287+
288+
Wire.beginTransmission(_i2cAddr);
285289
// write high 4 bits
286-
_sendNibble((value >> 4 & 0x0F), isData);
290+
_writeNibble((value >> 4 & 0x0F), isData);
287291
// write low 4 bits
288-
_sendNibble((value & 0x0F), isData);
292+
_writeNibble((value & 0x0F), isData);
293+
Wire.endTransmission();
289294
} // _send()
290295

291296

292297
// write a nibble / halfByte with handshake
293-
void LiquidCrystal_PCF8574::_sendNibble(uint8_t halfByte, bool isData)
298+
void LiquidCrystal_PCF8574::_writeNibble(uint8_t halfByte, bool isData)
294299
{
295300
// map the data to the given pin connections
296-
uint8_t data = 0;
301+
uint8_t data = isData ? _rs_mask : 0;
302+
// _rw_mask is not used here.
303+
if (_backlight > 0)
304+
data |= _backlight_mask;
297305

298306
// allow for arbitrary pin configuration
299307
if (halfByte & 0x01) data |= _data_mask[0];
300308
if (halfByte & 0x02) data |= _data_mask[1];
301309
if (halfByte & 0x04) data |= _data_mask[2];
302310
if (halfByte & 0x08) data |= _data_mask[3];
303311

304-
_write2Wire(data, isData, true);
305-
delayMicroseconds(1); // enable pulse must be >450ns
306-
_write2Wire(data, isData, false);
307-
delayMicroseconds(37); // commands need > 37us to settle
312+
// Note that the specified speed of the PCF8574 chip is 100KHz.
313+
// Transmitting a single byte takes 9 clock ticks at 100kHz -> 90us.
314+
// The 37us delay is only necessary after sending the second nibble.
315+
// But in that case we have to restart the transfer using additional
316+
// >10 clock cycles. Hence, no additional delays are necessary even
317+
// when the I2C bus is operated beyond the chip's spec in fast mode
318+
// at 400 kHz.
319+
320+
Wire.write(data | _enable_mask);
321+
// delayMicroseconds(1); // enable pulse must be >450ns
322+
Wire.write(data);
323+
// delayMicroseconds(37); // commands need > 37us to settle
324+
} // _writeNibble
325+
326+
327+
// write a nibble / halfByte with handshake
328+
void LiquidCrystal_PCF8574::_sendNibble(uint8_t halfByte, bool isData)
329+
{
330+
Wire.beginTransmission(_i2cAddr);
331+
_writeNibble(halfByte, isData);
332+
Wire.endTransmission();
308333
} // _sendNibble
309334

310335

311-
// private function to change the PCF8674 pins to the given value
336+
// private function to change the PCF8574 pins to the given value
312337
void LiquidCrystal_PCF8574::_write2Wire(uint8_t data, bool isData, bool enable)
313338
{
314339
if (isData)

src/LiquidCrystal_PCF8574.h

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class LiquidCrystal_PCF8574 : public Print
9898
// low level functions
9999
void _send(uint8_t value, bool isData = false);
100100
void _sendNibble(uint8_t halfByte, bool isData = false);
101+
void _writeNibble(uint8_t halfByte, bool isData);
101102
void _write2Wire(uint8_t data, bool isData, bool enable);
102103

103104
void init(uint8_t i2cAddr, uint8_t rs, uint8_t rw, uint8_t enable,

0 commit comments

Comments
 (0)