ht1621b 0.1.0

Platform-agnostic embedded-hal driver for the HT1621B LCD controller (3-wire bit-bang)
Documentation

ht1621b

Platform-agnostic embedded-hal 1.0 driver for the HT1621B LCD controller, driven over its custom 3-wire serial interface (CS / WR / DA).

  • Ht1621Bus: low-level protocol layer (raw commands / RAM writes).
  • Ht1621: ergonomic, command-based wrapper with Config, power control, buzzer, and time-base / watchdog helpers.

Generic over any embedded-hal 1.0 OutputPin + DelayNs; the power-on initialisation (Ht1621::new) is async and borrows an embedded-hal-async DelayNs for its millisecond-scale waits.

Example

use ht1621b::{Config, Ht1621};

// `cs`, `wr`, `da`: OutputPin; `delay`: DelayNs; `async_delay`: async DelayNs
let mut lcd = Ht1621::new(cs, wr, da, delay, &mut async_delay, Config::default()).await;
lcd.write_byte(arbitrary_int::u6::new(0x00), 0xEF); // display a digit

Resource-constrained MCUs (e.g. Cortex-M0+)

This driver targets extremely small parts (tested against budgets like 3 KB SRAM / 20 KB flash). It uses no heap, no static buffers, and the struct is only the four moved fields (cs, wr, da, delay), so its RAM footprint is a handful of bytes. The dominant cost is flash, which is overwhelmingly decided by your application's release profile.

Add this to your binary crate's Cargo.toml:

[profile.release]
opt-level = "z"     # optimise for size
lto = true
codegen-units = 1
panic = "abort"     # drop unwind tables / landing pads
strip = true

panic = "abort" is usually the single biggest flash win at this level, as it removes the unwinding machinery entirely.

Cortex-M0+ notes (thumbv6m-none-eabi)

  • The M0+ has no hardware divide and no atomic compare-and-swap. This driver contains neither division nor atomics, so it maps cleanly onto the thumbv6m instruction set.
  • When constructing RAM addresses, prefer arbitrary_int::u6::new_masked(x) over u6::new(x): new() panics on out-of-range input, which pulls the core::fmt panic-formatting machinery into flash. new_masked() truncates instead and keeps that code path out of your binary.

Measuring size

An example harness is included that instantiates the full driver (including the async new) on bare metal so you can profile it:

cargo size --example size_m0 --release --target thumbv6m-none-eabi -- -A

Measured on thumbv6m-none-eabi (driver + minimal cortex-m-rt runtime; the size_m0 harness exercises new, clear, write_byte, and set_config). Two profiles compared — stock release (opt-level = 3, no LTO) vs. the size profile above:

profile .text .rodata / .data / .bss
stock release 124 B 0 / 0 / 0
recommended size opts 356 B 0 / 0 / 0

(.text here excludes the 192 B .vector_table, which is Cortex-M runtime, not the driver. Stock release inlines the no-op harness pins away more aggressively, hence the smaller number; both are tiny.)

Key takeaways: .rodata/.data/.bss are all 0 — no core::fmt panic-formatting machinery, libcalls, or static buffers are linked, and the driver's static RAM footprint is zero. Unused features cost nothing: methods are generic over the pins/delay, so any command you never call (e.g. tone or time-base/WDT) is dropped entirely by dead-code elimination.