ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_display_pal_eink.c
Go to the documentation of this file.
1
35
37
38#include <stddef.h>
39#include <stdint.h>
40
41#include "ra8_attributes.h"
42#include "ra8_check.h"
43#include "ra8_display_pal.h"
45#include "ra8_epaper.h"
46#include "ra8_err.h"
47#include "ra8_log.h"
48
50static const char* const s_tag = "ra8_display_pal_eink";
51
63
66
86
100
116
117/* =============================================================================
118 * Conversion + mapping helpers
119 * =============================================================================
120 */
121
123{
124 const uint32_t r5 = (uint32_t)((px >> (uint16_t)k_eink_r5_shift) & (uint16_t)k_eink_r5_mask);
125 const uint32_t g6 = (uint32_t)((px >> (uint16_t)k_eink_g6_shift) & (uint16_t)k_eink_g6_mask);
126 const uint32_t b5 = (uint32_t)(px & (uint16_t)k_eink_b5_mask);
127 /* Expand 5/6-bit channels to 8-bit by bit-replication (0->0, max->255). */
128 const uint32_t r8 = (r5 << 3U) | (r5 >> 2U);
129 const uint32_t g8 = (g6 << 2U) | (g6 >> 4U);
130 const uint32_t b8 = (b5 << 3U) | (b5 >> 2U);
131 const uint32_t luma = ((r8 * (uint32_t)k_eink_luma_r) + (g8 * (uint32_t)k_eink_luma_g) +
132 (b8 * (uint32_t)k_eink_luma_b)) >>
133 (uint32_t)k_eink_luma_shift;
134 return (uint8_t)luma;
135}
136
160{
161 if (hint == k_display_refresh_fast) {
162 return k_ra8_epaper_wf_a2;
163 }
164 if (hint == k_display_refresh_init) {
166 }
168}
169
197{
198 RA8_CHECK_NULL_PTR(cfg->framebuffer, s_tag, "cfg->framebuffer");
199 if (cfg->width_px == 0U || cfg->height_px == 0U) {
201 }
202 if (cfg->pixfmt != k_display_pixfmt_rgb565) {
204 }
205 if (cfg->width_px > (uint16_t)k_eink_line_max_px) {
207 }
208 /* The IT8951 descriptor is BSP-supplied through panel_timing. */
209 if (cfg->panel_timing == nullptr) {
211 }
212 const uint32_t need_bytes =
213 (uint32_t)cfg->width_px * (uint32_t)cfg->height_px * (uint32_t)k_eink_rgb565_bpp;
214 if (cfg->framebuffer_bytes < need_bytes) {
216 }
217 return k_ra8_ok;
218}
219
240{
241 const uint32_t stride = (uint32_t)cfg->width_px * (uint32_t)k_eink_rgb565_bpp;
242 s_eink_ctx.caps.width_px = cfg->width_px;
243 s_eink_ctx.caps.height_px = cfg->height_px;
245 s_eink_ctx.caps.stride_bytes = stride;
246 s_eink_ctx.caps.refresh_latency_us_typ = (uint32_t)k_eink_refresh_quality_us;
247 s_eink_ctx.caps.supports_partial_update = true;
248 s_eink_ctx.caps.continuous_refresh = false;
249
250 s_eink_ctx.fb.pixels = cfg->framebuffer;
251 s_eink_ctx.fb.width_px = cfg->width_px;
252 s_eink_ctx.fb.height_px = cfg->height_px;
253 s_eink_ctx.fb.stride_bytes = stride;
255 s_eink_ctx.initialised = true;
256}
257
281{
282 if (r.x > caps->width_px) {
284 }
285 if (r.y > caps->height_px) {
287 }
288 if ((uint32_t)r.x + (uint32_t)r.w > (uint32_t)caps->width_px) {
290 }
291 if ((uint32_t)r.y + (uint32_t)r.h > (uint32_t)caps->height_px) {
293 }
294 return k_ra8_ok;
295}
296
325static void internal_eink_pack_row_4bpp(const uint16_t* src, uint16_t width)
326{
327 const uint32_t px_per_byte = (uint32_t)k_eink_px_per_byte_4bpp;
328 for (uint32_t col = 0U; col < (uint32_t)width; col += px_per_byte) {
329 /* The controller keeps only the high nibble of an 8 bpp byte, so
330 * dropping the low nibble here costs nothing optically and halves the
331 * bytes on the wire. */
332 const uint8_t hi_px = ra8_display_pal_eink_luma_from_rgb565(src[col]);
333 const uint32_t next = col + 1U;
334 const uint8_t lo_px = (next < (uint32_t)width)
337 const uint8_t hi_n = (uint8_t)((hi_px >> k_eink_nibble_shift) & k_eink_nibble_mask);
338 const uint8_t lo_n = (uint8_t)((lo_px >> k_eink_nibble_shift) & k_eink_nibble_mask);
339 s_eink_line[col / px_per_byte] = (uint8_t)((uint8_t)(hi_n << k_eink_nibble_shift) | lo_n);
340 }
341}
342
374{
375 const uint16_t* fb = (const uint16_t*)c->fb.pixels;
376 const uint32_t width = (uint32_t)c->fb.width_px;
377 for (uint16_t row = 0U; row < rect.h; ++row) {
378 const uint16_t* src = &fb[(((uint32_t)rect.y + row) * width) + rect.x];
380 const ra8_epaper_area_t area = {.x = rect.x,
381 .y = (uint16_t)(rect.y + row),
382 .width = rect.w,
383 .height = 1U};
384 size_t need = 0U;
385 const ra8_err_t serr = ra8_epaper_image_bytes(&area, k_ra8_epaper_pf_4bpp, &need);
386 if (serr != k_ra8_ok) {
387 return serr;
388 }
389 const ra8_err_t err = ra8_epaper_load_image(&area,
391 need,
394 if (err != k_ra8_ok) {
395 return err;
396 }
397 }
398 return k_ra8_ok;
399}
400
401/* =============================================================================
402 * Vtable callbacks
403 * =============================================================================
404 */
405
437static ra8_err_t internal_eink_init(const display_cfg_t* cfg, void** out_ctx)
438{
439 RA8_CHECK_NULL_PTR(cfg, s_tag, "cfg");
440 RA8_CHECK_NULL_PTR(out_ctx, s_tag, "out_ctx");
442 if (v != k_ra8_ok) {
443 return v;
444 }
445 if (s_eink_ctx.initialised) {
446 ra8_log_error(s_tag, "internal_eink_init: already initialised");
447 return k_ra8_err_busy;
448 }
449 const ra8_err_t err = ra8_epaper_init((const ra8_epaper_cfg_t*)cfg->panel_timing);
450 if (err != k_ra8_ok) {
451 ra8_log_error(s_tag, "internal_eink_init: ra8_epaper_init failed");
452 return err;
453 }
455 *out_ctx = &s_eink_ctx;
456 ra8_log_info(s_tag, "internal_eink_init: IT8951 backend bound");
457 return k_ra8_ok;
458}
459
483{
484 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
485 RA8_CHECK_NULL_PTR(out, s_tag, "out");
486 *out = ((const eink_ctx_t*)ctx)->caps;
487 return k_ra8_ok;
488}
489
514/* cppcheck-suppress constParameterCallback -- display_pal_ops_t fixes this callback's context type as void*. */
516{
517 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
518 RA8_CHECK_NULL_PTR(out, s_tag, "out");
519 *out = ((const eink_ctx_t*)ctx)->fb;
520 return k_ra8_ok;
521}
522
552{
553 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
554 const eink_ctx_t* c = (const eink_ctx_t*)ctx;
555 const ra8_err_t v = internal_eink_check_rect(&c->caps, rect);
556 if (v != k_ra8_ok) {
557 return v;
558 }
559 const ra8_err_t lerr = internal_eink_load_rect(c, rect);
560 if (lerr != k_ra8_ok) {
561 return lerr;
562 }
563 const ra8_epaper_area_t area = {.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h};
565}
566
592static ra8_err_t internal_eink_clear(void* ctx, uint32_t color)
593{
594 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
595 eink_ctx_t* c = (eink_ctx_t*)ctx;
596 uint16_t* pixels = (uint16_t*)c->fb.pixels;
597 const uint32_t pixels_total = (uint32_t)c->fb.width_px * (uint32_t)c->fb.height_px;
598 const uint16_t rgb565 = (uint16_t)(color & (uint32_t)UINT16_MAX);
599 for (uint32_t i = 0U; i < pixels_total; ++i) {
600 pixels[i] = rgb565;
601 }
602 return k_ra8_ok;
603}
604
631{
632 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
633 eink_ctx_t* c = (eink_ctx_t*)ctx;
634 const ra8_err_t err = ra8_epaper_sleep();
635 c->initialised = false;
636 c->fb.pixels = nullptr;
637 return err;
638}
639
640/* =============================================================================
641 * Public iface instance
642 * =============================================================================
643 */
644
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Display Platform Abstraction Layer for the RA8D2.
@ k_display_pixfmt_rgb565
16 bpp, 5/6/5 packed.
struct display_backend_iface display_backend_iface_t
display_refresh_hint_t
Intent passed into display_flush; backends map this onto whatever waveform/mode their controller offe...
@ k_display_refresh_fast
Prioritise latency.
@ k_display_refresh_init
Full reset of the panel.
static ra8_err_t internal_eink_validate_cfg(const display_cfg_t *cfg)
Validate the caller cfg against the e-ink backend's rules.
static ra8_err_t internal_eink_get_caps(const void *ctx, display_caps_t *out)
Vtable get_caps – O(1) copy from s_eink_ctx.caps.
static ra8_err_t internal_eink_deinit(void *ctx)
Vtable deinit – sleep the panel and drop state.
static ra8_err_t internal_eink_init(const display_cfg_t *cfg, void **out_ctx)
E-ink backend init – validate, bring up the IT8951, snapshot.
static ra8_err_t internal_eink_clear(void *ctx, uint32_t color)
Vtable clear – fill the RGB565 framebuffer with color.
uint8_t ra8_display_pal_eink_luma_from_rgb565(uint16_t px)
Convert one RGB565 pixel to an 8 bpp greyscale (Rec.601 luma).
static ra8_epaper_waveform_t internal_eink_waveform(display_refresh_hint_t hint)
Map a PAL refresh hint onto an IT8951 waveform mode.
static uint8_t s_eink_line[k_eink_line_max_px]
Bounded RGB565 -> 4 bpp conversion scratch (one panel row).
static void internal_eink_pack_row_4bpp(const uint16_t *src, uint16_t width)
Pack one RGB565 span into s_eink_line as 4 bpp greyscale.
static void internal_eink_snapshot(const display_cfg_t *cfg)
Snapshot the validated cfg into s_eink_ctx.
static ra8_err_t internal_eink_get_framebuffer(void *ctx, display_fb_t *out)
Vtable get_framebuffer – hand back the RGB565 FB descriptor.
static ra8_err_t internal_eink_load_rect(const eink_ctx_t *c, display_rect_t rect)
Convert + stream each row of rect into the IT8951 frame RAM.
static ra8_err_t internal_eink_flush(void *ctx, display_rect_t rect, display_refresh_hint_t hint)
Vtable flush – convert rect to 8bpp and refresh the panel.
ra8_display_pal_eink_const_t
Internal numeric constants (no magic numbers).
@ k_eink_line_max_px
Max row width (= panel max).
@ k_eink_luma_b
Rec.601 B weight (0.114*256).
@ k_eink_nibble_mask
Low-nibble mask.
@ k_eink_rgb565_bpp
Bytes per RGB565 pixel.
@ k_eink_luma_shift
Luma weight denominator shift.
@ k_eink_px_per_byte_4bpp
Pixels packed per 4bpp byte.
@ k_eink_white_nibble
4bpp white, used to pad tails.
@ k_eink_luma_r
Rec.601 R weight (0.299*256).
@ k_eink_luma_g
Rec.601 G weight (0.587*256).
@ k_eink_nibble_shift
8bpp luma -> 4bpp nibble shift.
@ k_eink_refresh_quality_us
Typical GC16 latency.
static ra8_err_t internal_eink_check_rect(const display_caps_t *caps, display_rect_t r)
Confirm a rectangle stays inside the active framebuffer.
static eink_ctx_t s_eink_ctx
Single e-ink backend context.
ra8_display_pal_eink_rgb_t
RGB565 field shifts / masks for luma extraction.
@ k_eink_g6_mask
6-bit green mask.
@ k_eink_b5_mask
5-bit blue mask.
@ k_eink_r5_shift
Red field shift in RGB565.
@ k_eink_g6_shift
Green field shift in RGB565.
@ k_eink_r5_mask
5-bit red mask.
E-ink backend (IT8951) for the display PAL.
const display_backend_iface_t k_display_backend_eink_it8951
E-ink (IT8951) backend vtable.
Internal vtable + handle types for the display PAL.
IT8951 e-paper controller SPI driver – public API.
@ k_ra8_epaper_pf_4bpp
4 bpp, 2 px per byte – preferred.
Definition ra8_epaper.h:183
ra8_err_t ra8_epaper_display_area(const ra8_epaper_area_t *area, ra8_epaper_waveform_t waveform)
Refresh the indicated rectangle on the physical panel.
Definition ra8_epaper.c:927
ra8_epaper_waveform_t
Symbolic IT8951 panel-refresh waveform selectors.
Definition ra8_epaper.h:106
@ k_ra8_epaper_wf_init
INIT - flush to white, slowest.
Definition ra8_epaper.h:107
@ k_ra8_epaper_wf_a2
A2 - black/white only, fastest.
Definition ra8_epaper.h:110
@ k_ra8_epaper_wf_gc16
GC16 - 16-grey full quality.
Definition ra8_epaper.h:109
ra8_err_t ra8_epaper_init(const ra8_epaper_cfg_t *cfg)
Bring up the IT8951 panel against the injected SPI bus seam.
Definition ra8_epaper.c:654
@ k_ra8_epaper_endian_little
Host buffer is little-endian.
Definition ra8_epaper.h:264
ra8_err_t ra8_epaper_sleep(void)
Drop the panel into deep-sleep (~15 uA per Waveshare AN).
Definition ra8_epaper.c:961
ra8_err_t ra8_epaper_load_image(const ra8_epaper_area_t *area, const uint8_t *buf, size_t buf_len, ra8_epaper_pixel_format_t pf, ra8_epaper_endian_t endian)
Push a packed greyscale buffer into the controller's frame RAM.
Definition ra8_epaper.c:837
ra8_err_t ra8_epaper_image_bytes(const ra8_epaper_area_t *area, ra8_epaper_pixel_format_t pf, size_t *out_bytes)
Bytes a packed source buffer needs for area at depth pf.
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_busy
Resource busy – blocking operation cannot proceed.
Definition ra8_err.h:195
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Capabilities a backend reports after display_init.
uint16_t width_px
Display visible width.
uint16_t height_px
Display visible height.
Configuration descriptor passed into display_init.
void * framebuffer
Application framebuffer storage (caller owns).
display_pixfmt_t pixfmt
Pixel format of framebuffer.
uint16_t width_px
Visible framebuffer width in pixels.
uint32_t framebuffer_bytes
Size of the framebuffer buffer in bytes.
uint16_t height_px
Visible framebuffer height in pixels.
const void * panel_timing
Panel RGB timing for parallel-RGB backends, opaque here so the generic PAL stays HAL-agnostic.
Framebuffer descriptor returned by display_get_framebuffer.
uint16_t width_px
Framebuffer width.
uint16_t height_px
Framebuffer height.
void * pixels
Base of pixel data (RGB565 unless noted).
Rectangle in framebuffer coordinates passed into display_flush.
uint16_t h
Height in pixels.
uint16_t w
Width in pixels.
uint16_t y
Top edge in pixels.
uint16_t x
Left edge in pixels.
E-ink backend context attached to the PAL handle.
display_caps_t caps
Caps.
bool initialised
Initialised.
display_fb_t fb
Fb.
Rectangle descriptor (top-left + size) used by load + display.
Definition ra8_epaper.h:317
Configuration descriptor for ra8_epaper_init.
Definition ra8_epaper.h:304