ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_epaper.c File Reference

IT8951 e-paper SPI driver implementation. More...

#include "ra8_epaper.h"
#include <stddef.h>
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_hw_err.h"
#include "ra8_log.h"
#include "ra8_port_regs.h"
#include "ra8_port_utils.h"
#include "ra8_spi_bus_ops.h"
#include "ra8_time.h"
Include dependency graph for ra8_epaper.c:

Go to the source code of this file.

Data Structures

struct  ra8_epaper_panel_t
 File-scope panel context. More...

Enumerations

enum  ra8_epaper_preamble_t : uint16_t {
  k_ra8_epaper_preamble_cmd = 0x6000U ,
  k_ra8_epaper_preamble_wr = 0x0000U ,
  k_ra8_epaper_preamble_rd = 0x1000U
}
 SPI preamble words (DS chapter 3.4 table 3-3). More...
enum  ra8_epaper_cmd_t : uint16_t {
  k_ra8_epaper_cmd_sys_run = 0x0001U ,
  k_ra8_epaper_cmd_sleep = 0x0003U ,
  k_ra8_epaper_cmd_reg_rd = 0x0010U ,
  k_ra8_epaper_cmd_reg_wr = 0x0011U ,
  k_ra8_epaper_cmd_ld_img_area = 0x0021U ,
  k_ra8_epaper_cmd_ld_img_end = 0x0022U ,
  k_ra8_epaper_cmd_dpy_area = 0x0034U ,
  k_ra8_epaper_cmd_vcom = 0x0039U ,
  k_ra8_epaper_cmd_get_dev_info = 0x0302U
}
 Subset of IT8951 user commands used by this driver. More...
enum  ra8_epaper_vcom_arg_t : uint16_t {
  k_ra8_epaper_vcom_get = 0x0000U ,
  k_ra8_epaper_vcom_set = 0x0001U
}
 Argument word selecting the direction of the VCOM command. More...
enum  ra8_epaper_reg_t : uint16_t {
  k_ra8_epaper_reg_lisar_lo = 0x0208U ,
  k_ra8_epaper_reg_lisar_hi = 0x020AU ,
  k_ra8_epaper_reg_lutafsr = 0x1224U
}
 Memory-mapped controller registers we touch. More...
enum  ra8_epaper_limits_t : uint32_t {
  k_ra8_epaper_busy_poll_max = 200000U ,
  k_ra8_epaper_lut_poll_max = 200000U ,
  k_ra8_epaper_dev_info_words = 20U ,
  k_ra8_epaper_reset_pulse_ms = 10U ,
  k_ra8_epaper_status_unset = 0xFFFFU ,
  k_ra8_epaper_byte_mask = 0xFFU ,
  k_ra8_epaper_dummy_tx = 0xFFU ,
  k_ra8_epaper_white_pad = 0x00FFU ,
  k_ra8_epaper_byte_shift = 8U ,
  k_ra8_epaper_pf_shift = 4U
}
 Bounded retry / sizing limits. More...
enum  ra8_epaper_wire_pf_t : uint16_t {
  k_ra8_epaper_wire_pf_2bpp = 0U ,
  k_ra8_epaper_wire_pf_3bpp = 1U ,
  k_ra8_epaper_wire_pf_4bpp = 2U ,
  k_ra8_epaper_wire_pf_8bpp = 3U
}
 LD_IMG_AREA arg0 pixel-format codes (IT8951 programming guide). More...
enum  ra8_epaper_state_t : uint8_t {
  k_ra8_epaper_state_uninit = 0U ,
  k_ra8_epaper_state_ready = 1U
}
 Driver lifecycle state. More...

Functions

static ra8_err_t internal_ra8_epaper_send16 (uint16_t word)
 Send a 16-bit word MSB-first over the injected SPI bus seam.
static ra8_err_t internal_ra8_epaper_recv16 (uint16_t *out_word)
 Receive one 16-bit word MSB-first.
static ra8_err_t internal_ra8_epaper_wait_ready (void)
 Block until the panel asserts HRDY.
static ra8_err_t internal_ra8_epaper_write_cmd (uint16_t cmd)
 Send a command code (DS chapter 3.4).
static ra8_err_t internal_ra8_epaper_write_data16 (uint16_t word)
 Send a 16-bit data word (DS chapter 3.4).
static ra8_err_t internal_ra8_epaper_read_data16 (uint16_t *out_word)
 Read a 16-bit data word (DS chapter 3.4).
static ra8_err_t internal_ra8_epaper_reg_write (uint16_t reg, uint16_t value)
 Write to an IT8951 internal register.
static ra8_err_t internal_ra8_epaper_reg_read (uint16_t reg, uint16_t *value)
 Read from an IT8951 internal register.
static void internal_ra8_epaper_pulse_reset (void)
 Pulse the panel /RESET line low for 10 ms then back high.
static ra8_err_t internal_ra8_epaper_read_dev_info (ra8_epaper_dev_info_t *out)
 Read and decode the 40-byte GET_DEV_INFO response.
static uint16_t internal_ra8_epaper_wire_pf (ra8_epaper_pixel_format_t pf)
 Map a driver pixel-format selector onto the LD_IMG_AREA wire code.
static ra8_err_t internal_ra8_epaper_send_load_args (const ra8_epaper_area_t *area, ra8_epaper_pixel_format_t pf, ra8_epaper_endian_t endian)
 Send the LD_IMG_AREA argument block (5 words: arg0..arg4).
static ra8_err_t internal_ra8_epaper_stream_pixels (const uint8_t *buf, size_t buf_len)
 Stream a packed pixel buffer into the controller as 16-bit words.
static ra8_err_t internal_ra8_epaper_resolve_waveform (ra8_epaper_waveform_t waveform, uint16_t *out_mode)
 Resolve a symbolic waveform selector into this panel's LUT mode.
static ra8_err_t internal_ra8_epaper_send_display_args (const ra8_epaper_area_t *area, uint16_t wf_mode)
 Send the DPY_AREA argument block (5 words: x,y,w,h,wf).
ra8_err_t ra8_epaper_init (const ra8_epaper_cfg_t *cfg)
 Bring up the IT8951 panel against the injected SPI bus seam.
ra8_err_t ra8_epaper_dev_info (ra8_epaper_dev_info_t *out_info)
 Hand back the GET_DEV_INFO block captured during init.
ra8_err_t ra8_epaper_get_vcom (uint16_t *out_mv)
 Read the controller's current VCOM setting, in millivolts.
ra8_err_t ra8_epaper_set_vcom (uint16_t mv)
 Implementation of ra8_epaper_set_vcom() – write, then read back and compare before granting the INV-VCOM-1 permit.
bool ra8_epaper_vcom_verified (void)
 Implementation of ra8_epaper_vcom_verified() – the latch alone already implies the lifecycle state.
static ra8_err_t internal_ra8_epaper_validate_load (const ra8_epaper_area_t *area, size_t buf_len, ra8_epaper_pixel_format_t pf)
 Validate a load request's lifecycle, buffer size and geometry.
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.
static ra8_err_t internal_ra8_epaper_wait_lut_idle (void)
 Poll REG_LUTAFSR until the controller reports every LUT idle.
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.
ra8_err_t ra8_epaper_sleep (void)
 Drop the panel into deep-sleep (~15 uA per Waveshare AN).

Variables

static const char * s_tag = "EPAPER"
 Logging tag used by every error path in this TU.
static ra8_epaper_panel_t s_panel
 Single-instance panel context.

Detailed Description

IT8951 e-paper SPI driver implementation.

Tag
[Ring 3 / HAL] {World: NS}

Implements ra8_epaper.h. The wire protocol follows the IT8951 datasheet rev 0.2 chapter 3.4 ("SPI Interface") and chapter 4 ("Application Note") plus the Waveshare IT8951 e-paper user guide. References to "DS" / "AN" in comments cite those documents.

Every SPI transaction begins with a 16-bit preamble:

  • 0x6000 = host -> controller, command code follows
  • 0x0000 = host -> controller, data words follow
  • 0x1000 = host <- controller, host reads data words

The host also has to honour HRDY (a GPIO from the panel) before each preamble: when HRDY is low, the controller is still busy processing the previous request. The driver polls HRDY through ra8_gpio_read with a bounded retry budget to satisfy NASA Power-of-10 Rule 2.

Definition in file ra8_epaper.c.

Enumeration Type Documentation

◆ ra8_epaper_cmd_t

enum ra8_epaper_cmd_t : uint16_t

Subset of IT8951 user commands used by this driver.

Codes from DS chapter 4.2 "User Command Set".

Enumerator
k_ra8_epaper_cmd_sys_run 

Wake from standby.

k_ra8_epaper_cmd_sleep 

Enter deep-sleep.

k_ra8_epaper_cmd_reg_rd 

Register read.

k_ra8_epaper_cmd_reg_wr 

Register write.

k_ra8_epaper_cmd_ld_img_area 

Begin load (rectangle).

k_ra8_epaper_cmd_ld_img_end 

End load.

k_ra8_epaper_cmd_dpy_area 

Refresh rectangle.

k_ra8_epaper_cmd_vcom 

Get / set VCOM bias.

k_ra8_epaper_cmd_get_dev_info 

40-byte info block.

Definition at line 73 of file ra8_epaper.c.

◆ ra8_epaper_limits_t

enum ra8_epaper_limits_t : uint32_t

Bounded retry / sizing limits.

Enumerator
k_ra8_epaper_busy_poll_max 

Outer HRDY poll budget.

k_ra8_epaper_lut_poll_max 

LUT-busy poll budget.

k_ra8_epaper_dev_info_words 

40-byte block / 2.

k_ra8_epaper_reset_pulse_ms 

Reset assert dwell.

k_ra8_epaper_status_unset 

Pre-read sentinel value.

k_ra8_epaper_byte_mask 

Low-byte extraction mask.

k_ra8_epaper_dummy_tx 

Dummy byte for SPI reads.

k_ra8_epaper_white_pad 

0xFF pad for odd tail.

k_ra8_epaper_byte_shift 

Bits per byte.

k_ra8_epaper_pf_shift 

LD_IMG_AREA arg0 PF shift.

Definition at line 114 of file ra8_epaper.c.

◆ ra8_epaper_preamble_t

enum ra8_epaper_preamble_t : uint16_t

SPI preamble words (DS chapter 3.4 table 3-3).

Enumerator
k_ra8_epaper_preamble_cmd 

Host -> command write.

k_ra8_epaper_preamble_wr 

Host -> data write.

k_ra8_epaper_preamble_rd 

Host <- data read.

Definition at line 60 of file ra8_epaper.c.

◆ ra8_epaper_reg_t

enum ra8_epaper_reg_t : uint16_t

Memory-mapped controller registers we touch.

Enumerator
k_ra8_epaper_reg_lisar_lo 

LISAR low half (DS 4.4).

k_ra8_epaper_reg_lisar_hi 

LISAR high half.

k_ra8_epaper_reg_lutafsr 

LUT busy status.

Definition at line 104 of file ra8_epaper.c.

◆ ra8_epaper_state_t

enum ra8_epaper_state_t : uint8_t

Driver lifecycle state.

Enumerator
k_ra8_epaper_state_uninit 

Not initialized yet.

k_ra8_epaper_state_ready 

Initialized and idle.

Definition at line 158 of file ra8_epaper.c.

◆ ra8_epaper_vcom_arg_t

enum ra8_epaper_vcom_arg_t : uint16_t

Argument word selecting the direction of the VCOM command.

The VCOM command (k_ra8_epaper_cmd_vcom) is bidirectional: the first data word chooses whether the controller answers with its current VCOM or consumes a following word as the new one. Matches the Waveshare IT8951 reference driver's EPD_IT8951_GetVCOM / SetVCOM.

Enumerator
k_ra8_epaper_vcom_get 

Read VCOM; one word follows back.

k_ra8_epaper_vcom_set 

Write VCOM; one word follows out.

Definition at line 95 of file ra8_epaper.c.

◆ ra8_epaper_wire_pf_t

enum ra8_epaper_wire_pf_t : uint16_t

LD_IMG_AREA arg0 pixel-format codes (IT8951 programming guide).

Deliberately distinct from ra8_epaper_pixel_format_t: the controller numbers its formats 2 bpp = 0, 3 bpp = 1, 4 bpp = 2, 8 bpp = 3, which is neither the bit depth nor the driver's selector ordering. 1 bpp has no code of its own, so it maps onto the 8 bpp code here.

Warning
That mapping alone is NOT a working 1 bpp path. The controller only interprets bi-level data as bi-level while its bitmap-mode register bit is armed, and this driver does not arm it: the arm/restore window spans ra8_epaper_load_image through the end of ra8_epaper_display_area, so leaving it armed would corrupt the next 4/8 bpp update. Until that cross-call state is designed and bench-checked, k_ra8_epaper_pf_1bpp exercises only the 32-pixel alignment contract; no production caller selects it, and the display PAL packs 4 bpp.
Enumerator
k_ra8_epaper_wire_pf_2bpp 

2 bits per pixel.

k_ra8_epaper_wire_pf_3bpp 

3 bits per pixel.

k_ra8_epaper_wire_pf_4bpp 

4 bits per pixel.

k_ra8_epaper_wire_pf_8bpp 

8 bits per pixel.

Definition at line 147 of file ra8_epaper.c.

Function Documentation

◆ internal_ra8_epaper_pulse_reset()

void internal_ra8_epaper_pulse_reset ( void )
static

Pulse the panel /RESET line low for 10 ms then back high.

Drives cfg.reset_pin high -> low -> high through ra8_gpio_write with a k_ra8_epaper_reset_pulse_ms dwell after each edge (Waveshare IT8951 user guide reset sequence). The write results are deliberately discarded: the pulse runs before the panel can report anything, and a mis-wired pin surfaces on the HRDY wait that immediately follows. The same body runs on every build – the host unit-test build drives the RAM-backed PORT window (ra8_delay_ms is a host no-op inside ra8_core), so tests observe the final POSR set-bit write on the reset port's PCNTR3.

Precondition
ra8_epaper_init copied a validated cfg into s_panel.
cfg.reset_pin addresses a mapped PORT pin.
Postcondition
The /RESET line is left driven high (panel out of reset).
Three reset-dwell delays have elapsed (firmware builds).
Note
Not thread-safe; init path only.
Since
0.1.0

Definition at line 442 of file ra8_epaper.c.

References k_ra8_epaper_reset_pulse_ms, k_ra8_level_high, k_ra8_level_low, pin, ra8_delay_ms(), ra8_gpio_write(), and s_panel.

Referenced by ra8_epaper_init().

◆ internal_ra8_epaper_read_data16()

ra8_err_t internal_ra8_epaper_read_data16 ( uint16_t * out_word)
staticnodiscard

Read a 16-bit data word (DS chapter 3.4).

Parameters
[out]out_wordReceive slot; non-NULL.
Returns
ra8_err_t error code.

Definition at line 351 of file ra8_epaper.c.

References internal_ra8_epaper_recv16(), internal_ra8_epaper_send16(), internal_ra8_epaper_wait_ready(), k_ra8_epaper_preamble_rd, and k_ra8_ok.

Referenced by internal_ra8_epaper_read_dev_info(), internal_ra8_epaper_reg_read(), and ra8_epaper_get_vcom().

◆ internal_ra8_epaper_read_dev_info()

ra8_err_t internal_ra8_epaper_read_dev_info ( ra8_epaper_dev_info_t * out)
staticnodiscard

Read and decode the 40-byte GET_DEV_INFO response.

The block is read once at init and cached in s_panel.info; the reported geometry is cross-checked against the configured panel size and logged (not enforced) on mismatch, because a controller that has not finished loading its waveform can report zeroes on the first read.

Parameters
[out]outInfo block to populate; non-NULL.
Returns
ra8_err_t propagating SPI errors.

Definition at line 466 of file ra8_epaper.c.

References internal_ra8_epaper_read_data16(), internal_ra8_epaper_write_cmd(), k_ra8_epaper_cmd_get_dev_info, k_ra8_epaper_dev_info_words, k_ra8_ok, and ra8_epaper_decode_dev_info().

Referenced by ra8_epaper_init().

◆ internal_ra8_epaper_recv16()

ra8_err_t internal_ra8_epaper_recv16 ( uint16_t * out_word)
staticnodiscard

Receive one 16-bit word MSB-first.

Parameters
[out]out_wordReceive slot; non-NULL.
Returns
ra8_err_t from the underlying SPI bus.
Precondition
out_word non-NULL.
Postcondition
On success, *out_word contains the received word.

Definition at line 237 of file ra8_epaper.c.

References ra8_spi_bus_ops_t::ctx, k_ra8_epaper_byte_shift, k_ra8_epaper_dummy_tx, k_ra8_ok, s_panel, and ra8_spi_bus_ops_t::xfer8.

Referenced by internal_ra8_epaper_read_data16().

◆ internal_ra8_epaper_reg_read()

ra8_err_t internal_ra8_epaper_reg_read ( uint16_t reg,
uint16_t * value )
staticnodiscard

Read from an IT8951 internal register.

Parameters
[in]regRegister address.
[out]valueReceive slot; non-NULL.
Returns
ra8_err_t error code.

Definition at line 407 of file ra8_epaper.c.

References internal_ra8_epaper_read_data16(), internal_ra8_epaper_write_cmd(), internal_ra8_epaper_write_data16(), k_ra8_epaper_cmd_reg_rd, and k_ra8_ok.

Referenced by internal_ra8_epaper_wait_lut_idle().

◆ internal_ra8_epaper_reg_write()

ra8_err_t internal_ra8_epaper_reg_write ( uint16_t reg,
uint16_t value )
staticnodiscard

Write to an IT8951 internal register.

The "register write" sequence is REG_WR (0x0011) followed by two data words: the register address then the value (DS 4.2.5).

Parameters
[in]regRegister address.
[in]valueValue to write.
Returns
ra8_err_t error code.

Definition at line 386 of file ra8_epaper.c.

References internal_ra8_epaper_write_cmd(), internal_ra8_epaper_write_data16(), k_ra8_epaper_cmd_reg_wr, and k_ra8_ok.

Referenced by ra8_epaper_load_image().

◆ internal_ra8_epaper_resolve_waveform()

ra8_err_t internal_ra8_epaper_resolve_waveform ( ra8_epaper_waveform_t waveform,
uint16_t * out_mode )
staticnodiscard

Resolve a symbolic waveform selector into this panel's LUT mode.

The mapping is per-panel data carried in s_panel.cfg.waveform – never the enumerator's own value, which is only an ordinal. An unrecognised selector is refused rather than defaulted, because defaulting would put an arbitrary waveform on the panel.

Parameters
[in]waveformSymbolic selector.
[out]out_modeReceives the LUT mode number; non-NULL.
Returns
k_ra8_ok or k_ra8_err_invalid_arg for an unknown selector.

Definition at line 597 of file ra8_epaper.c.

References ra8_epaper_waveform_cfg_t::a2, ra8_epaper_waveform_cfg_t::du, ra8_epaper_waveform_cfg_t::gc16, ra8_epaper_waveform_cfg_t::init, k_ra8_epaper_wf_a2, k_ra8_epaper_wf_du, k_ra8_epaper_wf_gc16, k_ra8_epaper_wf_init, k_ra8_err_invalid_arg, k_ra8_ok, and s_panel.

Referenced by ra8_epaper_display_area().

◆ internal_ra8_epaper_send16()

ra8_err_t internal_ra8_epaper_send16 ( uint16_t word)
staticnodiscard

Send a 16-bit word MSB-first over the injected SPI bus seam.

Parameters
[in]wordWord to send.
Returns
ra8_err_t from the underlying bus.xfer8.
Precondition
ra8_epaper_init validated the injected bus seam.
Postcondition
One word has been clocked out; receive bytes discarded.

Definition at line 212 of file ra8_epaper.c.

References ra8_spi_bus_ops_t::ctx, k_ra8_epaper_byte_mask, k_ra8_epaper_byte_shift, k_ra8_ok, s_panel, and ra8_spi_bus_ops_t::xfer8.

Referenced by internal_ra8_epaper_read_data16(), internal_ra8_epaper_write_cmd(), and internal_ra8_epaper_write_data16().

◆ internal_ra8_epaper_send_display_args()

ra8_err_t internal_ra8_epaper_send_display_args ( const ra8_epaper_area_t * area,
uint16_t wf_mode )
staticnodiscard

Send the DPY_AREA argument block (5 words: x,y,w,h,wf).

Parameters
[in]areaRectangle to refresh.
[in]wf_modeResolved LUT mode number for this panel.
Returns
ra8_err_t error code.

Definition at line 627 of file ra8_epaper.c.

References ra8_epaper_area_t::height, internal_ra8_epaper_write_data16(), k_ra8_ok, ra8_epaper_area_t::width, ra8_epaper_area_t::x, and ra8_epaper_area_t::y.

Referenced by ra8_epaper_display_area().

◆ internal_ra8_epaper_send_load_args()

ra8_err_t internal_ra8_epaper_send_load_args ( const ra8_epaper_area_t * area,
ra8_epaper_pixel_format_t pf,
ra8_epaper_endian_t endian )
staticnodiscard

Send the LD_IMG_AREA argument block (5 words: arg0..arg4).

Parameters
[in]areaRectangle to load.
[in]pfSource pixel depth.
[in]endianSource-buffer endianness.
Returns
ra8_err_t error code.

Definition at line 520 of file ra8_epaper.c.

References ra8_epaper_area_t::height, internal_ra8_epaper_wire_pf(), internal_ra8_epaper_write_data16(), k_ra8_epaper_byte_shift, k_ra8_epaper_pf_shift, k_ra8_ok, ra8_epaper_area_t::width, ra8_epaper_area_t::x, and ra8_epaper_area_t::y.

Referenced by ra8_epaper_load_image().

◆ internal_ra8_epaper_stream_pixels()

ra8_err_t internal_ra8_epaper_stream_pixels ( const uint8_t * buf,
size_t buf_len )
staticnodiscard

Stream a packed pixel buffer into the controller as 16-bit words.

The IT8951 frame RAM is word-wide; pack pairs of source bytes into one word. Odd tails are padded with 0xFF (white). Depth-agnostic – the bytes are already packed at the caller's pf, and the controller unpacks them per the arg0 format code.

Parameters
[in]bufSource bytes; non-NULL, length buf_len.
[in]buf_lenTotal number of bytes.
Returns
ra8_err_t error code.

Definition at line 564 of file ra8_epaper.c.

References internal_ra8_epaper_write_data16(), k_ra8_epaper_byte_shift, k_ra8_epaper_white_pad, and k_ra8_ok.

Referenced by ra8_epaper_load_image().

◆ internal_ra8_epaper_validate_load()

ra8_err_t internal_ra8_epaper_validate_load ( const ra8_epaper_area_t * area,
size_t buf_len,
ra8_epaper_pixel_format_t pf )
staticnodiscard

Validate a load request's lifecycle, buffer size and geometry.

Three refusals, in the order that gives the caller the most specific diagnosis. The alignment check is the load-bearing one: a 1 bpp update off the 32-pixel grid does not render at all – the controller accepts the command and shows nothing – so catching it here turns a blank screen into a return code.

Parameters
[in]areaRectangle to load; non-NULL.
[in]buf_lenCaller-supplied buffer length in bytes.
[in]pfSource pixel depth.
Returns
ra8_err_t error code.
Return values
k_ra8_okRequest accepted.
k_ra8_err_invalid_statePanel never initialized.
k_ra8_err_invalid_sizeEmpty area or buf_len mismatch.
k_ra8_err_invalid_arg1 bpp geometry off the 32-pixel grid.
Precondition
area is non-NULL and readable.
pf is a valid ra8_epaper_pixel_format_t.
Postcondition
No bus traffic is generated.
No driver state is mutated.

Definition at line 818 of file ra8_epaper.c.

References k_ra8_epaper_state_ready, k_ra8_err_invalid_arg, k_ra8_err_invalid_size, k_ra8_err_invalid_state, k_ra8_ok, ra8_epaper_area_is_aligned(), ra8_epaper_image_bytes(), ra8_log_error, s_panel, and s_tag.

Referenced by ra8_epaper_load_image().

◆ internal_ra8_epaper_wait_lut_idle()

ra8_err_t internal_ra8_epaper_wait_lut_idle ( void )
staticnodiscard

Poll REG_LUTAFSR until the controller reports every LUT idle.

The per-poll "LUT idle" comparison is routed through the ra8_fake_mmio fault seam under the host unit-test build (issue #177 / T1-01) so this real poll/timeout loop executes on host instead of a compiled-out short-circuit; un-armed the seam is transparent and honours the comparison. The LUTAFSR value is clocked in over the injected bus, so the seam is keyed on the seam's context cookie – a stable, test-addressable object the test itself bound into cfg (a stack local cannot be armed). Firmware and ra8_emulator take the plain comparison path.

Returns
ra8_err_t error code.
Return values
k_ra8_okController reports no busy LUTs.
k_ra8_err_hw_timeoutPoll budget exhausted with LUTs still busy.
otherForwarded from the LUTAFSR register read.
Precondition
A DPY_AREA command has been issued.
s_panel.cfg.bus is bound.
Postcondition
No driver state is mutated.
The loop runs at most k_ra8_epaper_lut_poll_max iterations.

Definition at line 902 of file ra8_epaper.c.

References internal_ra8_epaper_reg_read(), k_ra8_epaper_lut_poll_max, k_ra8_epaper_reg_lutafsr, k_ra8_epaper_status_unset, k_ra8_err_hw_timeout, k_ra8_ok, and s_panel.

Referenced by ra8_epaper_display_area().

◆ internal_ra8_epaper_wait_ready()

ra8_err_t internal_ra8_epaper_wait_ready ( void )
staticnodiscard

Block until the panel asserts HRDY.

Polls cfg.busy_pin through ra8_gpio_read with a bounded retry budget. On the host unit-test build the busy pin is mmap'd RAM with no panel to deassert it, so the ra8_fake_mmio fault seam owns the loop-exit decision – first-poll success unless a test arms a fault on the pin's input register (PCNTR2) to drive the timeout leg.

Returns
ra8_err_t error code.
Return values
k_ra8_okHRDY high.
k_ra8_err_hw_timeoutBudget exhausted with HRDY still low.

Definition at line 269 of file ra8_epaper.c.

References k_ra8_epaper_busy_poll_max, k_ra8_err_hw_timeout, k_ra8_level_high, k_ra8_level_low, k_ra8_ok, r_port_regs_t::PCNTR2, pin, ra8_gpio_read(), ra8_log_error, RA8_PIN_PORT, ra8_port(), s_panel, and s_tag.

Referenced by internal_ra8_epaper_read_data16(), internal_ra8_epaper_write_cmd(), internal_ra8_epaper_write_data16(), and ra8_epaper_init().

◆ internal_ra8_epaper_wire_pf()

uint16_t internal_ra8_epaper_wire_pf ( ra8_epaper_pixel_format_t pf)
staticnodiscard

Map a driver pixel-format selector onto the LD_IMG_AREA wire code.

The controller's arg0 bitfield numbers formats 2/3/4/8 bpp as 0/1/2/3. 1 bpp has no code of its own, so it maps to the 8 bpp code here. See ra8_epaper_wire_pf_t for why that mapping on its own does not constitute a working 1 bpp path.

Parameters
[in]pfDriver-side pixel format.
Returns
The two-bit code for arg0.

Definition at line 497 of file ra8_epaper.c.

References k_ra8_epaper_pf_1bpp, k_ra8_epaper_pf_2bpp, k_ra8_epaper_pf_4bpp, k_ra8_epaper_pf_8bpp, k_ra8_epaper_wire_pf_2bpp, k_ra8_epaper_wire_pf_4bpp, and k_ra8_epaper_wire_pf_8bpp.

Referenced by internal_ra8_epaper_send_load_args().

◆ internal_ra8_epaper_write_cmd()

ra8_err_t internal_ra8_epaper_write_cmd ( uint16_t cmd)
staticnodiscard

◆ internal_ra8_epaper_write_data16()

ra8_err_t internal_ra8_epaper_write_data16 ( uint16_t word)
staticnodiscard

◆ ra8_epaper_dev_info()

ra8_err_t ra8_epaper_dev_info ( ra8_epaper_dev_info_t * out_info)
nodiscard

Hand back the GET_DEV_INFO block captured during init.

ra8_epaper_init decodes the 40-byte response once and caches it, so this is an O(1) copy with no bus traffic.

Parameters
[out]out_infoReceives the decoded device block; non-NULL.
Returns
ra8_err_t error code.
Return values
k_ra8_okInfo copied.
k_ra8_err_null_ptrout_info is NULL.
k_ra8_err_invalid_statePanel never initialized.
Precondition
ra8_epaper_init succeeded.
out_info is writable.
Postcondition
*out_info mirrors the cached block.
No bus traffic and no driver state mutated.
Note
Thread-safe against other readers; not against ra8_epaper_init.
See also
ra8_epaper_dev_info_t
Since
0.1.0

Definition at line 701 of file ra8_epaper.c.

References k_ra8_epaper_state_ready, k_ra8_err_invalid_state, k_ra8_ok, RA8_CHECK_NULL_PTR, s_panel, and s_tag.

◆ ra8_epaper_display_area()

ra8_err_t ra8_epaper_display_area ( const ra8_epaper_area_t * area,
ra8_epaper_waveform_t waveform )
nodiscard

Refresh the indicated rectangle on the physical panel.

Resolves waveform through the panel's ra8_epaper_waveform_cfg_t (supplied at init) into the LUT mode number this panel's controller firmware expects, issues DPY_AREA (0x0034) with area and that mode number, then polls REG_LUTAFSR (0x1224) until it reads zero (no LUTs busy). IT8951 datasheet rev 0.2 chapter 4.2.4.

This call enforces INV-VCOM-1. DPY_AREA is the command that puts a bias across the electrophoretic film, so it is refused outright unless ra8_epaper_set_vcom has programmed a VCOM and read it back unchanged. There is no override and no "just this once" path: a wrong or unknown bias damages the panel cumulatively with time under bias, and a blank screen is recoverable where a degraded panel is not. Stage pixels with ra8_epaper_load_image as much as you like beforehand – loading frame RAM applies no bias and is deliberately ungated.

Parameters
[in]areaRectangle to refresh (panel coords); non-NULL.
[in]waveformSymbolic waveform selector – not a wire value.
Returns
ra8_err_t error code.
Return values
k_ra8_okPanel idle and updated.
k_ra8_err_null_ptrarea is NULL.
k_ra8_err_invalid_statePanel never initialized.
k_ra8_err_validation_failedNo verified VCOM – INV-VCOM-1 refusal. Resolve one through ra8_epd_cal_resolve and apply it; do not retry blind.
k_ra8_err_invalid_argwaveform is not a known selector.
k_ra8_err_hw_timeoutLUT busy never cleared.
Precondition
ra8_epaper_load_image populated the area.
ra8_epaper_vcom_verified() is true.
Postcondition
The pixels of area match the most recent load.
The controller reports every LUT idle again.
Note
Not thread-safe.
See also
ra8_epaper_set_vcom
ra8_epaper_vcom_verified
ra8_epaper_waveform_cfg_for_lut
Since
0.1.0

Definition at line 927 of file ra8_epaper.c.

References internal_ra8_epaper_resolve_waveform(), internal_ra8_epaper_send_display_args(), internal_ra8_epaper_wait_lut_idle(), internal_ra8_epaper_write_cmd(), k_ra8_epaper_cmd_dpy_area, k_ra8_epaper_state_ready, k_ra8_err_invalid_state, k_ra8_err_validation_failed, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, s_panel, and s_tag.

Referenced by internal_eink_flush().

◆ ra8_epaper_get_vcom()

ra8_err_t ra8_epaper_get_vcom ( uint16_t * out_mv)
nodiscard

Read the controller's current VCOM setting, in millivolts.

Issues the VCOM command (0x0039) with argument word 0x0000 ("get") and reads back one data word. The returned value is the magnitude of the panel common-electrode bias in millivolts: VCOM is always negative, so a panel labelled -1.53V reads back as 1530. Confirmed against the Waveshare IT8951 reference driver's EPD_IT8951_GetVCOM (USDEF_I80_CMD_VCOM = 0x0039).

Parameters
[out]out_mvReceives the VCOM magnitude in millivolts; non-NULL.
Returns
ra8_err_t error code.
Return values
k_ra8_okValue read.
k_ra8_err_null_ptrout_mv is NULL.
k_ra8_err_invalid_statePanel never initialized.
k_ra8_err_hw_timeoutHRDY stuck low.
Precondition
ra8_epaper_init succeeded.
out_mv is writable.
Postcondition
On success *out_mv holds the controller's VCOM magnitude.
No panel pixels are disturbed.
Note
Not thread-safe.
Warning
The value the controller powers up with is whatever its own driver board persisted. Treat it as a candidate to be range checked, never as authoritative – see ra8_epd_cal_resolve.
See also
ra8_epaper_set_vcom
Since
0.1.0

Definition at line 711 of file ra8_epaper.c.

References internal_ra8_epaper_read_data16(), internal_ra8_epaper_write_cmd(), internal_ra8_epaper_write_data16(), k_ra8_epaper_cmd_vcom, k_ra8_epaper_state_ready, k_ra8_epaper_vcom_get, k_ra8_err_invalid_state, k_ra8_ok, RA8_CHECK_NULL_PTR, s_panel, and s_tag.

Referenced by ep_vcom_get(), and ra8_epaper_set_vcom().

◆ ra8_epaper_init()

ra8_err_t ra8_epaper_init ( const ra8_epaper_cfg_t * cfg)
nodiscard

Bring up the IT8951 panel against the injected SPI bus seam.

Algorithm:

  1. Validate cfg (non-NULL bus.xfer8, sane panel geometry).
  2. Pulse the panel /RESET line (10 ms low / 10 ms high) so the IT8951 resets into a known state.
  3. Wait for HRDY to assert.
  4. Send SYS_RUN (0x0001) to take the controller out of standby.
  5. Send GET_DEV_INFO (0x0302) and consume the 40-byte response so the device's panel size matches cfg.

The SPI bus itself is app-owned: initialise the peripheral behind cfg->bus (mode 0, at most the IT8951's 24 MHz ceiling) before calling this.

Parameters
[in]cfgConfiguration descriptor.
Returns
ra8_err_t error code.
Return values
k_ra8_okPanel responsive and identified.
k_ra8_err_null_ptrcfg is NULL.
k_ra8_err_invalid_argcfg field out of range or cfg->bus.xfer8 NULL.
k_ra8_err_invalid_stateDriver already initialized.
k_ra8_err_hw_timeoutHRDY never asserted.
Precondition
The SPI peripheral behind cfg->bus is initialised (mode 0).
Reset / busy pins configured as GPIO output / input.
Postcondition
On success, the driver state machine is in k_ra8_epaper_state_ready and accepts load / display calls.
Note
Not thread-safe; called once during single-threaded init.
Since
0.1.0

Definition at line 654 of file ra8_epaper.c.

References internal_ra8_epaper_pulse_reset(), internal_ra8_epaper_read_dev_info(), internal_ra8_epaper_wait_ready(), internal_ra8_epaper_write_cmd(), k_ra8_epaper_cmd_sys_run, k_ra8_epaper_state_ready, k_ra8_epaper_state_uninit, k_ra8_err_invalid_state, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_epaper_geometry_agrees(), ra8_epaper_validate_cfg(), ra8_log_error, ra8_log_info, ra8_log_warn, s_panel, and s_tag.

Referenced by internal_eink_init().

◆ ra8_epaper_load_image()

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 )
nodiscard

Push a packed greyscale buffer into the controller's frame RAM.

Sends the LD_IMG_AREA (0x0021) command then streams buf over SPI. Internally:

  1. Set target frame address via REG_LISAR (0x0208 / 0x020A).
  2. Send LD_IMG_AREA + 5-arg block: endianness, pf, rotate, area.
  3. Stream buf 16-bit words MSB-first.
  4. Send LD_IMG_END (0x0022).

buf is packed at pf's depth, rows byte-aligned – exactly ra8_epaper_image_bytes(area, pf, ...) bytes. Prefer k_ra8_epaper_pf_4bpp for greyscale: it matches the panel's 16 grey levels, the controller discards the low nibble of 8 bpp data anyway, and it halves the wire time.

Parameters
[in]areaRectangle to update (panel coords); non-NULL. For k_ra8_epaper_pf_1bpp, x and width must be multiples of k_ra8_epaper_align_1bpp_px.
[in]bufPacked source buffer; non-NULL.
[in]buf_lenLength of buf in bytes; must equal ra8_epaper_image_bytes(area, pf, ...).
[in]pfSource pixel depth.
[in]endianSource endianness.
Returns
ra8_err_t error code.
Return values
k_ra8_okBuffer transferred.
k_ra8_err_null_ptrarea or buf is NULL.
k_ra8_err_invalid_statePanel never initialized.
k_ra8_err_invalid_sizebuf_len mismatch or empty area.
k_ra8_err_invalid_arg1 bpp rectangle violates the 32-pixel X/width alignment.
k_ra8_err_hw_timeoutHRDY stuck low.
Precondition
ra8_epaper_init succeeded.
buf_len matches area at depth pf.
Postcondition
Frame RAM holds the new pixel data; panel is NOT yet refreshed.
On any error path no partial area is marked for refresh.
Note
Not thread-safe.
See also
ra8_epaper_image_bytes
ra8_epaper_align_area
Since
0.1.0

Definition at line 837 of file ra8_epaper.c.

References internal_ra8_epaper_reg_write(), internal_ra8_epaper_send_load_args(), internal_ra8_epaper_stream_pixels(), internal_ra8_epaper_validate_load(), internal_ra8_epaper_write_cmd(), k_ra8_epaper_cmd_ld_img_area, k_ra8_epaper_cmd_ld_img_end, k_ra8_epaper_reg_lisar_hi, k_ra8_epaper_reg_lisar_lo, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by internal_eink_load_rect().

◆ ra8_epaper_set_vcom()

ra8_err_t ra8_epaper_set_vcom ( uint16_t mv)
nodiscard

Implementation of ra8_epaper_set_vcom() – write, then read back and compare before granting the INV-VCOM-1 permit.

Programme the controller's VCOM, verify it, and grant the INV-VCOM-1 permit.

Definition at line 730 of file ra8_epaper.c.

References internal_ra8_epaper_write_cmd(), internal_ra8_epaper_write_data16(), k_ra8_epaper_cmd_vcom, k_ra8_epaper_state_ready, k_ra8_epaper_vcom_set, k_ra8_err_invalid_arg, k_ra8_err_invalid_state, k_ra8_err_validation_failed, k_ra8_ok, ra8_epaper_get_vcom(), ra8_log_error, s_panel, and s_tag.

Referenced by ep_vcom_set().

◆ ra8_epaper_sleep()

ra8_err_t ra8_epaper_sleep ( void )
nodiscard

Drop the panel into deep-sleep (~15 uA per Waveshare AN).

Issues SLEEP (0x0003). Subsequent calls require a fresh ra8_epaper_init because the controller forgets all register state.

Returns
ra8_err_t error code.
Return values
k_ra8_okPanel asleep.
k_ra8_err_invalid_statePanel never initialized.
Precondition
ra8_epaper_init succeeded.
Postcondition
Driver state machine is back in uninitialized; caller must re-init before any further load / display.
Note
Not thread-safe.
Since
0.1.0

Definition at line 961 of file ra8_epaper.c.

References internal_ra8_epaper_write_cmd(), k_ra8_epaper_cmd_sleep, k_ra8_epaper_state_ready, k_ra8_epaper_state_uninit, k_ra8_err_invalid_state, and s_panel.

Referenced by internal_eink_deinit().

◆ ra8_epaper_vcom_verified()

bool ra8_epaper_vcom_verified ( void )
nodiscard

Implementation of ra8_epaper_vcom_verified() – the latch alone already implies the lifecycle state.

Report whether the INV-VCOM-1 permit is currently held.

Definition at line 781 of file ra8_epaper.c.

References s_panel.

Variable Documentation

◆ s_panel

◆ s_tag

const char* s_tag = "EPAPER"
static

Logging tag used by every error path in this TU.

Definition at line 49 of file ra8_epaper.c.