TinyGPU
Loading...
Searching...
No Matches
QSPIBusBitBang.h
Go to the documentation of this file.
1#pragma once
2#include <stdint.h>
3
4#include "TinyGPU/Abstractions/IQSPIBus.h"
5#include "TinyGPU/Emulation.h"
6
7namespace tinygpu {
8
9/**
10 * @brief Portable, software-driven IQSPIBus backend built only on
11 * pinMode()/digitalWrite() - works on any Arduino core, notably RP2040 and
12 * STM32, neither of which exposes a hardware peripheral for this panel
13 * family's quad-SPI protocol the way ESP-IDF's esp_lcd does for ESP32
14 * (see QSPIBusESP32.h):
15 * - RP2040 has no dedicated quad-SPI-out peripheral; its PIO blocks
16 * could bit-bang this protocol at much higher throughput with a
17 * custom PIO program, but that's a real follow-up project, not
18 * something to get subtly wrong un-tested against real hardware.
19 * - STM32's QUADSPI/OCTOSPI peripheral (where present - H7/F7/L4+/...,
20 * not every part) is designed for external flash/PSRAM (address-phase
21 * memory-mapped or indirect reads/writes), not for driving a display
22 * controller's command/pixel-write protocol, and its pin/AF/clock
23 * setup is per-variant CubeMX territory - not something this library
24 * can wire up generically without a specific target board.
25 *
26 * Reproduces the same wire protocol QSPIBusESP32/esp_lcd's quad_mode
27 * produces (see IQSPIBus.h and DisplayDriverQSPI.h's class doc comment):
28 * the 32-bit {opcode, cmd, 0x00, 0x00} frame is always clocked out over
29 * all 4 data lines (a nibble per clock, MSB-first); what follows is
30 * clocked out over D0 only (1 bit/clock) for a 0x02 ("param") frame, or
31 * over all 4 lines (a nibble/clock) for a 0x32 ("color") frame.
32 *
33 * This is correctness-first, not throughput-first: each bit is one
34 * digitalWrite() pair (data setup + clock pulse), so a full-screen
35 * writeColor() call is far slower here than on QSPIBusESP32's DMA path.
36 * Good enough to bring a panel up and validate the higher layers
37 * (NV3041ADriver, ...) on RP2040/STM32; treat clock-limited frame rate as
38 * an expected tradeoff of this backend, not a bug.
39 */
40class QSPIBusBitBang : public IQSPIBus {
41 public:
42 QSPIBusBitBang(int8_t cs, int8_t sclk, int8_t d0, int8_t d1, int8_t d2,
43 int8_t d3)
44 : cs_(cs), sclk_(sclk), d0_(d0), d1_(d1), d2_(d2), d3_(d3) {}
45
46 bool begin() override {
47 pinMode(cs_, OUTPUT);
48 pinMode(sclk_, OUTPUT);
49 pinMode(d0_, OUTPUT);
50 pinMode(d1_, OUTPUT);
51 pinMode(d2_, OUTPUT);
52 pinMode(d3_, OUTPUT);
53 digitalWrite(cs_, HIGH);
54 digitalWrite(sclk_, LOW); // SPI mode 0: clock idles low
55 return true;
56 }
57
58 void end() override {}
59
60 bool writeCommand(uint8_t cmd, const uint8_t* param, size_t len) override {
61 digitalWrite(cs_, LOW);
62 sendFrameQuad(kOpcodeParam, cmd);
63 for (size_t i = 0; i < len; ++i) sendByteSingle(param[i]);
64 digitalWrite(cs_, HIGH);
65 return true;
66 }
67
68 bool writeColor(uint8_t cmd, const uint8_t* data, size_t len) override {
69 digitalWrite(cs_, LOW);
70 sendFrameQuad(kOpcodeColor, cmd);
71 for (size_t i = 0; i < len; ++i) sendByteQuad(data[i]);
72 digitalWrite(cs_, HIGH);
73 return true;
74 }
75
76 private:
77 static constexpr uint8_t kOpcodeParam = 0x02;
78 static constexpr uint8_t kOpcodeColor = 0x32;
79
80 // Clocks one rising edge with the data lines already set - SPI mode 0
81 // (CPOL=0, CPHA=0): data is set up while the clock is low, then sampled
82 // by the panel on the rising edge.
83 void clockPulse() {
84 digitalWrite(sclk_, HIGH);
85 digitalWrite(sclk_, LOW);
86 }
87
88 void sendNibble(uint8_t nibble) {
89 digitalWrite(d0_, (nibble >> 0) & 1);
90 digitalWrite(d1_, (nibble >> 1) & 1);
91 digitalWrite(d2_, (nibble >> 2) & 1);
92 digitalWrite(d3_, (nibble >> 3) & 1);
93 clockPulse();
94 }
95
96 void sendByteQuad(uint8_t b) {
97 sendNibble((b >> 4) & 0x0F);
98 sendNibble(b & 0x0F);
99 }
100
101 void sendByteSingle(uint8_t b) {
102 for (int8_t bit = 7; bit >= 0; --bit) {
103 digitalWrite(d0_, (b >> bit) & 1);
104 clockPulse();
105 }
106 }
107
108 // The 32-bit {opcode, cmd, 0x00, 0x00} command frame, always sent quad
109 // (8 nibbles), matching esp_lcd's quad_mode=1 framing (see IQSPIBus.h).
110 void sendFrameQuad(uint8_t opcode, uint8_t cmd) {
111 sendByteQuad(opcode);
112 sendByteQuad(cmd);
113 sendByteQuad(0x00);
114 sendByteQuad(0x00);
115 }
116
117 int8_t cs_, sclk_, d0_, d1_, d2_, d3_;
118};
119
120} // namespace tinygpu
Abstraction over the transport used by DisplayDriverQSPI (see TinyGPU/Drivers/DisplayDriverQSPI....
Definition: IQSPIBus.h:24
Portable, software-driven IQSPIBus backend built only on pinMode()/digitalWrite() - works on any Ardu...
Definition: QSPIBusBitBang.h:40
QSPIBusBitBang(int8_t cs, int8_t sclk, int8_t d0, int8_t d1, int8_t d2, int8_t d3)
Definition: QSPIBusBitBang.h:42
bool writeCommand(uint8_t cmd, const uint8_t *param, size_t len) override
Definition: QSPIBusBitBang.h:60
void end() override
Definition: QSPIBusBitBang.h:58
bool begin() override
Sets up the bus (pins/peripheral). Returns false on failure.
Definition: QSPIBusBitBang.h:46
bool writeColor(uint8_t cmd, const uint8_t *data, size_t len) override
Definition: QSPIBusBitBang.h:68
Definition: DSIBusESP32.h:19