FastLED 3.9.15
Loading...
Searching...
No Matches

◆ decodeSpiEdges()

static size_t decodeSpiEdges ( fl::shared_ptr< fl::RxChannel > rx_channel,
fl::span< uint8_t > rx_buffer,
uint32_t clock_hz )
static

Definition at line 174 of file AutoResearchTest.cpp.

176 {
177 if (!rx_channel || clock_hz == 0) {
178 FL_WARN("[SPI DECODE] Invalid parameters");
179 return 0;
180 }
181
182 const uint32_t bit_period_ns = static_cast<uint32_t>(1000000000ULL / clock_hz);
183 const uint32_t half_bit_ns = bit_period_ns / 2;
184 FL_WARN("[SPI DECODE] clock=" << clock_hz << " Hz, bit_period=" << bit_period_ns << " ns");
185
186 // Read raw edges (up to 4096 to handle large strips)
187 constexpr size_t MAX_EDGES = 4096;
188 fl::vector<fl::EdgeTime> edges(MAX_EDGES);
189 fl::span<fl::EdgeTime> edge_span(edges.data(), edges.size());
190 size_t edge_count = rx_channel->getRawEdgeTimes(edge_span, 0);
191
192 if (edge_count == 0) {
193 FL_WARN("[SPI DECODE] No edges captured");
194 return 0;
195 }
196 FL_WARN("[SPI DECODE] Captured " << edge_count << " edges");
197
198 // Reconstruct bit stream from edges
199 // Each edge has a level (high/low) and duration in ns.
200 // Number of bits = round(duration_ns / bit_period_ns)
202 bits.reserve(edge_count * 4); // Rough estimate
203
204 for (size_t i = 0; i < edge_count; i++) {
205 uint32_t duration = edges[i].ns;
206 uint32_t num_bits = (duration + half_bit_ns) / bit_period_ns;
207 if (num_bits == 0) num_bits = 1; // At least 1 bit per edge
208 uint8_t bit_val = edges[i].high ? 1 : 0;
209 for (uint32_t b = 0; b < num_bits; b++) {
210 bits.push_back(bit_val);
211 }
212 }
213
214 FL_WARN("[SPI DECODE] Reconstructed " << bits.size() << " bits");
215
216 if (bits.size() < 32) {
217 FL_WARN("[SPI DECODE] Too few bits for APA102 frame");
218 return 0;
219 }
220
221 // Convert bits to bytes (MSB first)
222 size_t total_bytes = bits.size() / 8;
223 fl::vector<uint8_t> raw_bytes(total_bytes);
224 for (size_t i = 0; i < total_bytes; i++) {
225 uint8_t byte_val = 0;
226 for (int bit = 7; bit >= 0; bit--) {
227 size_t bit_idx = i * 8 + (7 - bit);
228 if (bit_idx < bits.size() && bits[bit_idx]) {
229 byte_val |= (1 << bit);
230 }
231 }
232 raw_bytes[i] = byte_val;
233 }
234
235 FL_WARN("[SPI DECODE] Decoded " << total_bytes << " raw bytes");
236
237 // Log first few bytes for debugging
238 {
239 fl::sstream dbg;
240 dbg << "[SPI DECODE] First bytes:";
241 size_t show = total_bytes < 20 ? total_bytes : 20;
242 for (size_t i = 0; i < show; i++) {
243 dbg << " 0x";
244 uint8_t hi = raw_bytes[i] >> 4;
245 uint8_t lo = raw_bytes[i] & 0xF;
246 dbg << (char)(hi < 10 ? '0' + hi : 'A' + hi - 10);
247 dbg << (char)(lo < 10 ? '0' + lo : 'A' + lo - 10);
248 }
249 FL_WARN(dbg.str());
250 }
251
252 // Find APA102 start frame (4 bytes of 0x00)
253 // The RMT may not capture the start frame since it's all zeros and
254 // the idle state is also LOW. In that case, the first captured data
255 // is the first LED frame header (0xE0 | brightness).
256 size_t data_start = 0;
257
258 // Check if we captured the start frame
259 if (total_bytes >= 4 && raw_bytes[0] == 0x00 && raw_bytes[1] == 0x00 &&
260 raw_bytes[2] == 0x00 && raw_bytes[3] == 0x00) {
261 data_start = 4; // Skip start frame
262 FL_WARN("[SPI DECODE] Found start frame at offset 0");
263 } else {
264 // No start frame captured (RMT started at first edge = first HIGH bit)
265 // First byte should be 0xE0|brightness or 0xFF
266 FL_WARN("[SPI DECODE] No start frame (first edge = first LED data)");
267 }
268
269 // Extract LED RGB data from APA102 frames
270 // Each LED: [0xE0|brightness] [color0] [color1] [color2] (4 bytes)
271 // With EOrder=RGB in the validation config, the wire order after the
272 // brightness header is [R, G, B] — we copy these directly to rx_buffer.
273 // NOTE: The APA102 end frame (all 0xFF) also has (0xFF & 0xE0) == 0xE0,
274 // so we cannot distinguish end frame from a max-brightness LED purely
275 // from the header byte. We stop after we've exhausted captured data.
276 size_t led_bytes_written = 0;
277 size_t pos = data_start;
278
279 while (pos + 4 <= total_bytes) {
280 uint8_t header = raw_bytes[pos];
281
282 // Check for valid LED frame header (top 3 bits must be 111)
283 if ((header & 0xE0) != 0xE0) {
284 FL_WARN("[SPI DECODE] End of LED data at byte " << pos
285 << " (header=0x" << ((header >> 4) < 10 ? '0' + (header >> 4) : 'A' + (header >> 4) - 10)
286 << ((header & 0xF) < 10 ? '0' + (header & 0xF) : 'A' + (header & 0xF) - 10) << ")");
287 break;
288 }
289
290 uint8_t c0 = raw_bytes[pos + 1];
291 uint8_t c1 = raw_bytes[pos + 2];
292 uint8_t c2 = raw_bytes[pos + 3];
293
294 if (led_bytes_written + 3 <= rx_buffer.size()) {
295 rx_buffer[led_bytes_written + 0] = c0;
296 rx_buffer[led_bytes_written + 1] = c1;
297 rx_buffer[led_bytes_written + 2] = c2;
298 led_bytes_written += 3;
299 } else {
300 FL_WARN("[SPI DECODE] rx_buffer full at LED " << (led_bytes_written / 3));
301 break;
302 }
303 pos += 4;
304 }
305
306 size_t num_leds = led_bytes_written / 3;
307 FL_WARN("[SPI DECODE] Extracted " << num_leds << " LEDs (" << led_bytes_written << " RGB bytes)");
308 return led_bytes_written;
309}
uint8_t pos
Definition Blur.ino:11
FastLED show()
size_t getRawEdgeTimes(fl::span< EdgeTime > out, size_t offset=0) FL_NOEXCEPT
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
string str() const FL_NOEXCEPT
Definition strstream.h:43
fl::size size() const FL_NOEXCEPT
void reserve(fl::size n) FL_NOEXCEPT
Definition vector.h:591
void push_back(const T &value) FL_NOEXCEPT
Definition vector.h:624
#define FL_WARN(X)
Definition log.h:276
fl::u32 uint32_t
Definition s16x16x4.h:219
unsigned char uint8_t
Definition s16x16x4.h:209

References fl::vector< T >::data(), FL_WARN, pos, fl::vector< T >::push_back(), fl::vector< T >::reserve(), show(), fl::span< T, Extent >::size(), fl::vector_basic::size(), and fl::sstream::str().

Referenced by capture().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: