Overview
SegLCDLib uses a layered architecture separating concerns between abstract interface, controller protocol handling, and display-specific implementations.
┌─────────────────────────────────────────────────────┐
│ User Application (Arduino Sketch) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ SegLCDLib Base Class (SegLCDLib : public Print) │
│ - Abstract interface (pure virtual methods) │
│ - LCD API 1.0 spec compliance │
│ - Cursor positioning (row, col) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ Controller Driver Layer │
│ ├─ SegDriver_PCx85 (I2C) │
│ ├─ SegDriver_HT1621 (3-wire serial) │
│ ├─ SegDriver_HT1622 (3-wire serial) │
│ └─ SegDriver_VK0192 (3-wire serial) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ LCD Implementation Layer │
│ ├─ SegLCD_PCF85176_TempHum │
│ ├─ SegLCD_PCF85176_6DigSigBattProgress │
│ ├─ SegLCD_HT1621_4SegDegree │
│ └─ ... (one class per display variant) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ Hardware Communication │
│ ├─ I2C Bus (Wire library for PCF85176) │
│ └─ 3-Wire Serial (GPIO for HT1621/1622/VK0192) │
└─────────────────────────────────────────────────────┘
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 size_t write(uint8_t ch)
Definition SegLCDLib.h:89
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:71
virtual void clear()
Clear all visible segments on the display.
Definition SegLCDLib.cpp:60
virtual void init()=0
Logical display sections that can be targeted by higher-level rendering logic.
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: 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
uint8_t _subaddress
Definition SegDriver_PCx85.h:126
uint8_t _address
Definition SegDriver_PCx85.h:125
void _writeRam(uint8_t data, uint8_t address=0) override
Low-level method to write a single byte to display RAM.
Definition SegDriver_PCx85.cpp:71
uint8_t * _ramBuffer
Dynamic RAM buffer for display data (allocated by derived classes).
Definition SegLCDLib.h:356
Features:
- I2C protocol (2 pins: SDA, SCL)
- 40-byte RAM for segment data
- 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
uint8_t _pinCLK;
uint8_t _pinDATA;
uint8_t _pinCS;
void _writeCommand(uint8_t cmd);
void _writeData(uint16_t addr, uint8_t data);
void _writeBit(uint8_t bit);
};
HT1621 LCD segment display driver.
Definition SegDriver_HT1621.h:12
Features:
- 3-wire serial protocol: Clock, Data, Chip Select
- 16-byte RAM (HT1621) or 32-byte RAM (HT1622)
- Configurable BIAS and DRIVE modes
- Integrated into LCD module (no separate IC on board)
- GPIO-based (no special hardware needed)
Protocol:
- Set CS low
- Send 9-bit command/address
- Send data bits MSB first
- Set CS high
VK0192 (3-Wire Serial, Advanced)
File: src/SegDriver_VK0192.h
uint8_t _pinCLK, _pinDATA, _pinCS;
void _mapSegmentToAddress(uint8_t digit, uint8_t segment,
uint8_t& addr, uint8_t& bit);
};
VK0192 LCD segment display driver.
Definition SegDriver_VK0192.h:12
Features:
- 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 3: 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);
void setTempSymbol(bool on);
void setHumiditySymbol(bool on);
private:
void _mapSegments(uint8_t digit, uint8_t segments);
};
Implementation of the PCF85176 controllers.
Definition SegDriver_PCx85.h:144
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:
SegLCD_PCF85176_6DigSigBattProgress(TwoWire& i2c, uint8_t address = 0x38, uint8_t subaddress = 0);
size_t write(uint8_t ch);
void setSignal(uint8_t bars);
void setBattery(uint8_t level);
void setProgress(uint8_t percent);
private:
void _mapSegments(uint8_t digit, uint8_t segments);
static const uint8_t _charMap[256];
static const uint8_t MAX_DIGITS = 6;
};
virtual void init() override
Logical display sections that can be targeted by higher-level rendering logic.
Definition SegDriver_PCx85.cpp:10
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[40];
_ramBuffer[0] |= 0x01;
_writeRam();
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();
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
- I2C? → Use PCF85176/PCF8576 driver
- 3-wire? → Use HT1621/HT1622/VK0192 driver
Step 2: Create LCD Class
virtual size_t write(uint8_t ch);
void _mapSegments(uint8_t digit, uint8_t segments);
};
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 |
| 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