ISF15ACP4 Driver 1.0.0
API reference
Loading...
Searching...
No Matches
isf15acp4.ipp
Go to the documentation of this file.
1#ifndef ISF15ACP4_HEADER_INCLUDED
2#error "isf15acp4.ipp must be included from isf15acp4.hpp"
3#endif
4
5namespace isf15acp4 {
6namespace detail {
7
8template <typename SpiType>
9bool presenterDrawPixel(void* ctx, uint8_t x, uint8_t y, Color565 color) noexcept {
10 return static_cast<SmartDisplay<SpiType>*>(ctx)->DrawPixel(x, y, color);
11}
12
13template <typename SpiType>
14bool presenterDrawLine(void* ctx, Point a, Point b, Color565 color) noexcept {
15 return static_cast<SmartDisplay<SpiType>*>(ctx)->DrawLine(a, b, color);
16}
17
18template <typename SpiType>
19bool presenterDrawRect(void* ctx, Rect r, Color565 color, bool filled) noexcept {
20 return static_cast<SmartDisplay<SpiType>*>(ctx)->DrawRectangle(r, color, filled);
21}
22
23template <typename SpiType>
24bool presenterFillScreen(void* ctx, Color565 color) noexcept {
25 return static_cast<SmartDisplay<SpiType>*>(ctx)->FillScreen(color);
26}
27
28template <typename SpiType>
29bool presenterFlushCanvas(void* ctx, const Color565* data, std::size_t bytes) noexcept {
30 return static_cast<SmartDisplay<SpiType>*>(ctx)->WriteColors(data, bytes / sizeof(Color565));
31}
32
33} // namespace detail
34
35template <typename SpiType>
36SmartDisplay<SpiType>::SmartDisplay(SpiType* bus, const SmartDisplayConfig& config) noexcept
37 : bus_(bus), config_(config), variant_(config.variant), geometry_(GetGeometry(config.variant)) {
38 window_ = {0, 0, static_cast<uint8_t>(geometry_.width - 1), static_cast<uint8_t>(geometry_.height - 1)};
39}
40
41template <typename SpiType>
43 : SmartDisplay(bus, SmartDisplayConfig{.variant = variant}) {}
44
45template <typename SpiType>
47 last_error_ = e;
48 error_flags_ |= static_cast<uint16_t>(e);
49}
50
51template <typename SpiType>
53 // SSD1331 rev 1.2, section 7.9 and Table 18: hold RES# low (Reset asserted = Active) for at least 3 us, release
54 // it high (Inactive), then allow the reset completion time (2 us max) before the first command.
55 bus_->GpioSetActive(CtrlPin::Reset);
56 bus_->DelayUs(ssd1331::kResetPulseMinUs);
57 bus_->GpioSetInactive(CtrlPin::Reset);
58 bus_->DelayUs(ssd1331::kResetRecoveryUs);
59}
60
61template <typename SpiType>
62bool SmartDisplay<SpiType>::sendCommand(uint8_t cmd, const uint8_t* data, uint8_t len) noexcept {
63 if (!bus_->BeginCommand()) {
64 setError(Error::SpiWrite);
65 return false;
66 }
67
68 if (!bus_->WriteBytes(&cmd, 1)) {
69 bus_->EndTransaction();
70 setError(Error::SpiWrite);
71 return false;
72 }
73
74 if (len > 0 && data != nullptr) {
75 if (!bus_->WriteBytes(data, len)) {
76 bus_->EndTransaction();
77 setError(Error::SpiWrite);
78 return false;
79 }
80 }
81
82 bus_->EndTransaction();
83 return true;
84}
85
86template <typename SpiType>
87bool SmartDisplay<SpiType>::sendCommandByte(uint8_t cmd, uint8_t data) noexcept {
88 return sendCommand(cmd, &data, 1);
89}
90
91template <typename SpiType>
92bool SmartDisplay<SpiType>::sendCommandOnly(uint8_t cmd) noexcept {
93 return sendCommand(cmd, nullptr, 0);
94}
95
96template <typename SpiType>
98 std::size_t count = 0;
99 const ssd1331::InitStep* table = ssd1331::GetInitTable(variant_, count);
100
101 for (std::size_t i = 0; i < count; ++i) {
102 if (!sendCommand(table[i].command, table[i].data, table[i].data_len)) {
103 return false;
104 }
105 }
106 return true;
107}
108
109template <typename SpiType>
110bool SmartDisplay<SpiType>::Initialize(bool enable_vcc) noexcept {
111 if (bus_ == nullptr) {
112 setError(Error::InvalidParam);
113 return false;
114 }
115
116 if (!bus_->EnsureInitialized()) {
117 setError(Error::NotInitialized);
118 return false;
119 }
120
121 bus_->GpioSetInactive(CtrlPin::ChipSelect);
122 bus_->GpioSetActive(CtrlPin::DataCommand);
123 bus_->GpioSetInactive(CtrlPin::VccEnable);
124
125 pulseReset();
126
127 if (enable_vcc) {
128 bus_->GpioSetActive(CtrlPin::VccEnable);
129 bus_->DelayUs(1000);
130 }
131
132 if (!runInitTable()) {
133 return false;
134 }
135
136 if (!SetFullWindow()) {
137 return false;
138 }
139
140 initialized_ = true;
141 return true;
142}
143
144template <typename SpiType>
145bool SmartDisplay<SpiType>::Reset(bool enable_vcc) noexcept {
146 initialized_ = false;
147 return Initialize(enable_vcc);
148}
149
150template <typename SpiType>
152 return sendCommandOnly(static_cast<uint8_t>(ssd1331::Command::DisplayOn));
153}
154
155template <typename SpiType>
157 return sendCommandOnly(static_cast<uint8_t>(ssd1331::Command::DisplayOff));
158}
159
160template <typename SpiType>
162 return sendCommandOnly(0xAC);
163}
164
165template <typename SpiType>
166bool SmartDisplay<SpiType>::SetMasterCurrent(uint8_t level) noexcept {
167 if (level > 0x0F) {
168 setError(Error::OutOfRange);
169 return false;
170 }
171 return sendCommandByte(static_cast<uint8_t>(ssd1331::Command::MasterCurrent), level);
172}
173
174template <typename SpiType>
175bool SmartDisplay<SpiType>::SetWindow(const Rect& rect) noexcept {
176 if (rect.x1 < rect.x0 || rect.y1 < rect.y0 || rect.x1 >= geometry_.width ||
177 rect.y1 >= geometry_.height) {
178 setError(Error::OutOfRange);
179 return false;
180 }
181
182 // Display coordinates to controller RAM addresses: the ISC15ANP4 and ISC01P show a window of the 96 x 64 RAM
183 // starting at column 10h / 16h (NKK Application Notes rev H, Tables 4 and 5).
184 const Rect ram = toRam(rect);
185 const uint8_t col_data[2] = {ram.x0, ram.x1};
186 const uint8_t row_data[2] = {ram.y0, ram.y1};
187
188 if (!sendCommand(static_cast<uint8_t>(ssd1331::Command::SetColumnAddress), col_data, 2)) {
189 return false;
190 }
191 if (!sendCommand(static_cast<uint8_t>(ssd1331::Command::SetRowAddress), row_data, 2)) {
192 return false;
193 }
194
195 window_ = rect;
196 return true;
197}
198
199template <typename SpiType>
201 return SetWindow({0, 0, static_cast<uint8_t>(geometry_.width - 1), static_cast<uint8_t>(geometry_.height - 1)});
202}
203
204template <typename SpiType>
205Rect SmartDisplay<SpiType>::toRam(const Rect& rect) const noexcept {
206 return {static_cast<uint8_t>(rect.x0 + geometry_.column_start), static_cast<uint8_t>(rect.y0 + geometry_.row_start),
207 static_cast<uint8_t>(rect.x1 + geometry_.column_start), static_cast<uint8_t>(rect.y1 + geometry_.row_start)};
208}
209
210template <typename SpiType>
211bool SmartDisplay<SpiType>::WritePixels(const uint8_t* data, std::size_t byte_len) noexcept {
212 if (!initialized_) {
213 setError(Error::NotInitialized);
214 return false;
215 }
216 if (data == nullptr || byte_len == 0 || (byte_len % 2) != 0) {
217 setError(Error::InvalidParam);
218 return false;
219 }
220
221 if (!bus_->BeginData()) {
222 setError(Error::SpiWrite);
223 return false;
224 }
225
226 if (!bus_->WriteBytes(data, byte_len)) {
227 bus_->EndTransaction();
228 setError(Error::SpiWrite);
229 return false;
230 }
231
232 bus_->EndTransaction();
233 return true;
234}
235
236template <typename SpiType>
237bool SmartDisplay<SpiType>::WriteColors(const Color565* colors, std::size_t count) noexcept {
238 if (!initialized_) {
239 setError(Error::NotInitialized);
240 return false;
241 }
242 if (colors == nullptr || count == 0) {
243 setError(Error::InvalidParam);
244 return false;
245 }
246
247 if (!bus_->BeginData()) {
248 setError(Error::SpiWrite);
249 return false;
250 }
251
252 // SSD1331 rev 1.2 section 7.5.2, Table 9 (65k colour, 8-bit interface): first byte C4..C0 B5..B3, second byte
253 // B2..B0 A4..A0, i.e. the 16-bit pixel high byte first, with C = blue and A = red on the NKK panels
254 // (ToPanelPixel). Serialise explicitly so the CPU's byte order does not matter, in chunks inside one data
255 // transaction.
256 constexpr std::size_t kChunkPixels = 64;
257 uint8_t chunk[kChunkPixels * 2];
258 std::size_t done = 0;
259 while (done < count) {
260 const std::size_t n = (count - done) < kChunkPixels ? (count - done) : kChunkPixels;
261 for (std::size_t i = 0; i < n; ++i) {
262 const uint16_t c = ssd1331::ToPanelPixel(colors[done + i]);
263 chunk[2 * i] = static_cast<uint8_t>(c >> 8);
264 chunk[2 * i + 1] = static_cast<uint8_t>(c & 0xFF);
265 }
266 if (!bus_->WriteBytes(chunk, n * 2)) {
267 bus_->EndTransaction();
268 setError(Error::SpiWrite);
269 return false;
270 }
271 done += n;
272 }
273
274 bus_->EndTransaction();
275 return true;
276}
277
278template <typename SpiType>
279bool SmartDisplay<SpiType>::DrawCanvas(const Canvas& canvas) noexcept {
280 if (canvas.Width() != geometry_.width || canvas.Height() != geometry_.height) {
281 setError(Error::InvalidParam);
282 return false;
283 }
284 if (!SetFullWindow()) {
285 return false;
286 }
287 return WriteColors(canvas.Data(), canvas.ByteSize() / sizeof(Color565));
288}
289
290template <typename SpiType>
292 return GraphicsContext(variant_, config_.graphics);
293}
294
295template <typename SpiType>
297 GraphicsContext gfx(variant_, config_.graphics);
298 if (config_.graphics == GraphicsBackend::HardwareDirect) {
299 ConfigurePresenter(gfx);
300 }
301 return gfx;
302}
303
304template <typename SpiType>
306 DisplayPresenter presenter{};
307 presenter.context = this;
308 presenter.draw_pixel = &detail::presenterDrawPixel<SpiType>;
309 presenter.draw_line = &detail::presenterDrawLine<SpiType>;
310 presenter.draw_rect = &detail::presenterDrawRect<SpiType>;
311 presenter.fill_screen = &detail::presenterFillScreen<SpiType>;
312 presenter.flush_canvas = &detail::presenterFlushCanvas<SpiType>;
313 gfx.BindPresenter(presenter);
314}
315
316template <typename SpiType>
318 if (gfx.Backend() == GraphicsBackend::HardwareDirect) {
319 return true;
320 }
321 return DrawCanvas(gfx.CanvasBuffer());
322}
323
324template <typename SpiType>
325bool SmartDisplay<SpiType>::DrawPixel(uint8_t x, uint8_t y, Color565 color) noexcept {
326 if (!SetWindow({x, y, x, y})) {
327 return false;
328 }
329 return WriteColors(&color, 1);
330}
331
332template <typename SpiType>
334 Canvas canvas(variant_);
335 canvas.Clear(color);
336 return DrawCanvas(canvas);
337}
338
339template <typename SpiType>
340void SmartDisplay<SpiType>::colorToSsd1331(Color565 color, uint8_t out[3]) noexcept {
341 // NKK panels: colour A = red, B = green, C = blue (NKK Application Notes rev H section 9; init A0h bit 2 = 0).
342 out[0] = static_cast<uint8_t>((color >> 11) & 0x1F); // A: red 5-bit
343 out[1] = static_cast<uint8_t>((color >> 5) & 0x3F); // B: green 6-bit
344 out[2] = static_cast<uint8_t>(color & 0x1F); // C: blue 5-bit
345}
346
347template <typename SpiType>
348bool SmartDisplay<SpiType>::DrawLine(const Point& start, const Point& end, Color565 color) noexcept {
349 uint8_t rgb[3];
350 colorToSsd1331(color, rgb);
351 const Rect ram = toRam({start.x, start.y, end.x, end.y});
352 const uint8_t data[7] = {ram.x0, ram.y0, ram.x1, ram.y1,
353 static_cast<uint8_t>(rgb[2] << 1), rgb[1],
354 static_cast<uint8_t>(rgb[0] << 1)};
355 return sendCommand(static_cast<uint8_t>(ssd1331::Command::DrawLine), data, sizeof(data));
356}
357
358template <typename SpiType>
359bool SmartDisplay<SpiType>::DrawRectangle(const Rect& rect, Color565 color, bool filled) noexcept {
360 if (filled) {
361 const uint8_t fill_cfg[2] = {0x01, 0x00};
362 if (!sendCommand(static_cast<uint8_t>(ssd1331::Command::FillEnable), fill_cfg, 2)) {
363 return false;
364 }
365 }
366
367 uint8_t rgb[3];
368 colorToSsd1331(color, rgb);
369 const Rect ram = toRam(rect);
370 const uint8_t data[10] = {ram.x0, ram.y0, ram.x1, ram.y1,
371 static_cast<uint8_t>(rgb[2] << 1), rgb[1],
372 static_cast<uint8_t>(rgb[0] << 1),
373 static_cast<uint8_t>(rgb[2] << 1), rgb[1],
374 static_cast<uint8_t>(rgb[0] << 1)};
375 const bool ok = sendCommand(static_cast<uint8_t>(ssd1331::Command::DrawRectangle), data,
376 sizeof(data));
377
378 if (filled) {
379 const uint8_t disable[2] = {0x00, 0x00};
380 sendCommand(static_cast<uint8_t>(ssd1331::Command::FillEnable), disable, 2);
381 }
382 return ok;
383}
384
385template <typename SpiType>
386bool SmartDisplay<SpiType>::ClearWindow(const Rect& rect) noexcept {
387 const Rect ram = toRam(rect);
388 const uint8_t data[4] = {ram.x0, ram.y0, ram.x1, ram.y1};
389 return sendCommand(static_cast<uint8_t>(ssd1331::Command::ClearWindow), data, sizeof(data));
390}
391
392template <typename SpiType>
393bool SmartDisplay<SpiType>::IsPressed() const noexcept {
394 return bus_ != nullptr && bus_->GpioRead(CtrlPin::Switch);
395}
396
397template <typename SpiType>
398bool SmartDisplay<SpiType>::ConfigureScroll(uint8_t h_offset, uint8_t start_row, uint8_t scroll_rows,
399 uint8_t v_offset, uint8_t interval_code,
400 bool activate) noexcept {
401 // SSD1331 rev 1.2 Table 10, 27h note 2: the parameters must not change while scrolling is active, so stop
402 // first (NKK Application Notes rev H section 12: send 27h only with scrolling deactivated).
403 if (!StopScroll()) {
404 return false;
405 }
406 const uint8_t data[5] = {h_offset, start_row, scroll_rows, v_offset,
407 static_cast<uint8_t>(interval_code & 0x03)};
408 if (!sendCommand(static_cast<uint8_t>(ssd1331::Command::ScrollSetup), data, sizeof(data))) {
409 return false;
410 }
411 if (!activate) {
412 return true;
413 }
414 return sendCommandOnly(static_cast<uint8_t>(ssd1331::Command::ActivateScroll));
415}
416
417template <typename SpiType>
419 return sendCommandOnly(static_cast<uint8_t>(ssd1331::Command::DeactivateScroll));
420}
421
422template <typename SpiType>
424 // SSD1331 rev 1.2 Table 10, 2Eh note: after deactivating, the RAM data needs to be rewritten.
425 if (!StopScroll()) {
426 return false;
427 }
428 return Present(gfx);
429}
430
431} // namespace isf15acp4
CPU-side RGB565 framebuffer with basic drawing operations.
Definition isf15acp4_canvas.hpp:22
void Clear(Color565 color) noexcept
Clear the framebuffer to a solid color.
Definition isf15acp4_canvas.ipp:13
Owns the active graphics backend selected at construction time.
Definition isf15acp4_graphics_context.hpp:42
High-level driver for NKK SSD1331-based SmartDisplay modules.
Definition isf15acp4.hpp:31
bool ClearWindow(const Rect &rect) noexcept
Clear a rectangular window via controller command 25H, in display coordinates.
Definition isf15acp4.ipp:386
void setError(Error e) noexcept
Definition isf15acp4.ipp:46
bool sendCommand(uint8_t cmd, const uint8_t *data, uint8_t len) noexcept
Definition isf15acp4.ipp:62
bool StopScroll() noexcept
Stop hardware scrolling (2EH).
Definition isf15acp4.ipp:418
bool DrawLine(const Point &start, const Point &end, Color565 color) noexcept
Draw a hardware-accelerated line (command 21H), in display coordinates.
Definition isf15acp4.ipp:348
bool IsPressed() const noexcept
Read momentary switch state when wired to SpiInterface::CtrlPin::Switch.
Definition isf15acp4.ipp:393
bool runInitTable() noexcept
Definition isf15acp4.ipp:97
void ConfigurePresenter(GraphicsContext &gfx) noexcept
Register presenter hooks for HardwareDirect backend (bind this display to gfx).
Definition isf15acp4.ipp:305
bool DrawRectangle(const Rect &rect, Color565 color, bool filled=false) noexcept
Draw a hardware rectangle outline (command 22H), in display coordinates.
Definition isf15acp4.ipp:359
Rect toRam(const Rect &rect) const noexcept
Definition isf15acp4.ipp:205
bool DisplayOn() noexcept
Turn display on (normal mode, command AFH).
Definition isf15acp4.ipp:151
SmartDisplay(SpiType *bus, const SmartDisplayConfig &config) noexcept
Construct with explicit configuration (variant + graphics backend).
Definition isf15acp4.ipp:36
bool SetWindow(const Rect &rect) noexcept
Configure the active GRAM download window.
Definition isf15acp4.ipp:175
bool DisplayDim() noexcept
Enter dim mode (command ACH).
Definition isf15acp4.ipp:161
bool DisplayOff() noexcept
Turn display off / sleep (command AEH).
Definition isf15acp4.ipp:156
bool SetMasterCurrent(uint8_t level) noexcept
Set master current while display is off (command 87H).
Definition isf15acp4.ipp:166
bool DrawCanvas(const Canvas &canvas) noexcept
Upload an entire canvas framebuffer.
Definition isf15acp4.ipp:279
void pulseReset() noexcept
Definition isf15acp4.ipp:52
bool DrawPixel(uint8_t x, uint8_t y, Color565 color) noexcept
Draw one RGB565 pixel at (x,y).
Definition isf15acp4.ipp:325
bool ConfigureScroll(uint8_t h_offset, uint8_t start_row, uint8_t scroll_rows, uint8_t v_offset, uint8_t interval_code, bool activate) noexcept
Set up hardware scrolling and optionally start it.
Definition isf15acp4.ipp:398
bool FillScreen(Color565 color) noexcept
Fill the panel with a solid color.
Definition isf15acp4.ipp:333
bool sendCommandByte(uint8_t cmd, uint8_t data) noexcept
Definition isf15acp4.ipp:87
bool Reset(bool enable_vcc=true) noexcept
Hardware reset pulse and full re-initialization.
Definition isf15acp4.ipp:145
bool sendCommandOnly(uint8_t cmd) noexcept
Definition isf15acp4.ipp:92
bool WritePixels(const uint8_t *data, std::size_t byte_len) noexcept
Stream raw pixel bytes to the controller GRAM, unchanged.
Definition isf15acp4.ipp:211
bool Initialize(bool enable_vcc=true) noexcept
Perform power-up, reset, and SSD1331 initialization.
Definition isf15acp4.ipp:110
bool Present(const GraphicsContext &gfx) noexcept
Present any graphics backend to the panel.
Definition isf15acp4.ipp:317
static void colorToSsd1331(Color565 color, uint8_t out[3]) noexcept
Definition isf15acp4.ipp:340
bool SetFullWindow() noexcept
Configure full-panel memory window for the active variant.
Definition isf15acp4.ipp:200
GraphicsContext CreateGraphicsContext() noexcept
Create a graphics context matching this display configuration.
Definition isf15acp4.ipp:296
SmartDisplay(SpiType *bus, ProductVariant variant=ProductVariant::Isf15acp4) noexcept
Construct a SmartDisplay driver (defaults to ISF15ACP4 + BuiltinCanvas).
Definition isf15acp4.ipp:42
bool WriteColors(const Color565 *colors, std::size_t count) noexcept
Stream Color565 pixels to the controller GRAM in the SSD1331 65k-colour byte order.
Definition isf15acp4.ipp:237
bool presenterFillScreen(void *ctx, Color565 color) noexcept
Definition isf15acp4.ipp:24
bool presenterFlushCanvas(void *ctx, const Color565 *data, std::size_t bytes) noexcept
Definition isf15acp4.ipp:29
bool presenterDrawPixel(void *ctx, uint8_t x, uint8_t y, Color565 color) noexcept
Definition isf15acp4.ipp:9
bool presenterDrawLine(void *ctx, Point a, Point b, Color565 color) noexcept
Definition isf15acp4.ipp:14
bool presenterDrawRect(void *ctx, Rect r, Color565 color, bool filled) noexcept
Definition isf15acp4.ipp:19
constexpr uint16_t ToPanelPixel(Color565 color) noexcept
Convert an RGB565 colour to the 16-bit pixel word the NKK panels expect in display RAM.
Definition isf15acp4_ssd1331.hpp:151
constexpr uint32_t kResetRecoveryUs
Wait after releasing RES# before the first command.
Definition isf15acp4_ssd1331.hpp:73
const InitStep * GetInitTable(ProductVariant variant, std::size_t &count) noexcept
Resolve initialization table for a product variant.
Definition isf15acp4_ssd1331.hpp:158
constexpr uint32_t kResetPulseMinUs
Minimum RES# low pulse width (SSD1331 rev 1.2 Table 18; NKK power ON sequence).
Definition isf15acp4_ssd1331.hpp:66
Definition isf15acp4.hpp:19
Error
Driver error flags (bitmask).
Definition isf15acp4_types.hpp:28
ProductVariant
Supported NKK SmartDisplay OLED product variants.
Definition isf15acp4_types.hpp:19
uint16_t Color565
16-bit RGB565 color value (R5G6B5).
Definition isf15acp4_types.hpp:50
constexpr const DisplayGeometry & GetGeometry(ProductVariant variant) noexcept
Get geometry metadata for a product variant.
Definition isf15acp4_types.hpp:77
@ ChipSelect
SS / slave select (active-low on device)
@ Reset
RES# reset input: Active = held in reset (pin low), Inactive = running (pin high)
@ VccEnable
Optional charge-pump / 16V rail enable.
@ Switch
Optional momentary switch sense input.
@ DataCommand
D/C: command (inactive) vs data (active)
Type-erased presenter used by HardwareDirect backend.
Definition isf15acp4_graphics_context.hpp:22
void * context
Definition isf15acp4_graphics_context.hpp:28
2D point in display coordinates.
Definition isf15acp4_types.hpp:90
Axis-aligned rectangle in display coordinates (inclusive bounds).
Definition isf15acp4_types.hpp:96
uint8_t y1
Definition isf15acp4_types.hpp:100
uint8_t x0
Definition isf15acp4_types.hpp:97
uint8_t x1
Definition isf15acp4_types.hpp:99
uint8_t y0
Definition isf15acp4_types.hpp:98
Configuration for SmartDisplay construction.
Definition isf15acp4_button_ui.hpp:17
ProductVariant variant
Definition isf15acp4_button_ui.hpp:18
One initialization command with optional trailing data bytes.
Definition isf15acp4_ssd1331.hpp:59