SegLCDLib
Loading...
Searching...
No Matches
SegLCDLib Architecture

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:

class SegLCDLib : public Print {
virtual void init() = 0; // Initialize display
virtual void clear() = 0; // Turn off all segments
virtual void on() = 0; // Display on
virtual void off() = 0; // Display off
virtual void setCursor(uint8_t row, uint8_t col) = 0;
virtual size_t write(uint8_t ch) = 0; // Print character
virtual void command(uint8_t cmd) = 0; // Raw command
};
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 ModeDrive { // Display refresh pattern
MODE_DRIVE_STATIC, // 1/1 duty (single digit, rare)
MODE_DRIVE_12, // 1/2 multiplex (2 digits)
MODE_DRIVE_13, // 1/3 multiplex (3 digits)
MODE_DRIVE_14 // 1/4 multiplex (4 digits)
};
enum ModeBias { // Voltage bias (sets contrast)
MODE_BIAS_13, // 1/3 bias
MODE_BIAS_12 // 1/2 bias
};
enum BacklightMode { // GPIO backlight control
BACKLIGHT_DIGITAL, // On/off only
BACKLIGHT_PWM // PWM brightness (0-255)
};
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

class SegTransportI2C : public SegTransport {
virtual void write(uint8_t address, uint8_t data) = 0;
virtual void write(uint8_t address, uint8_t* data, size_t length) = 0;
};
SegTransportI2CArduino(TwoWire& i2c);
};
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;
};
SegTransport3WireArduino(uint8_t data, uint8_t write, uint8_t read = -1);
};
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

class SegDriver_PCx85 : public SegLCDLib {
// I2C address configuration
uint8_t _address; // 0x38 (SA0=0) or 0x39 (SA0=1)
uint8_t _subaddress; // A0-A2 bits (protocol subaddress)
// RAM buffering is inherited from SegLCDLib
// Concrete LCD class allocates required size via _allocateBuffer()
uint8_t* _ramBuffer;
// Control methods
void _writeRam(); // Send buffer to controller
void _writeAddrRam(); // Write with address masking
};
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

class SegDriver_HT1621 : public SegLCDLib {
SegTransport3Wire& _transport; // 3-wire bus transport
uint8_t _cs; // Chip select pin (GPIO)
// RAM buffering is inherited from SegLCDLib
// Concrete LCD class allocates required size via _allocateBuffer()
uint8_t* _ramBuffer;
// 3-wire serial protocol methods via transport
void command(uint8_t cmd);
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:

  1. Assert CS through transport
  2. Send command/address prefix
  3. Send data bits MSB first through transport
  4. Release CS through transport

VK0192 (3-Wire Serial, Advanced)

File: src/SegDriver_VK0192.h

class SegDriver_VK0192 : public SegLCDLib {
// Same 3-wire transport model as HT1621
uint8_t _cs;
// RAM buffering is inherited from SegLCDLib
// Concrete LCD class allocates required size via _allocateBuffer()
uint8_t* _ramBuffer;
// Same transport and RAM write model as HT1621/HT1622
};
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:

// Display-specific methods
virtual size_t write(uint8_t ch); // Output digit
};
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:
// Constructor
SegLCD_PCF85176_6DigitSignalBatteryProgress(SegTransportI2C& transport, uint8_t address = 0x38, uint8_t subaddress = 0);
// Base methods (pure virtual implementations)
void init(); // Init controller + clear
size_t write(uint8_t ch); // Output 7-segment char
// Feature methods (display-specific)
void setSignalLevel(uint8_t bars); // 0-4 bars
void setBatteryLevel(uint8_t level); // 0-3 levels
void setProgress(uint8_t percent); // 0-100%
private:
// Segment mapping: maps digit position → RAM addresses/bits
void _mapSegments(uint8_t digit, uint8_t segments);
// 7-segment character map
static const uint8_t _charMap[256];
// Display layout constants
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); // Move output position
write(character); // Output at current position, advance

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;
// Concrete LCD class allocates exact size it needs
_allocateBuffer(RAM_SIZE);
// Update display: modify cached RAM, then sync affected byte(s)
_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:

// Example: Turn on segments for digit '5'
// _mapSegments(0b0110111); // logical segment set for one digit
// Internal mapping:
_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

// File: src/SegLCD_PCF85176_MyDisplay.h
class SegLCD_PCF85176_MyDisplay : public SegDriver_PCF85176 {
virtual size_t write(uint8_t ch);
void _mapSegments(uint8_t digit, uint8_t segments);
};

Instantiate with transport from user code:

SegTransportI2CArduino transport(Wire);
SegLCD_PCF85176_MyDisplay lcd(transport);

For 3-wire displays:

SegTransport3WireArduino transport(dataPin, wrPin);
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