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

Per-device e-paper panel calibration (VCOM) – record, storage seam, resolution policy. More...

#include <stddef.h>
#include <stdint.h>
#include "ra8_err.h"
Include dependency graph for ra8_epd_cal.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ra8_epd_cal_limits_mv_t
 The panel's documented VCOM window, in millivolt magnitudes. More...
struct  ra8_epd_cal_record_t
 Decoded per-device calibration record. More...
struct  ra8_epd_cal_store_t
 Injected non-volatile store for the calibration record. More...
struct  ra8_epd_cal_panel_ops_t
 Injected controller access for VCOM. More...
struct  ra8_epd_cal_cfg_t
 Everything ra8_epd_cal_resolve needs. More...
struct  ra8_epd_cal_result_t
 Outcome of ra8_epd_cal_resolve. More...

Typedefs

typedef ra8_err_t(* ra8_epd_cal_nv_read_fn_t) (void *ctx, uint8_t *dst, size_t len)
 Read the serialised record out of non-volatile storage (DI seam).
typedef ra8_err_t(* ra8_epd_cal_nv_write_fn_t) (void *ctx, const uint8_t *src, size_t len)
 Durably store the serialised record (DI seam).
typedef ra8_err_t(* ra8_epd_cal_panel_get_fn_t) (void *ctx, uint16_t *out_mv)
 Read the controller's current VCOM, in millivolts (DI seam).
typedef ra8_err_t(* ra8_epd_cal_panel_set_fn_t) (void *ctx, uint16_t mv)
 Programme the controller's VCOM, in millivolts (DI seam).

Enumerations

enum  ra8_epd_cal_limits_t : uint16_t {
  k_ra8_epd_cal_blob_size = 32U ,
  k_ra8_epd_cal_schema_version = 1U ,
  k_ra8_epd_cal_payload_len = 2U
}
 Sizing and schema constants for the calibration record. More...
enum  ra8_epd_cal_layout_t : uint8_t {
  k_ra8_epd_cal_off_magic = 0U ,
  k_ra8_epd_cal_off_version = 4U ,
  k_ra8_epd_cal_off_reserved0 = 5U ,
  k_ra8_epd_cal_off_payload_len = 6U ,
  k_ra8_epd_cal_off_vcom_mv = 8U ,
  k_ra8_epd_cal_off_reserved1 = 10U ,
  k_ra8_epd_cal_off_crc32 = 28U
}
 Byte offsets inside the serialised record. More...
enum  ra8_epd_cal_magic_t : uint8_t {
  k_ra8_epd_cal_magic_b0 = 0x45U ,
  k_ra8_epd_cal_magic_b1 = 0x56U ,
  k_ra8_epd_cal_magic_b2 = 0x43U ,
  k_ra8_epd_cal_magic_b3 = 0x4DU
}
 Bytes of the record magic 'EVCM' (E-paper VCOM). More...
enum  ra8_epd_cal_storage_t : uint32_t { k_ra8_epd_cal_extra_mram_offset = 0x200U }
 Placement of the record in non-volatile storage. More...
enum  ra8_epd_cal_source_t : uint8_t {
  k_ra8_epd_cal_src_none = 0U ,
  k_ra8_epd_cal_src_panel = 1U ,
  k_ra8_epd_cal_src_record = 2U ,
  k_ra8_epd_cal_src_provisioned = 3U ,
  k_ra8_epd_cal_src_bench = 4U
}
 Which authority supplied the resolved VCOM. More...

Functions

bool ra8_epd_cal_vcom_in_range (uint16_t mv, const ra8_epd_cal_limits_mv_t *limits)
 Range-check a candidate VCOM against a panel's documented window.
ra8_err_t ra8_epd_cal_serialize (const ra8_epd_cal_record_t *rec, uint8_t *dst, size_t dst_size)
 Serialise a record into its 32-byte on-flash form.
ra8_err_t ra8_epd_cal_deserialize (const uint8_t *src, size_t src_size, ra8_epd_cal_record_t *out_rec)
 Parse and integrity-check a serialised record.
ra8_err_t ra8_epd_cal_resolve (const ra8_epd_cal_cfg_t *cfg, ra8_epd_cal_result_t *out_result)
 Resolve the VCOM to drive this panel at, or fail safe.
ra8_err_t ra8_epd_cal_apply (const ra8_epd_cal_cfg_t *cfg, const ra8_epd_cal_result_t *result)
 Programme a resolved VCOM onto the controller and confirm it took.
ra8_err_t ra8_epd_cal_provision (const ra8_epd_cal_cfg_t *cfg, uint16_t vcom_mv)
 Write a per-device calibration record to non-volatile storage.

Detailed Description

Per-device e-paper panel calibration (VCOM) – record, storage seam, resolution policy.

Tag
[Ring 4 / Service] {World: NS}

Why this module exists

Every electrophoretic panel is manufactured with its own VCOM – the common-electrode bias that centres the drive waveform on that particular sheet of film. The correct value is measured at the factory and printed on that panel's flex cable (e.g. -1.53V). It is per-unit data, in exactly the same sense as a board's MAC address or a sensor's trim constants.

Driving a panel at the wrong VCOM is not a cosmetic problem:

  • Immediately, contrast drops and ghosting appears.
  • Cumulatively, the net DC bias left across the film degrades it permanently. The damage is not recoverable in software.

That makes VCOM the one value in this system where a plausible-looking wrong number is worse than no number at all, and it drives every design decision below.

Rule 1: VCOM is never a source constant

A hardcoded VCOM is correct for exactly the one panel the author had on the bench. Anyone who clones this firmware onto a panel calibrated differently gets a wrong value and quietly damages their hardware – and so does the original owner the day they replace a cracked panel. There is therefore no default VCOM anywhere in this tree, and no code path that invents one.

Rule 2: resolution order, with an explicit dead end

ra8_epd_cal_resolve consults sources in descending order of authority and stops at the first that yields an in-range value:

  1. The controller's own persisted value. The IT8951 driver board powers up with whatever VCOM its own configuration holds, which for a vendor-provisioned board is the right one. Read it back with the VCOM command and range-check it. This is the source that makes a stock Waveshare HAT work out of the box.
  2. The per-device configuration record (ra8_epd_cal_record_t) read through the injected ra8_epd_cal_store_t. This is the authority once a panel has been provisioned on this device, and the only one that survives a controller that forgets.
  3. An explicit provisioning value supplied by the operator for this boot – the number read off the panel's flex cable.

If none of those produces a trusted value, resolution fails (k_ra8_err_not_found, source == k_ra8_epd_cal_src_none) and the caller must refuse to drive the panel. A blank screen is a support call; a DC-biased panel is landfill.

Resolving a value is necessary but not sufficient: ra8_epd_cal_apply writes it and then reads it back and compares, and the e-paper driver refuses every display command until a VCOM has been echoed back unchanged. A link that accepts writes and changes nothing is the failure mode that otherwise looks exactly like success.

The one build-flag exception, and why it is not a hole

-DRA8_BENCH_VCOM_MV=<mv> adds a fourth, lowest-authority source so first light on a bench panel is not blocked behind provisioned storage. It is off unless typed on the compiler command line, it is consulted only after all three real sources decline (so it can never override a genuine calibration), it is range-checked like everything else, it logs loudly on every boot that uses it, and it is a compile error when RA8_PRODUCTION_BUILD is defined. That last guard is what keeps it from becoming the shipped hardcoded VCOM this module exists to prevent.

Rule 3: the record must outlive firmware updates

The device carries an A/B-bank DFU bootloader (libs/ra8_dfu) with Root-of-Trust-signed images (ra8_rot_verify_image). Two consequences follow, and they are the whole reason this record has its own integrity check rather than leaning on the image signature:

  • Per-device bytes cannot live inside the signed image. One signature authenticates one exact byte sequence. A value that differs per unit cannot be covered by an image signature shared across units without re-signing per device, which defeats the point of having a single signed release. So the record is outside the signature's scope and must carry its own integrity check – hence the CRC-32 trailer and the schema version below.
  • The record must live outside both code banks. Slot A (0x02020000) and Slot B (0x02090000) are erased and reprogrammed wholesale by DFU, and a rollback re-points the boot at an older image. Anything stored in either bank is destroyed by an update or reverted by a rollback. The record therefore belongs in extra-MRAM (data flash) at k_ra8_flash_extra_start, which no DFU path erases – the same region and the same reasoning as the anti-rollback counter in ra8_dfu_antirollback.c. Its slot is k_ra8_epd_cal_extra_mram_offset, placed clear of that counter.

Because the record is unsigned, its integrity check is a corruption check, not an authenticity one: it detects a torn write or a bit flip, not an attacker with physical write access to data flash. That is the correct threat model here – an attacker who can rewrite data flash can equally attack the panel directly – but it is stated rather than assumed.

Storage layout (k_ra8_epd_cal_blob_size bytes)

*   offset  size  field
*   ------  ----  ------------------------------------------------
*   0       4     magic   = 'E','V','C','M' (ASCII)
*   4       1     version = ::k_ra8_epd_cal_schema_version
*   5       1     reserved (zeroed)
*   6       2     payload_len = bytes of payload (2 for schema 1)
*   8       2     vcom_mv, little-endian magnitude in millivolts
*   10      18    reserved (zeroed, room for later schema growth)
*   28      4     crc32   = IEEE 802.3 polynomial over bytes 0..27
* 

payload_len plus version are what make forward migration possible: a future schema appends fields into the reserved span and bumps both, and this reader rejects what it does not understand rather than misreading it.

The blob is 32 bytes so it maps exactly onto one extra-MRAM erase block.

Dependency inversion

This module includes no HAL header. It reaches the controller and the non-volatile store only through injected function-pointer seams (ra8_epd_cal_panel_ops_t, ra8_epd_cal_store_t) – NASA Power-of-10 Rule 9 deviation, documented in CLAUDE.md. Production binds ra8_epaper_get_vcom / ra8_epaper_set_vcom and an extra-MRAM store; host tests bind mocks and drive every branch.

Since
0.1.0

Definition in file ra8_epd_cal.h.

Typedef Documentation

◆ ra8_epd_cal_nv_read_fn_t

typedef ra8_err_t(* ra8_epd_cal_nv_read_fn_t) (void *ctx, uint8_t *dst, size_t len)

Read the serialised record out of non-volatile storage (DI seam).

Production reads k_ra8_epd_cal_blob_size bytes from extra-MRAM at k_ra8_flash_extra_start + k_ra8_epd_cal_extra_mram_offset; host tests read a RAM fixture. Returning any non-k_ra8_ok code makes the resolver treat the record source as absent and fall through – it never fabricates a value.

Parameters
[in]ctxImplementation context (may be NULL).
[out]dstDestination buffer; non-NULL, at least len bytes.
[in]lenBytes to read; always k_ra8_epd_cal_blob_size.
Returns
ra8_err_t Error code.
Return values
k_ra8_okdst holds len bytes.
k_ra8_err_null_ptrdst was NULL.
otherBacking-store fault; treated as "no record".
Note
Thread safety is the implementation's responsibility; the boot-path caller is single-threaded.
Since
0.1.0

Definition at line 334 of file ra8_epd_cal.h.

◆ ra8_epd_cal_nv_write_fn_t

typedef ra8_err_t(* ra8_epd_cal_nv_write_fn_t) (void *ctx, const uint8_t *src, size_t len)

Durably store the serialised record (DI seam).

Called only from the explicit provisioning path (ra8_epd_cal_provision), never from the boot-time read path – a boot must not silently rewrite calibration.

Parameters
[in]ctxImplementation context (may be NULL).
[in]srcSerialised record; non-NULL, len bytes.
[in]lenBytes to write; always k_ra8_epd_cal_blob_size.
Returns
ra8_err_t Error code.
Return values
k_ra8_okRecord committed durably.
k_ra8_err_null_ptrsrc was NULL.
otherProgram fault; the caller must not assume the record persisted.
Note
Thread safety is the implementation's responsibility.
Since
0.1.0

Definition at line 358 of file ra8_epd_cal.h.

◆ ra8_epd_cal_panel_get_fn_t

typedef ra8_err_t(* ra8_epd_cal_panel_get_fn_t) (void *ctx, uint16_t *out_mv)

Read the controller's current VCOM, in millivolts (DI seam).

Production binds ra8_epaper_get_vcom.

Parameters
[in]ctxImplementation context (may be NULL).
[out]out_mvReceives the VCOM magnitude; non-NULL.
Returns
ra8_err_t Error code.
Return values
k_ra8_ok*out_mv holds the controller's VCOM.
k_ra8_err_null_ptrout_mv was NULL.
otherBus fault; treated as "no value".
Note
Not thread-safe; boot-path use only.
Since
0.1.0

Definition at line 404 of file ra8_epd_cal.h.

◆ ra8_epd_cal_panel_set_fn_t

typedef ra8_err_t(* ra8_epd_cal_panel_set_fn_t) (void *ctx, uint16_t mv)

Programme the controller's VCOM, in millivolts (DI seam).

Production binds ra8_epaper_set_vcom.

Parameters
[in]ctxImplementation context (may be NULL).
[in]mvVCOM magnitude to programme; already range-checked.
Returns
ra8_err_t Error code.
Return values
k_ra8_okController accepted the new VCOM.
otherBus fault; the previous VCOM remains in effect.
Note
Not thread-safe; boot-path use only.
Since
0.1.0

Definition at line 422 of file ra8_epd_cal.h.

Enumeration Type Documentation

◆ ra8_epd_cal_layout_t

enum ra8_epd_cal_layout_t : uint8_t

Byte offsets inside the serialised record.

Enumerator
k_ra8_epd_cal_off_magic 

'EVCM' magic.

k_ra8_epd_cal_off_version 

Schema version byte.

k_ra8_epd_cal_off_reserved0 

One reserved zero byte.

k_ra8_epd_cal_off_payload_len 

Payload length, LE16.

k_ra8_epd_cal_off_vcom_mv 

VCOM magnitude, LE16.

k_ra8_epd_cal_off_reserved1 

Reserved growth span.

k_ra8_epd_cal_off_crc32 

CRC-32 trailer, LE32.

Definition at line 169 of file ra8_epd_cal.h.

◆ ra8_epd_cal_limits_t

enum ra8_epd_cal_limits_t : uint16_t

Sizing and schema constants for the calibration record.

Enumerator
k_ra8_epd_cal_blob_size 

Serialised record size in bytes.

k_ra8_epd_cal_schema_version 

Current on-flash schema version.

k_ra8_epd_cal_payload_len 

Schema-1 payload bytes (vcom_mv).

Definition at line 159 of file ra8_epd_cal.h.

◆ ra8_epd_cal_magic_t

enum ra8_epd_cal_magic_t : uint8_t

Bytes of the record magic 'EVCM' (E-paper VCOM).

Enumerator
k_ra8_epd_cal_magic_b0 

'E'

k_ra8_epd_cal_magic_b1 

'V'

k_ra8_epd_cal_magic_b2 

'C'

k_ra8_epd_cal_magic_b3 

'M'

Definition at line 183 of file ra8_epd_cal.h.

◆ ra8_epd_cal_source_t

enum ra8_epd_cal_source_t : uint8_t

Which authority supplied the resolved VCOM.

Reported by ra8_epd_cal_resolve so the caller can log provenance and so a caller that requires provisioned-on-this-device calibration can reject a merely controller-reported value.

See also
ra8_epd_cal_resolve
Enumerator
k_ra8_epd_cal_src_none 

No trusted value – do NOT drive.

k_ra8_epd_cal_src_panel 

Read back from the controller.

k_ra8_epd_cal_src_record 

From the per-device NV record.

k_ra8_epd_cal_src_provisioned 

Operator-supplied this boot.

k_ra8_epd_cal_src_bench 

RA8_BENCH_VCOM_MV; see below.

Definition at line 248 of file ra8_epd_cal.h.

◆ ra8_epd_cal_storage_t

enum ra8_epd_cal_storage_t : uint32_t

Placement of the record in non-volatile storage.

The offset is relative to k_ra8_flash_extra_start (the extra-MRAM option-setting window base, 0x02E07600 – HUM Ch 59.7.4.5 Table 59.15 p 3592). No DFU path erases extra-MRAM, so the record survives an A/B firmware update and a rollback alike.

Note
The window is one-time-programmable option-setting memory, not a rewritable data-flash; a real rewritable-medium home for this record is a bench question tracked by #315.

Why this is not at the obvious 0x40

Block 0 of the window is already claimed by the DFU anti-rollback counter and block 1 is left spare for it to grow into, which makes 0x40 look like the natural next slot. It is not available: the general per-device configuration record specified alongside this module reserves 0x40 .. 0x1BF for its two sequence-numbered 192-byte copies. Landing this blob at 0x40 would have put a VCOM record exactly on top of copy 0 of that record – two writers, one address, discovered on the first provisioned unit rather than in review. This blob therefore starts after that reservation, at 0x200.

This placement is transitional by design

The per-device record is the intended long-term home for VCOM: one record, one CRC, one signature, all the per-unit bytes together. When it lands, the production binding of ra8_epd_cal_store_t becomes a reader over that record and this standalone blob is retired – the resolution policy in this module, which is the part that matters, does not change, because the store is an injected seam and callers of ra8_epd_cal_resolve cannot tell which backing answered. That substitutability is the whole reason the seam exists.

Invariant
The offset is a multiple of k_ra8_epd_cal_blob_size so the record occupies exactly one 32-byte erase block.
The offset lies outside the per-device record's reserved 0x40 .. 0x1BF span.
See also
ra8_epd_cal_store_t
Enumerator
k_ra8_epd_cal_extra_mram_offset 

Byte offset into extra-MRAM.

Definition at line 233 of file ra8_epd_cal.h.

Function Documentation

◆ ra8_epd_cal_apply()

ra8_err_t ra8_epd_cal_apply ( const ra8_epd_cal_cfg_t * cfg,
const ra8_epd_cal_result_t * result )
nodiscard

Programme a resolved VCOM onto the controller and confirm it took.

Re-checks the value against cfg->limits before writing it, writes it through cfg->panel.set, then reads it straight back through cfg->panel.get and requires an exact match. The pre-write re-check is deliberate belt-and-braces – result is caller-owned memory between resolve and here, and this is the last point before a number reaches the panel.

The readback is the load-bearing part. A dead link, a controller that ignores the command, or a bus stuck returning zeroes all accept the write and change nothing; without the compare that is indistinguishable from success, and the film would sit biased at the controller's own unknown value. The readback is compared for equality and deliberately not re-range-checked – the written value was range-checked a few lines earlier, so an equal readback is in range by construction and a second test would be unreachable code wearing a safety check's clothes. Range validation of a value the controller reports belongs where such a value is adopted rather than confirmed, which is the resolve-from- controller source, and it is applied there.

A cfg with no panel.get binding is rejected rather than write-and-hope: an unconfirmable bias is not one to leave on a panel.

Parameters
[in]cfgSeams and limits; non-NULL, with both panel.set and panel.get bound.
[in]resultA successful ra8_epd_cal_resolve outcome; non-NULL.
Returns
ra8_err_t Error code.
Return values
k_ra8_okController accepted the VCOM and echoed it back unchanged.
k_ra8_err_null_ptrcfg or result is NULL.
k_ra8_err_invalid_stateresult->source is none.
k_ra8_err_not_supportedcfg->panel.set or cfg->panel.get is not bound.
k_ra8_err_range_check_failedresult->vcom_mv is out of range.
k_ra8_err_hw_errorThe readback transaction failed.
k_ra8_err_validation_failedThe controller echoed a different value. Do not drive the panel.
otherForwarded from cfg->panel.set.
Precondition
result came from a successful ra8_epd_cal_resolve.
cfg->panel.set and cfg->panel.get are bound to the controller.
Postcondition
On success the controller reports result->vcom_mv as its VCOM.
On failure the caller must leave the panel dark – the bias in effect is unknown, which is precisely the state to refuse.
Note
Not thread-safe; boot-path use only.
See also
ra8_epd_cal_resolve
ra8_epaper_set_vcom
Since
0.1.0

Definition at line 507 of file ra8_epd_cal.c.

References ra8_epd_cal_panel_ops_t::ctx, ra8_epd_cal_panel_ops_t::get, k_ra8_epd_cal_src_none, k_ra8_err_hw_error, k_ra8_err_invalid_state, k_ra8_err_not_supported, k_ra8_err_range_check_failed, k_ra8_err_validation_failed, k_ra8_ok, ra8_epd_cal_cfg_t::limits, ra8_epd_cal_cfg_t::panel, RA8_CHECK_NULL_PTR, ra8_epd_cal_vcom_in_range(), ra8_log_error, s_tag, ra8_epd_cal_panel_ops_t::set, ra8_epd_cal_result_t::source, and ra8_epd_cal_result_t::vcom_mv.

Referenced by ep_calibrate_vcom().

◆ ra8_epd_cal_deserialize()

ra8_err_t ra8_epd_cal_deserialize ( const uint8_t * src,
size_t src_size,
ra8_epd_cal_record_t * out_rec )
nodiscard

Parse and integrity-check a serialised record.

Validation order is magic, then schema version, then payload length, then CRC-32. Each rejection is distinct so a caller can tell "never provisioned" (blank flash fails the magic check) from "provisioned but corrupted" (magic passes, CRC fails) – the two demand different operator responses.

A record whose version exceeds k_ra8_epd_cal_schema_version is rejected rather than best-effort parsed: a newer writer may have redefined the payload, and misreading calibration is exactly the failure this module exists to prevent.

Parameters
[in]srcSerialised record; non-NULL.
[in]src_sizeBytes available at src; must be >= k_ra8_epd_cal_blob_size.
[out]out_recReceives the decoded record; non-NULL.
Returns
ra8_err_t Error code.
Return values
k_ra8_okRecord valid; *out_rec populated.
k_ra8_err_null_ptrsrc or out_rec is NULL.
k_ra8_err_invalid_sizesrc_size too small.
k_ra8_err_not_foundMagic absent – never provisioned.
k_ra8_err_not_supportedSchema version newer than this build.
k_ra8_err_validation_failedPayload length wrong for the schema.
k_ra8_err_crc_mismatchCRC trailer does not match the body.
Precondition
src holds at least src_size readable bytes.
out_rec is writable.
Postcondition
On success out_rec->vcom_mv is non-zero.
On failure *out_rec is untouched.
Note
Thread-safe (no shared state).
See also
ra8_epd_cal_serialize
Since
0.1.0

Definition at line 348 of file ra8_epd_cal.c.

References internal_ra8_epd_cal_crc32(), internal_ra8_epd_cal_magic_ok(), internal_ra8_epd_cal_unpack_le16(), internal_ra8_epd_cal_unpack_le32(), k_ra8_epd_cal_blob_size, k_ra8_epd_cal_off_crc32, k_ra8_epd_cal_off_payload_len, k_ra8_epd_cal_off_vcom_mv, k_ra8_epd_cal_off_version, k_ra8_epd_cal_payload_len, k_ra8_epd_cal_schema_version, k_ra8_err_crc_mismatch, k_ra8_err_invalid_size, k_ra8_err_not_found, k_ra8_err_not_supported, k_ra8_err_validation_failed, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, s_tag, ra8_epd_cal_record_t::schema_version, and ra8_epd_cal_record_t::vcom_mv.

Referenced by internal_ra8_epd_cal_read_record().

◆ ra8_epd_cal_provision()

ra8_err_t ra8_epd_cal_provision ( const ra8_epd_cal_cfg_t * cfg,
uint16_t vcom_mv )
nodiscard

Write a per-device calibration record to non-volatile storage.

The explicit provisioning path: range-checks vcom_mv, serialises a sealed record and commits it through cfg->store.write. Called by a provisioning app or a service menu after the operator enters the value printed on the panel's flex cable – never automatically from a boot path, so calibration cannot drift without someone asking for it.

Parameters
[in]cfgSeams and limits; non-NULL.
[in]vcom_mvVCOM magnitude to persist, in millivolts.
Returns
ra8_err_t Error code.
Return values
k_ra8_okRecord committed durably.
k_ra8_err_null_ptrcfg is NULL.
k_ra8_err_not_supportedcfg->store.write is not bound.
k_ra8_err_range_check_failedvcom_mv outside cfg->limits.
otherForwarded from cfg->store.write.
Precondition
vcom_mv was read off this panel's own flex cable.
cfg->store.write targets storage outside both DFU code banks.
Postcondition
On success a later ra8_epd_cal_resolve finds the record.
On failure nothing was persisted.
Note
Not thread-safe.
Warning
Provisioning the wrong number is exactly as damaging as having none. Confirm the value against the panel's flex cable.
See also
ra8_epd_cal_resolve
Since
0.1.0

Definition at line 554 of file ra8_epd_cal.c.

References ra8_epd_cal_store_t::ctx, k_ra8_epd_cal_blob_size, k_ra8_epd_cal_schema_version, k_ra8_err_not_supported, k_ra8_err_range_check_failed, k_ra8_ok, ra8_epd_cal_cfg_t::limits, RA8_CHECK_NULL_PTR, ra8_epd_cal_serialize(), ra8_epd_cal_vcom_in_range(), ra8_log_error, s_tag, ra8_epd_cal_cfg_t::store, and ra8_epd_cal_store_t::write.

◆ ra8_epd_cal_resolve()

ra8_err_t ra8_epd_cal_resolve ( const ra8_epd_cal_cfg_t * cfg,
ra8_epd_cal_result_t * out_result )
nodiscard

Resolve the VCOM to drive this panel at, or fail safe.

Walks the sources documented at the top of this file – controller, then per-device record, then operator-provisioned value – and returns the first that yields a value inside cfg->limits. Every source is optional: a NULL seam or a failing read simply moves to the next.

This function never invents a value. When no source yields an in-range number it returns k_ra8_err_not_found with out_result->source == k_ra8_epd_cal_src_none and vcom_mv == 0, and the caller must not drive the panel. That is the whole point: an unpowered panel is recoverable, a panel driven at a guessed bias is not.

Parameters
[in]cfgSeams, limits and any provisioning value; non-NULL.
[out]out_resultReceives the resolved value and its provenance; non-NULL.
Returns
ra8_err_t Error code.
Return values
k_ra8_okA trusted VCOM was resolved.
k_ra8_err_null_ptrcfg or out_result is NULL.
k_ra8_err_invalid_argcfg->limits is self-inconsistent (zero or inverted window).
k_ra8_err_not_foundNo source produced an in-range value – refuse to drive the panel.
Precondition
The controller has been initialised if cfg->panel.get is bound.
cfg->limits states the panel's documented VCOM window.
Postcondition
On success out_result->vcom_mv is inside cfg->limits.
On failure out_result->source is k_ra8_epd_cal_src_none.
Note
Not thread-safe; boot-path use only.
Warning
Success means "a value worth trusting was found", not "the panel is now driving at it". Call ra8_epd_cal_apply to programme it.
Example:
if (ra8_epd_cal_resolve(&cfg, &r) != k_ra8_ok) {
ra8_log_error("APP", "no trusted VCOM -- panel left dark");
return k_ra8_err_not_found; // fail safe
}
(void)ra8_epd_cal_apply(&cfg, &r);
ra8_err_t ra8_epd_cal_resolve(const ra8_epd_cal_cfg_t *cfg, ra8_epd_cal_result_t *out_result)
Resolve the VCOM to drive this panel at, or fail safe.
ra8_err_t ra8_epd_cal_apply(const ra8_epd_cal_cfg_t *cfg, const ra8_epd_cal_result_t *result)
Programme a resolved VCOM onto the controller and confirm it took.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Outcome of ra8_epd_cal_resolve.
See also
ra8_epd_cal_apply
ra8_epd_cal_provision
Since
0.1.0

Definition at line 464 of file ra8_epd_cal.c.

References ra8_epd_cal_cfg_t::has_provisioned, internal_ra8_epd_cal_try_panel(), internal_ra8_epd_cal_try_record(), k_ra8_epd_cal_src_bench, k_ra8_epd_cal_src_none, k_ra8_epd_cal_src_provisioned, k_ra8_err_invalid_arg, k_ra8_err_not_found, k_ra8_ok, ra8_epd_cal_cfg_t::limits, ra8_epd_cal_limits_mv_t::max_mv, ra8_epd_cal_limits_mv_t::min_mv, ra8_epd_cal_cfg_t::provisioned_mv, RA8_CHECK_NULL_PTR, ra8_epd_cal_vcom_in_range(), ra8_log_error, ra8_log_warn, s_tag, ra8_epd_cal_result_t::source, and ra8_epd_cal_result_t::vcom_mv.

Referenced by ep_calibrate_vcom().

◆ ra8_epd_cal_serialize()

ra8_err_t ra8_epd_cal_serialize ( const ra8_epd_cal_record_t * rec,
uint8_t * dst,
size_t dst_size )
nodiscard

Serialise a record into its 32-byte on-flash form.

Writes magic, schema version, payload length and the VCOM field, zeroes every reserved byte, then appends the CRC-32 (IEEE 802.3) of everything preceding the trailer. Reserved bytes are zeroed rather than left undefined so the CRC is reproducible for a given record.

Parameters
[in]recRecord to serialise; non-NULL.
[out]dstDestination buffer; non-NULL.
[in]dst_sizeCapacity of dst; must be >= k_ra8_epd_cal_blob_size.
Returns
ra8_err_t Error code.
Return values
k_ra8_okdst holds a sealed record.
k_ra8_err_null_ptrrec or dst is NULL.
k_ra8_err_invalid_sizedst_size too small.
k_ra8_err_invalid_argrec->vcom_mv is zero.
Precondition
dst has room for k_ra8_epd_cal_blob_size bytes.
rec->vcom_mv was range-checked by the caller.
Postcondition
On success ra8_epd_cal_deserialize round-trips dst.
On failure dst is untouched.
Note
Thread-safe (no shared state).
See also
ra8_epd_cal_deserialize
Since
0.1.0

Definition at line 318 of file ra8_epd_cal.c.

References internal_ra8_epd_cal_crc32(), internal_ra8_epd_cal_pack_le16(), internal_ra8_epd_cal_pack_le32(), k_ra8_epd_cal_blob_size, k_ra8_epd_cal_magic_b0, k_ra8_epd_cal_magic_b1, k_ra8_epd_cal_magic_b2, k_ra8_epd_cal_magic_b3, k_ra8_epd_cal_off_b0, k_ra8_epd_cal_off_b1, k_ra8_epd_cal_off_b2, k_ra8_epd_cal_off_b3, k_ra8_epd_cal_off_crc32, k_ra8_epd_cal_off_magic, k_ra8_epd_cal_off_payload_len, k_ra8_epd_cal_off_vcom_mv, k_ra8_epd_cal_off_version, k_ra8_epd_cal_payload_len, k_ra8_epd_cal_schema_version, k_ra8_err_invalid_arg, k_ra8_err_invalid_size, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, s_tag, and ra8_epd_cal_record_t::vcom_mv.

Referenced by ra8_epd_cal_provision().

◆ ra8_epd_cal_vcom_in_range()

bool ra8_epd_cal_vcom_in_range ( uint16_t mv,
const ra8_epd_cal_limits_mv_t * limits )
nodiscard

Range-check a candidate VCOM against a panel's documented window.

The single decision every source's value passes through. Rejects the two failure signatures that matter in practice – 0 from a controller that has not finished booting, and 0xFFFF from blank flash or a failed read – by requiring a non-zero min_mv in the limits.

Parameters
[in]mvCandidate VCOM magnitude in millivolts.
[in]limitsPanel's documented window; non-NULL.
Returns
Whether mv may be programmed.
Return values
truelimits->min_mv <= mv <= limits->max_mv.
falsemv is outside the window, or limits is NULL.
Precondition
limits is NULL or readable.
limits->min_mv <= limits->max_mv.
Postcondition
No state mutated.
Return depends solely on the arguments.
Note
Pure function; thread-safe.
See also
ra8_epd_cal_resolve
Since
0.1.0

Definition at line 306 of file ra8_epd_cal.c.

References ra8_epd_cal_limits_mv_t::max_mv.

Referenced by internal_ra8_epd_cal_try_panel(), internal_ra8_epd_cal_try_record(), ra8_epd_cal_apply(), ra8_epd_cal_provision(), and ra8_epd_cal_resolve().