|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Per-device e-paper VCOM calibration – record codec and resolver. More...
#include "ra8_epd_cal.h"#include <stddef.h>#include <stdint.h>#include "ra8_attributes.h"#include "ra8_check.h"#include "ra8_err.h"#include "ra8_log.h"Go to the source code of this file.
Enumerations | |
| enum | ra8_epd_cal_crc_t : uint32_t { k_ra8_epd_cal_crc_init = 0xFFFFFFFFU , k_ra8_epd_cal_crc_poly = 0xEDB88320U } |
| IEEE 802.3 CRC-32 parameters. More... | |
| enum | ra8_epd_cal_bits_t : uint8_t { k_ra8_epd_cal_bits_per_byte = 8U , k_ra8_epd_cal_byte_shift = 8U , k_ra8_epd_cal_byte_mask = 0xFFU , k_ra8_epd_cal_off_b0 = 0U , k_ra8_epd_cal_off_b1 = 1U , k_ra8_epd_cal_off_b2 = 2U , k_ra8_epd_cal_off_b3 = 3U } |
| Byte / word packing constants (no magic numbers). More... | |
Functions | |
| static uint32_t | internal_ra8_epd_cal_crc32 (const uint8_t *data, size_t len) |
| Compute the IEEE 802.3 CRC-32 of a byte span. | |
| static void | internal_ra8_epd_cal_pack_le16 (uint8_t *dst, uint16_t val) |
| Store a little-endian 16-bit value. | |
| static uint16_t | internal_ra8_epd_cal_unpack_le16 (const uint8_t *src) |
| Load a little-endian 16-bit value. | |
| static void | internal_ra8_epd_cal_pack_le32 (uint8_t *dst, uint32_t val) |
| Store a little-endian 32-bit value. | |
| static uint32_t | internal_ra8_epd_cal_unpack_le32 (const uint8_t *src) |
| Load a little-endian 32-bit value. | |
| static bool | internal_ra8_epd_cal_magic_ok (const uint8_t *src) |
| Report whether a serialised record carries the 'EVCM' magic. | |
| static ra8_err_t | internal_ra8_epd_cal_read_record (const ra8_epd_cal_store_t *store, ra8_epd_cal_record_t *out_rec) |
| Read and validate the per-device record through the store seam. | |
| 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. | |
| static ra8_err_t | internal_ra8_epd_cal_try_panel (const ra8_epd_cal_cfg_t *cfg, ra8_epd_cal_result_t *out_result) |
| Try the controller's own persisted VCOM (resolution source 1). | |
| static ra8_err_t | internal_ra8_epd_cal_try_record (const ra8_epd_cal_cfg_t *cfg, ra8_epd_cal_result_t *out_result) |
| Try the per-device record in storage (resolution source 2). | |
| 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. | |
Variables | |
| static const char *const | s_tag = "EPD_CAL" |
| Logging tag used by every error path in this TU. | |
Per-device e-paper VCOM calibration – record codec and resolver.
Implements ra8_epd_cal.h. Two responsibilities, deliberately kept apart:
No HAL header is included: the controller and the non-volatile store are reached only through the injected seams in the config struct, which is what lets every branch below run on the host.
Definition in file ra8_epd_cal.c.
| enum ra8_epd_cal_bits_t : uint8_t |
Byte / word packing constants (no magic numbers).
Definition at line 103 of file ra8_epd_cal.c.
| enum ra8_epd_cal_crc_t : uint32_t |
IEEE 802.3 CRC-32 parameters.
Bit-banged rather than table-driven so the module has no dependency on a CRC library and no 1 KiB table in a build that only ever checks 32 bytes once per boot. Mirrors the constants in ra8_touch_cal.c so both calibration records use the same polynomial.
| Enumerator | |
|---|---|
| k_ra8_epd_cal_crc_init | CRC seed / final XOR. |
| k_ra8_epd_cal_crc_poly | Reversed IEEE 802.3 poly. |
Definition at line 94 of file ra8_epd_cal.c.
|
staticnodiscard |
Compute the IEEE 802.3 CRC-32 of a byte span.
Bit-banged reflected-polynomial form; no lookup table.
| [in] | data | Bytes to checksum; non-NULL. |
| [in] | len | Number of bytes. |
Definition at line 133 of file ra8_epd_cal.c.
References k_ra8_epd_cal_bits_per_byte, k_ra8_epd_cal_crc_init, and k_ra8_epd_cal_crc_poly.
Referenced by ra8_epd_cal_deserialize(), and ra8_epd_cal_serialize().
|
staticnodiscard |
Report whether a serialised record carries the 'EVCM' magic.
| [in] | src | Serialised record; non-NULL, >= blob size. |
Definition at line 252 of file ra8_epd_cal.c.
References 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, and k_ra8_epd_cal_off_magic.
Referenced by ra8_epd_cal_deserialize().
|
static |
Store a little-endian 16-bit value.
The record is serialised byte-at-a-time in an explicit byte order so a blob written on one machine deserialises identically on another. The host test suite is little-endian x86_64 and the target is little-endian Arm, but relying on that would make the on-storage format a property of the compiler rather than of the record.
| [out] | dst | Destination for two bytes; non-NULL. |
| [in] | val | Value to pack. |
Definition at line 169 of file ra8_epd_cal.c.
References k_ra8_epd_cal_byte_mask, k_ra8_epd_cal_byte_shift, k_ra8_epd_cal_off_b0, and k_ra8_epd_cal_off_b1.
Referenced by ra8_epd_cal_serialize().
|
static |
Store a little-endian 32-bit value.
The 32-bit counterpart of internal_ra8_epd_cal_pack_le16, used for the magic word and the CRC. Same rationale: the byte order is a property of the record format, not of the compiler that wrote it.
| [out] | dst | Destination for four bytes; non-NULL. |
| [in] | val | Value to pack. |
Definition at line 213 of file ra8_epd_cal.c.
References k_ra8_epd_cal_byte_mask, k_ra8_epd_cal_byte_shift, k_ra8_epd_cal_off_b0, k_ra8_epd_cal_off_b1, k_ra8_epd_cal_off_b2, and k_ra8_epd_cal_off_b3.
Referenced by ra8_epd_cal_serialize().
|
staticnodiscard |
Read and validate the per-device record through the store seam.
Any failure – unbound seam, read fault, blank flash, corrupt record – reports "no record" so the resolver falls through to the next source. The distinction between the failure modes is logged, not returned, because the resolver's behaviour is the same for all of them.
| [in] | store | Store seam; non-NULL. |
| [out] | out_rec | Receives the decoded record; non-NULL. |
Definition at line 279 of file ra8_epd_cal.c.
References ra8_epd_cal_store_t::ctx, k_ra8_epd_cal_blob_size, k_ra8_err_crc_mismatch, k_ra8_err_not_supported, k_ra8_ok, ra8_epd_cal_deserialize(), ra8_log_error, ra8_log_warn, ra8_epd_cal_store_t::read, and s_tag.
Referenced by internal_ra8_epd_cal_try_record().
|
staticnodiscard |
Try the controller's own persisted VCOM (resolution source 1).
A vendor-provisioned IT8951 driver board powers up holding the right VCOM, which is what makes a stock HAT work without provisioning. The value is still range-checked: a controller that has not finished booting reports 0, and a failed read reports 0xFFFF.
| [in] | cfg | Resolution config; non-NULL. |
| [out] | out_result | Populated only on success; non-NULL. |
Definition at line 410 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_panel, k_ra8_err_hw_error, k_ra8_err_not_supported, k_ra8_err_range_check_failed, k_ra8_ok, ra8_epd_cal_cfg_t::limits, ra8_epd_cal_cfg_t::panel, ra8_epd_cal_vcom_in_range(), ra8_log_warn, s_tag, ra8_epd_cal_result_t::source, and ra8_epd_cal_result_t::vcom_mv.
Referenced by ra8_epd_cal_resolve().
|
staticnodiscard |
Try the per-device record in storage (resolution source 2).
The authority once a panel has been provisioned on this device, and the only source that survives a controller which has forgotten its own configuration. Lives outside both DFU code banks so a firmware update or a rollback cannot erase it.
| [in] | cfg | Resolution config; non-NULL. |
| [out] | out_result | Populated only on success; non-NULL. |
Definition at line 448 of file ra8_epd_cal.c.
References internal_ra8_epd_cal_read_record(), k_ra8_epd_cal_src_record, k_ra8_err_not_found, k_ra8_err_range_check_failed, k_ra8_ok, ra8_epd_cal_cfg_t::limits, ra8_epd_cal_vcom_in_range(), ra8_log_warn, s_tag, ra8_epd_cal_result_t::source, ra8_epd_cal_cfg_t::store, ra8_epd_cal_record_t::vcom_mv, and ra8_epd_cal_result_t::vcom_mv.
Referenced by ra8_epd_cal_resolve().
|
staticnodiscard |
Load a little-endian 16-bit value.
| [in] | src | Two source bytes; non-NULL. |
Definition at line 186 of file ra8_epd_cal.c.
References k_ra8_epd_cal_byte_shift, k_ra8_epd_cal_off_b0, and k_ra8_epd_cal_off_b1.
Referenced by ra8_epd_cal_deserialize().
|
staticnodiscard |
Load a little-endian 32-bit value.
| [in] | src | Four source bytes; non-NULL. |
Definition at line 234 of file ra8_epd_cal.c.
References k_ra8_epd_cal_byte_shift, k_ra8_epd_cal_off_b0, k_ra8_epd_cal_off_b1, k_ra8_epd_cal_off_b2, and k_ra8_epd_cal_off_b3.
Referenced by ra8_epd_cal_deserialize().
|
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.
| [in] | cfg | Seams and limits; non-NULL, with both panel.set and panel.get bound. |
| [in] | result | A successful ra8_epd_cal_resolve outcome; non-NULL. |
| k_ra8_ok | Controller accepted the VCOM and echoed it back unchanged. |
| k_ra8_err_null_ptr | cfg or result is NULL. |
| k_ra8_err_invalid_state | result->source is none. |
| k_ra8_err_not_supported | cfg->panel.set or cfg->panel.get is not bound. |
| k_ra8_err_range_check_failed | result->vcom_mv is out of range. |
| k_ra8_err_hw_error | The readback transaction failed. |
| k_ra8_err_validation_failed | The controller echoed a different value. Do not drive the panel. |
| other | Forwarded from cfg->panel.set. |
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().
|
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.
| [in] | src | Serialised record; non-NULL. |
| [in] | src_size | Bytes available at src; must be >= k_ra8_epd_cal_blob_size. |
| [out] | out_rec | Receives the decoded record; non-NULL. |
| k_ra8_ok | Record valid; *out_rec populated. |
| k_ra8_err_null_ptr | src or out_rec is NULL. |
| k_ra8_err_invalid_size | src_size too small. |
| k_ra8_err_not_found | Magic absent – never provisioned. |
| k_ra8_err_not_supported | Schema version newer than this build. |
| k_ra8_err_validation_failed | Payload length wrong for the schema. |
| k_ra8_err_crc_mismatch | CRC trailer does not match the body. |
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().
|
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.
| [in] | cfg | Seams and limits; non-NULL. |
| [in] | vcom_mv | VCOM magnitude to persist, in millivolts. |
| k_ra8_ok | Record committed durably. |
| k_ra8_err_null_ptr | cfg is NULL. |
| k_ra8_err_not_supported | cfg->store.write is not bound. |
| k_ra8_err_range_check_failed | vcom_mv outside cfg->limits. |
| other | Forwarded from cfg->store.write. |
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.
|
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.
| [in] | cfg | Seams, limits and any provisioning value; non-NULL. |
| [out] | out_result | Receives the resolved value and its provenance; non-NULL. |
| k_ra8_ok | A trusted VCOM was resolved. |
| k_ra8_err_null_ptr | cfg or out_result is NULL. |
| k_ra8_err_invalid_arg | cfg->limits is self-inconsistent (zero or inverted window). |
| k_ra8_err_not_found | No source produced an in-range value – refuse to drive the panel. |
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().
|
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.
| [in] | rec | Record to serialise; non-NULL. |
| [out] | dst | Destination buffer; non-NULL. |
| [in] | dst_size | Capacity of dst; must be >= k_ra8_epd_cal_blob_size. |
| k_ra8_ok | dst holds a sealed record. |
| k_ra8_err_null_ptr | rec or dst is NULL. |
| k_ra8_err_invalid_size | dst_size too small. |
| k_ra8_err_invalid_arg | rec->vcom_mv is zero. |
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().
|
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.
| [in] | mv | Candidate VCOM magnitude in millivolts. |
| [in] | limits | Panel's documented window; non-NULL. |
| true | limits->min_mv <= mv <= limits->max_mv. |
| false | mv is outside the window, or limits is NULL. |
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().
|
static |
Logging tag used by every error path in this TU.
Definition at line 39 of file ra8_epd_cal.c.