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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//! Compile-time mapping of the HUB75 signals onto the bits of the DMA word.
//!
//! The [`plain`](crate::plain) and [`bitplane::plain`](crate::bitplane::plain)
//! framebuffers store one 16-bit word per pixel-clock cycle, while
//! [`latched`](crate::latched) and [`bitplane::latched`](crate::bitplane::latched)
//! store 8-bit words. In every case each HUB75 signal occupies a fixed bit, and
//! *which* bit depends on the board (for the 16-bit layouts) or on the external
//! latch circuit (for the 8-bit layouts). This module is the single source of
//! truth for those positions.
//!
//! ## The layouts
//!
//! ```text
//! 16-bit plain / bitplane::plain
//! default (ESP32 I2S / generic) interstate75 (RP2040/RP2350 PIO)
//! 15 .. 13 spare (dummy2, B1, G1) 15 .. 14 spare
//! 12 R1 Red - lower half 13 OE Output-Enable / Blank
//! 11 B0 Blue - upper half 12 LAT Latch / STB
//! 10 G0 Green - upper half 11 spare (unused)
//! 9 R0 Red - upper half 10..6 E D C B A Row address
//! 8 OE Output-Enable / Blank 5..3 B1 G1 R1 lower half
//! 7 spare 2..0 B0 G0 R0 upper half
//! 6 spare
//! 5 LAT Latch / STB
//! 4..0 A..E Row address
//!
//! 8-bit latched (fixed by the external latch circuit)
//! 7 OE Output-Enable / Blank
//! 6 LAT Latch / STB
//! 5 B2 Blue - sub-pixel group 2
//! 4 G2 Green - sub-pixel group 2
//! 3 R2 Red - sub-pixel group 2
//! 2 B1 Blue - sub-pixel group 1
//! 1 G1 Green - sub-pixel group 1
//! 0 R1 Red - sub-pixel group 1
//! ```
//!
//! Everything is a `const`, so the layout is folded away during compilation:
//! the generated code is identical to hand-written bit literals, with no
//! runtime branch and no lookup table. [`PINMAP`](crate::pinmap::PINMAP) and
//! [`LATCHED_PINMAP`](crate::pinmap::LATCHED_PINMAP) are
//! exported so a calling crate (the driver that owns the DMA/PIO peripheral)
//! can validate at compile time that it drives the panel with the same bit
//! assignments this crate assumes — e.g.
//!
//! ```
//! // The caller pins its own board definition to this crate's assumptions.
//! # #[cfg(not(feature = "interstate75"))]
//! const _: () = assert!(hub75_framebuffer::pinmap::PINMAP.oe == 8);
//! ```
// ---------------------------------------------------------------------------
// The signal -> bit table
// ---------------------------------------------------------------------------
/// Bit position of every HUB75 signal within one framebuffer word.
///
/// Naming note: `color0_*` (the `red0`/`grn0`/`blu0` fields) carry the
/// **upper-half** `R0/G0/B0` triplet and drive the `red1`/`grn1`/`blu1`
/// accessors, while `color1_*` (`red1`/`grn1`/`blu1` fields) carry the
/// **lower-half** `R1/G1/B1` triplet and drive `red2`/`grn2`/`blu2`.
///
/// The three colour signals of a triplet are assumed to be contiguous with red
/// as the least-significant bit (true of every supported layout); the exact
/// per-channel bits are recorded anyway so a driver can verify the wiring.
/// Layout for the 16-bit [`plain`](crate::plain) and
/// [`bitplane::plain`](crate::bitplane::plain) framebuffers, selected per board.
///
/// The default matches any parallel-output peripheral wired in the historical
/// HUB75 bit order used by the ESP32 I2S/PARLIO drivers; the `interstate75`
/// feature selects the Pimoroni Interstate 75 / 75 W (RP2040 / RP2350) layout,
/// which follows the board's contiguous `GPIO0..GPIO13` mapping so a PIO
/// `out pins, 16` stream can drive the panel with no run-time bit shuffling.
pub const PINMAP: PinMap = cfg_select! ;
/// Layout for the 8-bit [`latched`](crate::latched) and
/// [`bitplane::latched`](crate::bitplane::latched) framebuffers.
///
/// This layout is fixed by the external latch circuit, not by the board, so it
/// is not gated by a Cargo feature. It is exported for validation and to keep
/// every signal position in one place.
pub const LATCHED_PINMAP: PinMap = PinMap ;
// ---------------------------------------------------------------------------
// 16-bit layout: derived consts
// ---------------------------------------------------------------------------
/// Shift of the upper-half `R0/G0/B0` colour triplet.
pub const COLOR0_SHIFT: u32 = PINMAP.red0 as u32;
/// Shift of the lower-half `R1/G1/B1` colour triplet.
pub const COLOR1_SHIFT: u32 = PINMAP.red1 as u32;
/// Shift of the 5-bit row address (`A..E`).
pub const ADDR_SHIFT: u32 = PINMAP.addr_lsb as u32;
/// Mask covering the `R0/G0/B0` (upper-half) colour bits.
pub const COLOR0_MASK: u16 = PINMAP.color0_mask;
/// Mask covering the `R1/G1/B1` (lower-half) colour bits.
pub const COLOR1_MASK: u16 = PINMAP.color1_mask;
/// Mask covering every colour bit (`erase()` / `clear_colors()`).
pub const COLOR_MASK: u16 = PINMAP.color_mask;
/// Single-bit mask of the `LATCH` / `STB` signal.
pub const LATCH_BIT: u16 = PINMAP.latch_mask;
/// Single-bit mask of the output-enable signal.
pub const OE_BIT: u16 = PINMAP.oe_mask;
/// Every row-address bit set: the idle/sentinel address parked in the trailing
/// word so the final latch does not select the first row again.
pub const ADDR_MASK: u16 = PINMAP.addr_mask;
// ---------------------------------------------------------------------------
// 8-bit latched layout: derived consts
// ---------------------------------------------------------------------------
/// Single-bit mask of the latched layout's output-enable signal (bit position
/// only; the active/blank *levels* stay local to each module).
pub const LATCHED_OE_BIT: u8 = LATCHED_PINMAP.oe_mask as u8;
/// Single-bit mask of the latched layout's latch signal.
pub const LATCHED_LATCH_BIT: u8 = LATCHED_PINMAP.latch_mask as u8;
/// Mask covering the `R1/G1/B1` colour bits.
pub const LATCHED_COLOR0_MASK: u8 = LATCHED_PINMAP.color0_mask as u8;
/// Mask covering the `R2/G2/B2` colour bits.
pub const LATCHED_COLOR1_MASK: u8 = LATCHED_PINMAP.color1_mask as u8;
/// Mask covering every colour bit.
pub const LATCHED_COLOR_MASK: u8 = LATCHED_PINMAP.color_mask as u8;
// The `OE` *level* constants (`OE_ACTIVE` / `OE_BLANK`) deliberately stay local
// to each framebuffer module: the deprecated `plain` framebuffer and
// `bitplane::plain` use opposite default polarities (see `plain.rs` and
// `bitplane/plain/mod.rs`), and the latched modules use a third fixed layout.
// `pinmap` only owns the bit *position* (`OE_BIT` / `LATCHED_OE_BIT`), so
// relocating `OE` for a board cannot silently flip any polarity.
// ---------------------------------------------------------------------------
// Compile-time sanity checks
// ---------------------------------------------------------------------------
const _: = ;