Overview
SegLCDLib uses a layered architecture separating concerns between abstract interface, transport, controller protocol handling, and display-specific implementations.
┌─────────────────────────────────────────────────────┐
│ User Application (Arduino Sketch) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ LCD Implementation Layer │
│ ├─ SegLCD_PCF85176_TempHumidity │
│ ├─ SegLCD_PCF85176_6DigitSignalBatteryProgress │
│ ├─ SegLCD_HT1621_4SegDegree │
│ └─ ... (one class per display variant) │
└──────────────────┬──────────────────────────────────┘
│ inherits
┌──────────────────▼──────────────────────────────────┐
│ SegLCDLib Base Class (SegLCDLib : public Print) │
│ - Abstract interface (pure virtual methods) │
│ - LCD API 1.0 spec compliance │
│ - Cursor positioning (row, col) │
└──────────────────┬──────────────────────────────────┘
│ base of drivers
┌──────────────────▼──────────────────────────────────┐
│ Controller Driver Layer │
│ ├─ SegDriver_PCx85 (I2C via transport) │
│ ├─ SegDriver_HT1621 (3-wire via transport) │
│ ├─ SegDriver_HT1622 (3-wire via transport) │
│ └─ SegDriver_VK0192 (3-wire via transport) │
└──────────────────┬──────────────────────────────────┘
│ uses
┌──────────────────▼──────────────────────────────────┐
│ Transport Layer │
│ ├─ SegTransportI2C / SegTransportI2CArduino │
│ └─ SegTransport3Wire / SegTransport3WireArduino │
└──────────────────┬──────────────────────────────────┘
│ wraps
┌──────────────────▼──────────────────────────────────┐
│ Hardware Communication │
│ ├─ Wire / TwoWire bus │
│ └─ GPIO pins for DATA / WR / CS │
└─────────────────────────────────────────────────────┘
Layer 1: Abstract Base Class
File: src/SegLCDLib.h
Defines the common interface for all LCD implementations:
virtual void clear() = 0;
virtual void setCursor(uint8_t row, uint8_t col) = 0;
virtual size_t write(uint8_t ch) = 0;
virtual void command(uint8_t cmd) = 0;
};
Abstract base class for LCD segment display drivers.
Definition SegLCDLib.h:36
virtual void command(uint8_t command)=0
Send RAW command to controller.
virtual void init()
Logical display sections that can be targeted by higher-level rendering logic.
Definition SegLCDLib.cpp:18
virtual void on()=0
Turn the display on.
virtual void off()=0
Turn the display off.
virtual void setCursor(uint8_t row, uint8_t col)
Set cursor on exact digit.
Definition SegLCDLib.cpp:76
virtual size_t write(uint8_t)
Definition SegLCDLib.h:89
virtual void clear()
Clear all visible segments on the display.
Definition SegLCDLib.cpp:64
Key Features
- Inherits from
Print: Enables print(), println(), write() from Arduino standard library
- LCD API 1.0 Spec: Follows Arduino's LCD API specification (https://playground.arduino.cc/Code/LCDAPI/)
- Cursor System: Row/column addressing for positioning output
- Row 0: First display row (for simple LCDs, the only row)
- Column: Digit position within that row
- Example: T1T2 LCD has Row 0 (clock), Row 1 (T1 temps), Row 2 (T2 temps)
Enumerations
};
};
enum BacklightMode {
BACKLIGHT_DIGITAL,
BACKLIGHT_PWM
};
ModeBias
Bias mode used by LCD controller.
Definition SegLCDLib.h:25
@ MODE_BIAS_13
1/3 bias
Definition SegLCDLib.h:26
@ MODE_BIAS_12
1/2 bias
Definition SegLCDLib.h:27
ModeDrive
Drive mode used by LCD controller.
Definition SegLCDLib.h:15
@ MODE_DRIVE_12
1/2 multiplexing
Definition SegLCDLib.h:17
@ MODE_DRIVE_13
1/3 multiplexing
Definition SegLCDLib.h:18
@ MODE_DRIVE_14
1/4 multiplexing
Definition SegLCDLib.h:19
@ MODE_DRIVE_STATIC
Static drive (1/1 duty)
Definition SegLCDLib.h:16
Layer 2: Transport Layer
Transports encapsulate low-level bus access and are injected into controller drivers.
I2C Transport
File: src/SegTransport.h
virtual void write(uint8_t address, uint8_t data) = 0;
virtual void write(uint8_t address, uint8_t* data,
size_t length) = 0;
};
};
Arduino TwoWire implementation of the I2C transport.
Definition SegTransport.h:90
Abstract I2C transport.
Definition SegTransport.h:65
virtual void write(uint8_t address, uint8_t data)=0
Write one byte to an I2C device.
Abstract base class for low-level bus transports.
Definition SegTransport.h:9
3-Wire Transport
File: src/SegTransport.h
virtual void set_cs(uint8_t chipselect,
bool state) = 0;
virtual void write(uint16_t data, uint8_t bitCount) = 0;
};
};
Arduino GPIO implementation of the 3-wire transport.
Definition SegTransport.h:42
void write(uint16_t data, uint8_t bitCount) override
Write bits to the 3-wire bus, MSB first.
Definition SegTransport.cpp:19
Abstract 3-wire serial transport.
Definition SegTransport.h:18
virtual void set_cs(uint8_t chipselect, bool state)=0
Drive chip-select line.
virtual void write(uint16_t data, uint8_t bitCount)=0
Write bits to the 3-wire bus, MSB first.
Layer 3: Controller Drivers
Controllers handle protocol-level communication with LCD hardware. Each driver abstracts a specific protocol.
PCF85176 / PCF8576 (I2C)
File: src/SegDriver_PCx85.h, src/SegDriver_PCF85176.h
void _writeAddrRam();
};
Base class for PCx85 LCD segment display drivers.
Definition SegDriver_PCx85.h:12
void _writeRam(uint8_t *data, size_t length, uint8_t address=0) override
Low-level method to write a data buffer to display RAM.
Definition SegDriver_PCx85.cpp:45
uint8_t _subaddress
Definition SegDriver_PCx85.h:123
uint8_t _address
Definition SegDriver_PCx85.h:122
SegTransportI2C & _transport
Definition SegDriver_PCx85.h:121
size_t _ramBufferSize
Size of allocated RAM buffer in bytes.
Definition SegLCDLib.h:361
uint8_t * _ramBuffer
Dynamic RAM buffer for display data (allocated by derived classes).
Definition SegLCDLib.h:356
Features:
- I2C protocol (2 pins: SDA, SCL)
- Uses shared dynamic RAM buffer from
SegLCDLib
- Sends I2C transactions through
SegTransportI2C
- I2C address set by SA0 pin (0x38 or 0x39)
- A0-A2 are protocol subaddresses (not I2C address pins)
- Multiplexed display support (1/3 or 1/4 duty)
- Bias and drive mode configuration
Subclass Chain:
HT1621 / HT1622 (3-Wire Serial)
Files: src/SegDriver_HT1621.h, src/SegDriver_HT1622.h
void _writeRam(uint8_t* data,
size_t length, uint8_t address);
};
void command(uint8_t command) override
Send command to the controller.
Definition SegDriver_3Wire.cpp:22
uint8_t _cs
Chip select pin for the display.
Definition SegDriver_3Wire.h:85
SegTransport3Wire & _transport
Definition SegDriver_3Wire.h:84
virtual void _writeRam(uint8_t *data, size_t length, uint8_t address)
Low-level method to write a data buffer to display RAM.
Definition SegDriver_3Wire.cpp:37
HT1621 LCD segment display driver.
Definition SegDriver_HT1621.h:12
Features:
- 3-wire serial protocol: Clock, Data, Chip Select
- Uses shared dynamic RAM buffer from
SegLCDLib
- Sends DATA/WR/CS signaling through
SegTransport3Wire
- Typical controller RAM capacity is 16 bytes (HT1621) or 32 bytes (HT1622), but concrete LCD classes may allocate only the used portion
- Configurable BIAS and DRIVE modes
- Integrated into LCD module (no separate IC on board)
- No hardware peripheral required beyond GPIO pins
Protocol:
- Assert CS through transport
- Send command/address prefix
- Send data bits MSB first through transport
- Release CS through transport
VK0192 (3-Wire Serial, Advanced)
File: src/SegDriver_VK0192.h
};
VK0192 LCD segment display driver.
Definition SegDriver_VK0192.h:12
Features:
- Uses shared dynamic RAM buffer from
SegLCDLib
- Sends 3-wire signaling through
SegTransport3Wire
- VK0192 address space is 24×8 bit RAM (48 4-bit addresses 0-47)
- Irregular segment mapping (digits/segments not sequential in memory)
- 4μs minimum pulse width timing
- More complex segment addressing than HT1621/HT1622
Layer 4: LCD Implementations
LCD classes combine a controller driver with display-specific segment mapping and symbols.
Design Pattern: LCD Inherits from Controller
Unlike typical composition patterns, LCD classes inherit directly from controller drivers:
virtual size_t write(uint8_t ch);
};
Implementation of the PCF85176 controllers.
Definition SegDriver_PCx85.h:141
Temperature/humidity LCD with dual rows (PCF85176).
Definition SegLCD_PCF85176_TempHum.h:19
size_t write(uint8_t ch) override
Definition SegLCD_PCF85176_TempHum.cpp:111
Rationale:
- Controllers are always integrated into the LCD module (not separate components)
- Single instance serves both controller and display logic
- Reduces memory overhead (no separate controller instance)
- Simplifies user code (one object to manage)
Structure of LCD Class
public:
size_t write(uint8_t ch);
private:
static const uint8_t _charMap[256];
static const uint8_t DIGITS = 6;
};
6-digit LCD with signal/battery/progress indicators (PCF85176).
Definition SegLCD_PCF85176_6DigSigBattProgress.h:19
uint8_t _mapSegments(uint8_t val)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:317
void setProgress(uint8_t value)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:51
size_t write(uint8_t ch) override
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:203
void setSignalLevel(uint8_t value)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:32
void setBatteryLevel(uint8_t value)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:13
void init() override
Logical display sections that can be targeted by higher-level rendering logic.
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:8
Cursor and Positioning System
SegLCDLib uses row/column addressing inherited from LCD API:
setCursor(row, col);
write(character);
Row/Column Semantics
For most displays (single row):
setCursor(0, 0): Start of display
setCursor(0, 1): 2nd digit
setCursor(1, x): Invalid (most displays have no row 1)
For multi-row displays (e.g., T1T2 LCD):
setCursor(0, 0): Clock display, digit 1
setCursor(1, 0): T1 temperature, digit 1
setCursor(2, 0): T2 temperature, digit 1
Memory Management
RAM Buffering
All drivers maintain an in-memory buffer mirroring controller RAM:
uint8_t* _ramBuffer = nullptr;
size_t _ramBufferSize = 0;
_allocateBuffer(RAM_SIZE);
_ramBuffer[0] |= 0x01;
_writeRam(_ramBuffer[0], 0);
Benefits:
- Minimize I2C/3-wire transactions (one bulk write vs. multiple small writes)
- Support masked writes (update specific bits without full byte)
- Reduce latency (batch updates before hardware sync)
Segment Mapping
Each LCD class implements _mapSegments() to convert logical segments to physical RAM addresses:
_ramBuffer[addressForDigit0] |= bitsFor5;
_writeRam(_ramBuffer[addressForDigit0], hwAddressForDigit0);
Mapping varies by:
- Controller: Different RAM layouts (PCF vs HT vs VK)
- Display: Segment wiring (which output pin connects to which segment)
Initialization Flow
When user calls lcd.init():
1. Controller initialization (BIAS, DRIVE modes)
2. Enable display (turn on voltage supply)
3. Clear RAM buffer (all zeros)
4. Write buffer to hardware
5. Reset cursor to (0, 0)
Adding New Displays
Step 1: Identify Controller
Step 2: Create LCD Class
virtual size_t write(uint8_t ch);
void _mapSegments(uint8_t digit, uint8_t segments);
};
Instantiate with transport from user code:
SegLCD_PCF85176_MyDisplay lcd(transport);
For 3-wire displays:
SegLCD_HT1621_MyDisplay lcd(transport, csPin);
Step 3: Implement Segment Mapping
- Use RAW LCD variant to test physical wiring
- Record which output pins light which segments
- Implement
_mapSegments() to convert logical→physical
Step 4: Add Feature Methods
- Display-specific symbols (battery, signal, degree, etc.)
- Implement
write() to output characters correctly
See Adding New LCD for detailed tutorial.
Datasheet References
- PCF85176: I2C 40×8 segment LCD controller
- HT1621/1622: 3-wire serial LCD drivers
- VK0192: 3-wire LCD driver with irregular addressing
Key Design Decisions
| Decision | Rationale |
| LCD inherits controller | Controllers integrated in LCD modules, not external |
| Transport injected into drivers | Separate bus access from controller protocol and ease Arduino/non-Arduino backends |
| RAM buffering | Minimize I2C/serial transactions, batch updates |
| Row/column addressing | Follow Arduino LCD API standard for consistency |
| Print inheritance | Enable standard print() and println() operations |
| Base driver per protocol | Reduce code duplication (PCF, HT1621, HT1622, VK0192 each once) |
Related Documentation