roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
spi.h
Go to the documentation of this file.
1#pragma once
2
3#if defined(ARDUINO)
4#include <Arduino.h>
5#include <SPI.h>
6#else
7#include "driver/spi_master.h"
8#include "esp_err.h"
9#endif
10
11#include <cstring>
12
13#include "roo_backport.h"
14#include "roo_backport/byte.h"
17#include "roo_io/data/byte_order.h"
18#include "soc/spi_reg.h"
19
20#ifndef ROO_TESTING
21
22namespace roo_display {
23namespace esp32 {
24
25template <uint8_t spi_port, typename SpiSettings>
26class Esp32SpiDevice;
27
28template <uint8_t spi_port>
29class Esp32Spi {
30 public:
31 template <typename SpiSettings>
33
34#if defined(ARDUINO)
35 Esp32Spi() : spi_(SPI) {
36 static_assert(
38 "When using a SPI interface different than the default, you must "
39 "provide a SPIClass object also in the constructor.");
40 }
41
42 Esp32Spi(decltype(SPI)& spi) : spi_(spi) {}
43
44 void init() { spi_.begin(); }
45
46 void init(uint8_t sck, uint8_t miso, uint8_t mosi) {
47 spi_.begin(sck, miso, mosi);
48 }
49
50#else
51 // The host enum is one-off from the spi_port.
53
54 void init() {
56 .mosi_io_num = -1,
57 .miso_io_num = -1,
58 .sclk_io_num = -1,
59 .quadwp_io_num = -1,
60 .quadhd_io_num = -1,
61 .max_transfer_sz = 4096,
62 };
64 }
65
68 .mosi_io_num = mosi,
69 .miso_io_num = miso,
70 .sclk_io_num = sck,
71 .quadwp_io_num = -1,
72 .quadhd_io_num = -1,
73 .max_transfer_sz = 4096,
74 };
76 }
77
78#endif
79
80 private:
81 template <uint8_t, typename SpiSettings>
82 friend class Esp32SpiDevice;
83
84#if defined(ARDUINO)
85 decltype(SPI)& spi_;
86#else
88#endif
89};
90
91// Note: hardcoding the spi_port, making register accesses compile-time
92// constants, does bring tangible performance improvements; ~2% in 'Lines' test
93// in the Adafruit benchmark.
94template <uint8_t spi_port, typename SpiSettings>
96 public:
97 Esp32SpiDevice(Esp32Spi<spi_port>& spi) : spi_(spi.spi_) {}
98
99#if defined(ARDUINO)
100 void init() {}
101
103 spi_.beginTransaction(SPISettings(
105 }
106
108 spi_.beginTransaction(SPISettings(
110 SpiSetWriteOnlyMode(spi_port);
111 }
112
113 void endTransaction() {
114 SpiSetReadWriteMode(spi_port);
115 spi_.endTransaction();
116 }
117#else
118 void init() {
120 .command_bits = 0,
121 .address_bits = 0,
122 .dummy_bits = 0,
124 .clock_source = SPI_CLK_SRC_DEFAULT,
125 .duty_cycle_pos = 128,
126 .cs_ena_pretrans = 0,
127 .cs_ena_posttrans = 0,
128 .clock_speed_hz = SpiSettings::clock,
129 .input_delay_ns = 0,
130 .spics_io_num = -1,
133 : 0},
134 .queue_size = 1,
135 };
136 ESP_ERROR_CHECK(spi_bus_add_device(spi_, &config_, &device_));
137 }
138
142
147
152#endif
153
154 void flush() __attribute__((always_inline)) {
155 if (need_sync_) {
156 need_sync_ = false;
158 }
159 }
160
161 void write(uint8_t data) __attribute__((always_inline)) {
162 flush();
164 SpiWrite4(spi_port, static_cast<uint32_t>(data));
166 need_sync_ = true;
167 }
168
169 void write16(uint16_t data) __attribute__((always_inline)) {
170 flush();
172 SpiWrite4(spi_port, roo_io::htobe(data));
174 need_sync_ = true;
175 }
176
178 flush();
180 SpiWrite4(spi_port, roo_io::htobe(a) | (roo_io::htobe(b) << 16));
182 need_sync_ = true;
183 }
184
185 void writeBytes(const roo::byte* data, uint32_t len) {
186 flush();
187 uintptr_t misalign = reinterpret_cast<uintptr_t>(data) & 0x3u;
188 if (misalign != 0) {
189 uint32_t prefix = 4 - static_cast<uint32_t>(misalign);
190 if (prefix > len) prefix = len;
191 uint32_t word = 0;
196 len -= prefix;
197 if (len == 0) {
198 need_sync_ = true;
199 return;
200 }
201 data += prefix;
203 }
204 // const uint32_t* d32 = reinterpret_cast<const uint32_t*>(data);
205 if (len >= 64) {
207 while (true) {
210 len -= 64;
211 if (len == 0) {
212 need_sync_ = true;
213 return;
214 }
215 data += 64;
217 if (len < 64) break;
218 }
219 }
223 need_sync_ = true;
224 }
225
226 void fill16(const roo::byte* data, uint32_t repetitions) {
227 // Note: ESP32 is little-endian, so we're byte-swapping to
228 // get the bytes sorted correctly in the output register.
229 uint16_t hi = static_cast<uint16_t>(data[1]);
230 uint16_t lo = static_cast<uint16_t>(data[0]);
231 uint16_t d16 = (hi << 8) | lo;
232 uint32_t d32 = (d16 << 16) | d16;
234 if (len < 64) {
235 flush();
239 need_sync_ = true;
240 return;
241 }
242
243 uint32_t rem = len & 63;
244 flush();
246 if (rem != 0) {
249 len -= rem;
251 }
252
254 while (true) {
256 len -= 64;
257 if (len == 0) {
258 need_sync_ = true;
259 return;
260 }
262 }
263 }
264
265 void fill24(const roo::byte* data, uint32_t repetitions) {
266 uint32_t r = static_cast<uint8_t>(data[0]);
267 uint32_t g = static_cast<uint8_t>(data[1]);
268 uint32_t b = static_cast<uint8_t>(data[2]);
269 // Concatenate 4 pixels into three 32 bit blocks.
270 uint32_t d2 = b | r << 8 | g << 16 | b << 24;
271 uint32_t d1 = d2 << 8 | g;
272 uint32_t d0 = d1 << 8 | r;
274 if (len < 60) {
275 flush();
279 need_sync_ = true;
280 return;
281 }
282
283 uint32_t rem = len % 60;
284 flush();
286 if (rem != 0) {
289 len -= rem;
291 }
293 while (true) {
295 len -= 60;
296 if (len == 0) {
297 need_sync_ = true;
298 return;
299 }
301 }
302 }
303
304 void async_blit(const roo::byte* data, size_t row_stride_bytes,
305 size_t row_bytes, size_t row_count) {
306 if (data == nullptr || row_bytes == 0 || row_count == 0) {
307 return;
308 }
309
311 writeBytes(data, static_cast<uint32_t>(row_bytes * row_count));
312 flush();
313 return;
314 }
315
316 const roo::byte* row = data;
317 for (size_t i = 0; i < row_count; ++i) {
318 writeBytes(row, static_cast<uint32_t>(row_bytes));
320 }
321 flush();
322 }
323
324 roo::byte transfer(roo::byte data) __attribute__((always_inline)) {
326 SpiWrite4(spi_port, static_cast<uint32_t>(data));
329 return static_cast<roo::byte>(SpiRead4(spi_port) & 0xFF);
330 }
331
332 uint16_t transfer16(uint16_t data) __attribute__((always_inline)) {
333 // Apply byte-swapping for MSBFIRST (wr_bit_order = 0)
334 data = roo_io::htobe(data);
336 SpiWrite4(spi_port, data);
339 uint16_t result = SpiRead4(spi_port) & 0xFFFF;
340 // Apply byte-swapping for MSBFIRST (rd_bit_order = 0)
341 return roo_io::betoh(result);
342 }
343
344 private:
345#if defined(ARDUINO)
346 decltype(SPI)& spi_;
347#else
349 spi_device_handle_t device_;
350#endif
351 bool need_sync_ = false;
352};
353
354// Original ESP32: SPI0 (none), FSPI -> SPI1, HSPI -> SPI2, VSPI -> SPI3.
355// ESP32S2/S3/P4: FSPI -> SPI2, HSPI -> SPI3.
356// Others: FSPI -> SPI2.
357//
358// Based on
359// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-spi.h
360// and
361// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-spi.c.
362#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || \
363 CONFIG_IDF_TARGET_ESP32P4
364using Fspi = Esp32Spi<2>;
365using Hspi = Esp32Spi<3>;
366#elif CONFIG_IDF_TARGET_ESP32
367using Fspi = Esp32Spi<1>;
368using Hspi = Esp32Spi<2>;
369using Vspi = Esp32Spi<3>;
370#else // ESP32C2, C3, C5, C6, C61, H2
372#endif
373
374} // namespace esp32
375} // namespace roo_display
376
377#else
378// The SPI hardware is not emulated by roo_testing; we need to use a
379// higher-level driver abstraction.
380
382
383namespace roo_display {
384namespace esp32 {
385
386#if CONFIG_IDF_TARGET_ESP32
387using Vspi = ArduinoSpi;
388using Hspi = ArduinoSpi;
389#endif
390using Fspi = ArduinoSpi;
391
392} // namespace esp32
393} // namespace roo_display
394
395#endif
Esp32SpiDevice(Esp32Spi< spi_port > &spi)
Definition spi.h:97
void write(uint8_t data) __attribute__((always_inline))
Definition spi.h:161
void write16x2(uint16_t a, uint16_t b) __attribute((always_inline))
Definition spi.h:177
uint16_t transfer16(uint16_t data) __attribute__((always_inline))
Definition spi.h:332
void async_blit(const roo::byte *data, size_t row_stride_bytes, size_t row_bytes, size_t row_count)
Definition spi.h:304
void fill16(const roo::byte *data, uint32_t repetitions)
Definition spi.h:226
void writeBytes(const roo::byte *data, uint32_t len)
Definition spi.h:185
void fill24(const roo::byte *data, uint32_t repetitions)
Definition spi.h:265
void flush() __attribute__((always_inline))
Definition spi.h:154
roo::byte transfer(roo::byte data) __attribute__((always_inline))
Definition spi.h:324
void write16(uint16_t data) __attribute__((always_inline))
Definition spi.h:169
void init(uint8_t sck, uint8_t miso, uint8_t mosi)
Definition spi.h:66
Esp32Spi< 2 > Fspi
Definition spi.h:371
void SpiSetWriteOnlyMode(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:210
void SpiFill60(uint8_t spi_port, uint32_t d0, uint32_t d1, uint32_t d2) __attribute__((always_inline))
Definition spi_reg.h:335
void SpiFill64(uint8_t spi_port, uint32_t d32) __attribute__((always_inline))
Definition spi_reg.h:280
void SpiWriteUpTo64Aligned(uint8_t spi_port, const roo::byte *data, int len) __attribute__((always_inline))
Definition spi_reg.h:242
void SpiTxWait(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:147
void SpiSetOutBufferSize(uint8_t spi_port, int len) __attribute__((always_inline))
Definition spi_reg.h:194
void SpiTxStart(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:177
void SpiWrite4(uint8_t spi_port, uint32_t d32) __attribute__((always_inline))
Definition spi_reg.h:234
uint32_t SpiRead4(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:238
void SpiSetTxBufferSize(uint8_t spi_port, int len) __attribute__((always_inline))
Definition spi_reg.h:198
void SpiSetReadWriteMode(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:205
void SpiWrite64Aligned(uint8_t spi_port, const roo::byte *data) __attribute__((always_inline))
Definition spi_reg.h:214
void SpiFillUpTo60(uint8_t spi_port, uint32_t d0, uint32_t d1, uint32_t d2, int len) __attribute__((always_inline))
Definition spi_reg.h:353
void SpiFillUpTo64(uint8_t spi_port, uint32_t d32, int len) __attribute__((always_inline))
Definition spi_reg.h:299
Defines 140 opaque HTML named colors.
float r
Definition smooth.cpp:474
#define ROO_DISPLAY_ESP32_SPI_DEFAULT_PORT
Definition spi_reg.h:12
static constexpr uint32_t clock
static constexpr SpiBitOrder bit_order
static constexpr SpiDataMode data_mode