{"id":29148686,"url":"https://github.com/liebman/hub75-framebuffer","last_synced_at":"2025-10-30T01:08:02.911Z","repository":{"id":299107963,"uuid":"1002044987","full_name":"liebman/hub75-framebuffer","owner":"liebman","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-14T18:42:21.000Z","size":141,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-14T18:49:41.375Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liebman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-14T15:43:05.000Z","updated_at":"2025-06-14T18:42:24.000Z","dependencies_parsed_at":"2025-06-14T18:49:44.657Z","dependency_job_id":null,"html_url":"https://github.com/liebman/hub75-framebuffer","commit_stats":null,"previous_names":["liebman/hub75-framebuffer"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/liebman/hub75-framebuffer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liebman%2Fhub75-framebuffer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liebman%2Fhub75-framebuffer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liebman%2Fhub75-framebuffer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liebman%2Fhub75-framebuffer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liebman","download_url":"https://codeload.github.com/liebman/hub75-framebuffer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liebman%2Fhub75-framebuffer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262857289,"owners_count":23375492,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-30T22:06:23.729Z","updated_at":"2025-10-30T01:08:02.902Z","avatar_url":"https://github.com/liebman.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hub75-framebuffer\n\n[![Crates.io](https://img.shields.io/crates/v/hub75-framebuffer.svg)](https://crates.io/crates/hub75-framebuffer)\n[![Documentation](https://docs.rs/hub75-framebuffer/badge.svg)](https://docs.rs/hub75-framebuffer)\n[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](README.md)\n[![Coverage Status](https://coveralls.io/repos/github/liebman/hub75-framebuffer/badge.svg?branch=main)](https://coveralls.io/github/liebman/hub75-framebuffer?branch=main)\n\nDMA-friendly framebuffer implementations for driving HUB75 RGB LED matrix\npanels with Rust.  The crate focuses on **performance**, **correct timing**,\nand **ergonomic drawing** by integrating tightly with the `embedded-graphics`\necosystem.\n\n---\n\n## How HUB75 LED panels work (very short recap)\n\nA HUB75 panel behaves like a long daisy-chained shift-register:\n\n1. Color data for *one pair of rows* is shifted in serially on every cycle of `CLK`.\n2. After the last pixel of the row pair has been clocked, the controller blanks\n   the LEDs (`OE` HIGH), sets the address lines **A–E**, and produces a short\n   pulse on `LAT` to latch the freshly-shifted data into the LED drivers.\n3. `OE` goes LOW again and the row pair lights up while the next one is already\n   being shifted.\n\nColor depth is achieved with **Binary/Bit-Angle Code Modulation (BCM)**:\nlower bit-planes are shown for shorter times, higher ones for longer, yielding\n2^n intensity levels per channel while keeping peak currents low.\n\nIf you want a deeper explanation, have a look inside `src/lib.rs` — the crate\ndocumentation contains an extensive primer.\n\n---\n\n## Two framebuffer flavors\n\n| Module              | Extra hardware | Word size | Memory use | Pros / Cons |\n|---------------------|----------------|-----------|------------|-------------|\n| `plain`             | none           | 16 bit (14 used) | high       | Simplest, wires exactly like a standard HUB75 matrix. |\n| `latched`           | **external latch gate** (see below) | 8 bit | ×½ of `plain` | Lower memory footprint, but needs a tiny glue-logic board. |\n\n## Multiple Panels\n\n- Use `tiling::TiledFrameBuffer` to drive several HUB75 panels as one large display.\n- Combine it with a pixel-remapping policy like `ChainTopRightDown` and any of\n  the framebuffers above (plain or latched).\n- The wrapper exposes a single `embedded-graphics` canvas, so a 3 × 3 stack of\n  64 × 32 panels simply looks like a 192 × 96 screen while all coordinate translation happens transparently.\n\n### The latch circuit\n\nThe *latched* implementation assumes a small external circuit that holds the\nrow address while gating the pixel clock.  A typical solution uses a 74xx373\nlatch along with a few NAND gates:\n\n![Latch circuit block diagram](images/latch-circuit.png)\n\nThe latch IC stores the address bits whilst one NAND gate blocks the `CLK`\nsignal during the latch interval.  The remaining spare gate can be employed\nto combine a global PWM signal with `OE` for fine-grained brightness control\nas shown.\n\n---\n\n## Getting started\n\nAdd the dependency to your `Cargo.toml`:\n\n```toml\n[dependencies]\nhub75-framebuffer = \"0.5.0\"\n```\n\n### Choose your parameters\n\n```rust\nuse hub75_framebuffer::{compute_frame_count, compute_rows};\nuse hub75_framebuffer::latched::DmaFrameBuffer; \n// or ::plain::DmaFrameBuffer\n\nconst ROWS:       usize = 32;              // panel height\nconst COLS:       usize = 64;              // panel width\nconst BITS:       u8    = 3;               // colour depth ⇒ 7 BCM frames\nconst NROWS:      usize = compute_rows(ROWS);          // 16\nconst FRAME_COUNT:usize = compute_frame_count(BITS);   // (1\u003c\u003cBITS)-1 = 7\n\n// Create a framebuffer (already initialized/cleared)\nlet mut framebuffer = DmaFrameBuffer::\u003cROWS, COLS, NROWS, BITS, FRAME_COUNT\u003e\n    ::new();\n```\n\nYou can now draw using any `embedded-graphics` primitive:\n\n```rust\nuse embedded_graphics::prelude::*;\nuse embedded_graphics::primitives::{Circle, Rectangle, PrimitiveStyle};\nuse hub75_framebuffer::Color;\n\nRectangle::new(Point::new(0, 0), Size::new(COLS as u32, ROWS as u32))\n    .into_styled(PrimitiveStyle::with_fill(Color::BLACK))\n    .draw(\u0026mut framebuffer)\n    .unwrap();\n\nCircle::new(Point::new(20, 10), 8)\n    .into_styled(PrimitiveStyle::with_fill(Color::GREEN))\n    .draw(\u0026mut framebuffer)\n    .unwrap();\n```\n\nFinally hand the raw DMA buffer off to your MCU's parallel peripheral.\n\n---\n\n## Crate features\n\n### `esp-hal-dma` (required when using `esp-hal`)\n\n**Required** when using the `esp-hal` crate for ESP32 development. This\nfeature switches the `ReadBuffer` trait implementation from `embedded-dma`\nto `esp-hal::dma`. If you're targeting ESP32 devices with `esp-hal`, you\n**must** enable this feature for DMA compatibility.\n\n```toml\n[dependencies]\nhub75-framebuffer = { version = \"0.5.0\", features = [\"esp-hal-dma\"] }\n```\n\n### `esp32-ordering` (required for original ESP32 only)\n\n**Required** when targeting the original ESP32 chip (not ESP32-S3 or other\nvariants). This feature adjusts byte ordering to accommodate the quirky\nrequirements of the ESP32's I²S peripheral in 8-bit and 16-bit modes. Other\nESP32 variants (S2, S3, C3, etc.) do **not** need this feature.\n\n```toml\n[dependencies]\nhub75-framebuffer = { version = \"0.5.0\", features = [\"esp32-ordering\"] }\n```\n\n### `skip-black-pixels`\n\nSkip drawing black pixels for performance boost in UI applications. When\nenabled, calls to `set_pixel()` with `Color::BLACK` return early without\nwriting to the framebuffer, assuming the framebuffer was already cleared.\n\n### `defmt`\n\nImplement the `defmt::Format` trait so framebuffer types can be logged with\nthe [`defmt`](https://github.com/knurling-rs/defmt) ecosystem.\n\n### `doc-images`\n\nEmbed documentation images when building docs on docs.rs. Not needed for\nnormal usage.\n\nEnable features in your `Cargo.toml`:\n\n```toml\n[dependencies]\nhub75-framebuffer = { version = \"0.5.0\", \n                      features = [\"esp-hal-dma\", \"esp32-ordering\"] }\n```\n\n---\n\n## Running tests\n\n```shell\ncargo test\n```\n\nAll logic including bitfields, address mapping, brightness modulation and\nthe `embedded-graphics` integration is covered by a comprehensive test-suite\n(≈ 300 tests).\n\n---\n\n## License\n\nLicensed under either of\n\n* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or\n  \u003chttp://www.apache.org/licenses/LICENSE-2.0\u003e)\n* MIT license ([LICENSE-MIT](LICENSE-MIT) or\n  \u003chttp://opensource.org/licenses/MIT\u003e)\n\nat your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliebman%2Fhub75-framebuffer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliebman%2Fhub75-framebuffer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliebman%2Fhub75-framebuffer/lists"}