1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Bitplane framebuffers for a 16-bit plain HUB75 interface.
//!
//! All signals (row address, latch, OE, colour data) are packed into each
//! 16-bit word -- no external latch circuit is required.
//!
//! Two layout variants are provided:
//!
//! - [`frame`] -- Plane-major: all rows for one bit-plane stored contiguously.
//! The DMA chain sends each plane's data separately.
//! - [`row`] -- Row-major: all bit-planes for a single row stored contiguously.
//! Optimized for row-by-row BCM where the inter-row gap appears only once
//! per scan row instead of once per plane.
//!
//! Both variants share the same 16-bit `Entry` format and pin mapping.
//!
//! # Hardware Requirements
//! Requires a parallel output peripheral capable of clocking 16 bits (though
//! only 15 are actually used) at a time. The data is structured to directly
//! match the HUB75 connector signals, so no external latch or address-decode
//! hardware is needed.
//!
//! # HUB75 Signal Bit Mapping
//! Each 16-bit `Entry` represents the logic levels driven onto the HUB75 bus
//! during a single pixel-clock cycle:
//!
//! ```text
//! default (ESP32 I2S / generic) interstate75 (RP2040/RP2350 PIO)
//! 15 ─ (spare) 15 ─ (spare)
//! 14 ─ B1 Blue – lower half 14 ─ (spare)
//! 13 ─ G1 Green – lower half 13 ─ OE Output-Enable / Blank
//! 12 ─ R1 Red – lower half 12 ─ LAT Latch / STB
//! 11 ─ B0 Blue – upper half 11 ─ (spare)
//! 10 ─ G0 Green – upper half 10 ─ E Row address
//! 9 ─ R0 Red – upper half 9 ─ D Row address
//! 8 ─ OE Output-Enable / Blank 8 ─ C Row address
//! 7 ─ (spare) 7 ─ B Row address
//! 6 ─ (spare) 6 ─ A Row address
//! 5 ─ LAT Latch / STB 5 ─ B1 Blue – lower half
//! 4-0 ─ A..E Row address lines 4 ─ G1 Green – lower half
//! 3 ─ R1 Red – lower half
//! 2 ─ B0 Blue – upper half
//! 1 ─ G0 Green – upper half
//! 0 ─ R0 Red – upper half
//! ```
//!
//! The board-specific layout is selected by the `interstate75` feature; see
//! [`crate::pinmap`].
//!
//! The pixel clock is generated by the peripheral that owns the DMA stream and
//! is not part of the 16-bit word stored in the framebuffer.
//!
//! # Bitplane BCM Rendering
//! Each framebuffer is organised into `PLANES` bit-planes, one per colour
//! bit. To produce correct brightness via Binary Code Modulation, output each
//! plane's data (scan it) a number of times equal to its bit-weight (2^7 for
//! the MSB plane down to 2^0 for the LSB plane) — either with a DMA
//! descriptor chain or by reprogramming the DMA registers per segment from a
//! transfer-complete interrupt.
//!
//! Both layout variants store planes **LSB-first**: plane 0 carries bit 0
//! and is displayed once per scan, plane 7 carries bit 7 and is displayed
//! 128 times. Each BCM segment streams a contiguous *suffix* of planes,
//! repeated just enough times to bring each plane's total coverage to its
//! bit-weight — halving the number of DMA transfers per scan with identical
//! brightness. LSB-first ordering also means the first plane after an
//! address change displays stale data with minimal visual weight, avoiding
//! a separate primer segment. See the [`frame`] and [`row`] module
//! documentation for the exact scan order.
//!
//! See <https://www.batsocks.co.uk/readme/art_bcm_1.htm> for background on
//! BCM.
use bitfield;
pub use DmaFrameBuffer;
pub use crateADDR_MASK;
pub use cratePINMAP;
pub use crate;
pub use crate::;
// The OE *level* constants stay local: their default polarity differs between
// this module (standard active-low) and the deprecated `plain` module
// (active-high). Only the bit position (`pinmap::OE_BIT`) is shared.
pub const OE_ACTIVE: u16 = OE_BIT;
pub const OE_BLANK: u16 = 0;
pub const OE_ACTIVE: u16 = 0;
pub const OE_BLANK: u16 = OE_BIT;
// ---------------------------------------------------------------------------
// Shared helper functions
// ---------------------------------------------------------------------------
pub const
/// Builds the per-row pixel template with trail blank, active pixels, lead
/// blank, and latch embedded within the COLS entries.
pub const
// ---------------------------------------------------------------------------
// Entry — the 16-bit word that rides on the HUB75 bus
// ---------------------------------------------------------------------------
// The bit layout is selected per board by `crate::pinmap`; this single
// `bitfield!` definition reads the positions straight from `PINMAP` so the
// accessors can never drift out of sync with the DMA templates. Note the
// `red1/grn1/blu1` accessors are the *upper-half* triplet (`PINMAP.red0`...),
// and `red2/grn2/blu2` the lower half.
bitfield!