437 lines
24 KiB
C
437 lines
24 KiB
C
/**
|
||
* @file pwm.h (families/t06211/compact_src)
|
||
* @brief Compact single-header API for the t06211 PWM controller.
|
||
*
|
||
* Mirrors the public shape of the default family's `compact_src/pwm.h`
|
||
* but tailored to t06211's 5-stage pipeline (no standalone max_cl_error
|
||
* lookup, single-submap setpoint, factor+offset target compute).
|
||
*
|
||
* Public API is just two functions:
|
||
* pwm_init() — one-time setup
|
||
* pwm_service() — per-cycle update (pulls inputs via getters,
|
||
* runs the 5-stage pipeline, writes rt outputs)
|
||
*
|
||
* External inputs arrive through a getter vtable; each callback returns
|
||
* one signal in native PWM units.
|
||
*
|
||
* Every arithmetic choice mirrors the MCS-96 assembly; see
|
||
* families/t06211/src/ for per-stage commentary.
|
||
*/
|
||
#ifndef PWM_H
|
||
#define PWM_H
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
#include <string.h>
|
||
|
||
/* ── MCS-96 arithmetic primitives ───────────────────────────────────── */
|
||
#define MUL_S16(a, b) ((int32_t)(int16_t)(a) * (int32_t)(int16_t)(b))
|
||
#define MULU_U16(a, b) ((uint32_t)(uint16_t)(a) * (uint32_t)(uint16_t)(b))
|
||
#define CLAMP(v, lo, hi) ((v) < (lo) ? (lo) : (v) > (hi) ? (hi) : (v))
|
||
|
||
static inline int16_t shra16(int16_t v, unsigned n) {
|
||
if (!n) return v;
|
||
uint16_t u = (uint16_t)v, sign = (u >> 15) & 1u;
|
||
uint16_t m = sign ? (uint16_t)(((uint16_t)~0u) << (16u - n)) : 0u;
|
||
return (int16_t)((u >> n) | m);
|
||
}
|
||
static inline int32_t shra32(int32_t v, unsigned n) {
|
||
if (!n) return v;
|
||
uint32_t u = (uint32_t)v, sign = (u >> 31) & 1u;
|
||
uint32_t m = sign ? (~(uint32_t)0u << (32u - n)) : 0u;
|
||
return (int32_t)((u >> n) | m);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════
|
||
* 1D INTERPOLATION SLOT
|
||
* Layout matches family-1's pwm_interp_slot_t and the family-1 ROM
|
||
* convention [count*2, range, offset, seg_bytes] — t06211's FUN_6fb8
|
||
* fingerprints identically, so the same layout applies.
|
||
* ══════════════════════════════════════════════════════════════════════ */
|
||
typedef struct pwm_interp_slot {
|
||
int16_t row_stride; /* count * 2 (byte stride for Y-table rows) */
|
||
int16_t x_interval; /* x[k-1] - x[k] */
|
||
int16_t x_offset; /* input - x[k] */
|
||
int16_t y_byte_off; /* k * 2 */
|
||
} pwm_interp_slot_t;
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════
|
||
* EXTERNAL INPUTS
|
||
* Populated each cycle by pwm_service via the getter vtable.
|
||
* t06211-specific set: no temperature, no angle_dec_cmd in the setpoint
|
||
* (the family-2 setpoint is single-submap RPM-indexed).
|
||
* ══════════════════════════════════════════════════════════════════════ */
|
||
typedef struct pwm_inputs {
|
||
int16_t ckp_in; /* 0x02f8 — sensed position */
|
||
uint16_t rpm; /* 0x0040 — engine RPM */
|
||
int16_t angle_dec_cmd; /* 0x0042 — accepted for family-1 API parity; unused here */
|
||
int16_t inj_qty_demand; /* 0x0044 — CAN: inj quantity demand (PI large-neg gate) */
|
||
int16_t b_fb_kw; /* CAN: plunger feedback baseline. Gets copied into
|
||
can_raw_b_fb_kw and decoded by s_setpoint_can_decode
|
||
(port of FUN_64c3) to produce target. */
|
||
int16_t cl_gate_input; /* 0x0130 — CAN: open/closed-loop discriminant */
|
||
uint16_t supply_voltage; /* 0x0142 — shape_eval submap input */
|
||
int16_t temperature; /* 0x0146 — accepted for family-1 API parity; unused here */
|
||
} pwm_inputs_t;
|
||
|
||
/** Getter vtable. 8-callback layout matches family-1's pwm_input_getters_t
|
||
* so a single FBKW.c can drive either family's pwm.c without changes.
|
||
* t06211 does not consume angle_dec_cmd or temperature; those getters are
|
||
* called and stored into rt->inputs but the pipeline ignores them. */
|
||
typedef struct pwm_input_getters {
|
||
int16_t (*ckp_in) (void *ctx);
|
||
uint16_t (*rpm) (void *ctx);
|
||
int16_t (*angle_dec_cmd) (void *ctx); /* accepted; unused by t06211 */
|
||
int16_t (*inj_qty_demand)(void *ctx);
|
||
int16_t (*b_fb_kw) (void *ctx);
|
||
int16_t (*cl_gate_input) (void *ctx);
|
||
uint16_t (*supply_voltage)(void *ctx);
|
||
int16_t (*temperature) (void *ctx); /* accepted; unused by t06211 */
|
||
void *ctx;
|
||
} pwm_input_getters_t;
|
||
|
||
typedef struct pwm_calibration pwm_calibration_t;
|
||
typedef struct pwm_submap_descr pwm_submap_descr_t;
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════
|
||
* RUNTIME STATE
|
||
* RAM-address comments reference the t06211 MCS-96 map (see
|
||
* families/t06211/docs/variable-glossary.md).
|
||
* ══════════════════════════════════════════════════════════════════════ */
|
||
typedef struct pwm_runtime {
|
||
/* External inputs — refreshed each cycle from getters */
|
||
pwm_inputs_t inputs;
|
||
|
||
/* Async flags.
|
||
* reset_flag — set on reset edge; supervisor clears.
|
||
* system_flags_110 — bit 5 forces open-loop; bit 0 is fast-recovery latch.
|
||
*/
|
||
uint8_t reset_flag; /* 0x002e */
|
||
uint8_t system_flags_110; /* 0x0110 */
|
||
|
||
/* ── Dual-target setpoint architecture ── */
|
||
/* target (RW5E = RAM[0x005e]): PRIMARY CAN-driven setpoint
|
||
* computed by FUN_64c3 (called from CAN parser FUN_6192). Drives
|
||
* the PI controller in both open-loop (FUN_67c4:67e5) and
|
||
* closed-loop (FUN_67c4:680a) paths, and supervisor error compute
|
||
* (FUN_7beb:7c20). Formula: raw/2 + setpoint_offset + rw42_state,
|
||
* clamped >= cal->target_5e_min_clamp. */
|
||
int16_t target_5e; /* RW5E — primary CAN-decoded setpoint */
|
||
/* pi_high_clamp_ceiling (DAT_0336): SECONDARY RPM-derived ceiling
|
||
* computed by FUN_7168(setpoint_descr) + FUN_77b3:77c7. Used ONLY as
|
||
* upper-clamp in FUN_672b PI compensation (0x67a9/0x67b0) and
|
||
* FUN_7beb supervisor (0x7c30/0x7c37). Does not drive control
|
||
* directly. (Was misnamed `target_336` across earlier ports — the
|
||
* address-suffixed style hid that this is a PI clamp, not a target.) */
|
||
int16_t pi_high_clamp_ceiling;
|
||
/* CAN-staging buffers — written externally before pwm_service so
|
||
* s_setpoint_can_decode can transform them into target. */
|
||
int16_t can_raw_b_fb_kw; /* 0x012c — raw b_fb_kw from CAN */
|
||
int16_t can_aux_12e; /* 0x012e — secondary CAN field (drives rw42_state) */
|
||
int16_t can_half_12a; /* 0x012a — cached raw>>1 (debug visibility) */
|
||
/* setpoint_offset (RAM[0x0150]) is copied at runtime_init from
|
||
* cal.setpoint_offset (mirrors family-1's pattern at
|
||
* compact_src/pwm.c:66). The REAL ROM derives it at boot via
|
||
* FUN_6b7b @ 0x6bb5: RAM[0x150] = cal+0x4c - RW1E. Because RW1E's
|
||
* source at the call site (0x6c46) is in a Ghidra-unrecognised code
|
||
* region, we expose the field directly through cal so the user can
|
||
* set it to the match-fitted value without needing to simulate the
|
||
* full FUN_6b7b boot path. */
|
||
int16_t setpoint_offset; /* runtime copy of cal->setpoint_offset (DAT_0150 in ROM) */
|
||
int16_t rw42_state; /* RW42 register — = can_aux_12e when valid */
|
||
/* DAT_033a — supervisor reset stash: snapshots pi_integ_hi at the
|
||
* reset edge (FUN_7beb:7c05-7c0a). Dead store (no readers anywhere
|
||
* in the ROM); kept here for parity with the disasm. NOT a baseline
|
||
* of b_fb_kw — that name was inherited from the family-1 idiom and
|
||
* is incorrect for this variant. */
|
||
int16_t pi_integ_hi_snapshot;
|
||
/* PI P-term — produced by FUN_672b @ 0x6775 as `(p_gain_normal * err) >> 8`
|
||
* and consumed by the supervisor at FUN_7beb:0x7c26 as the additive
|
||
* shaping term on the error. (DAT_02b8 in ROM; was named
|
||
* `compensation_angle` in earlier ports.) */
|
||
int16_t pi_p_term;
|
||
int16_t angle_error_raw; /* DAT_033e — supervisor output (raw + ceiling-clamped) */
|
||
uint16_t cl_enable_counter; /* DAT_033c */
|
||
|
||
/* ── CL correction ── */
|
||
int16_t cl_correction_raw; /* DAT_0176 */
|
||
int16_t angle_offset; /* DAT_017c */
|
||
int16_t supervisor_state; /* RW17E — CL accumulator */
|
||
int16_t pos_error_normalizer; /* runtime copy of cal->init_pos_error_normalizer (DAT_0332 in ROM) */
|
||
int16_t neg_error_normalizer; /* runtime copy of cal->init_neg_error_normalizer (DAT_0334 in ROM) */
|
||
|
||
/* ── Publish + PI ── */
|
||
int16_t estimated_angle; /* DAT_02cc */
|
||
int16_t angle_error_pi; /* DAT_02be — target_5e − estimated_angle */
|
||
int16_t active_request; /* RW46 — published PI output / PWM feed-forward */
|
||
uint8_t pi_open_loop_flag; /* DAT_02c4 */
|
||
uint8_t pi_shape_flag; /* DAT_02c5 */
|
||
uint8_t pi_flag_c6; /* DAT_02c6 — previous-cycle pi_shape_flag latch */
|
||
uint8_t pi_flag_338; /* DAT_0338 — Block-4 cal-pinned latch */
|
||
|
||
/* P-shape segment bounds (boot-init from cal+0x10A/0x10C). PI Block-4
|
||
* error-window bounds (independent int16s, NOT a 32-bit integrator).
|
||
* Trim bytes at RAM[0x0414]/[0x0416] have no writers in the ROM, so
|
||
* bounds resolve to the cal bases verbatim. See open-questions §2. */
|
||
int16_t p_shape_bound_pos; /* DAT_0450 */
|
||
int16_t p_shape_bound_neg; /* DAT_0452 */
|
||
|
||
int16_t pi_state_118; /* DAT_0118 — recovery counter */
|
||
int16_t pi_state_c2; /* DAT_02c2 — cooldown counter */
|
||
|
||
/* PI integrator pair {hi:lo} at {DAT_02b4 : DAT_02b6}. Disasm
|
||
* `disasm_nav rw 0x02b4` shows only internal writers (FUN_672b,
|
||
* FUN_76aa, FUN_7c85), confirming this is a PI integrator state,
|
||
* not an external/CAN input. (Was misnamed `b_fb_kw` in the original
|
||
* glossary; renamed via the address-suffixed `pi_b4_state`/`pi_b6_state`
|
||
* intermediate; final semantic name aligns with the cross-family pair.) */
|
||
int16_t pi_integ_hi; /* DAT_02b4 — integrator high word */
|
||
int16_t pi_integ_lo; /* DAT_02b6 — integrator low word */
|
||
|
||
/* PI gains — boot-set by FUN_76aa from cal scalars (with optional
|
||
* trim bytes at RAM[0x0410]/[0x0412] which have no writers anywhere
|
||
* in the ROM, so the cal bases pass through unchanged). */
|
||
int16_t p_gain_normal; /* DAT_0454 — boot from cal+0x118; multiplier in pi_p_term = (gain*err)>>8 */
|
||
int16_t integ_step_normal; /* DAT_0456 — boot from cal+0x11A; multiplier in FUN_7c85 integrator step */
|
||
int16_t open_loop_p_gain; /* DAT_0330 — boot from cal+0x11C byte (clamped ≤15); used in `pi_integ_hi = (gain*err)>>4 + ckp` reset branch */
|
||
|
||
/* Anti-windup flag set by FUN_672b clamps; consulted by FUN_7c85
|
||
* to gate integration direction. Values: 0 (in-range), 1 (clamped
|
||
* low), 2 (clamped high). */
|
||
uint8_t pi_flag_c7; /* DAT_02c7 */
|
||
|
||
/* ── PWM output ── */
|
||
uint16_t pwm_duty; /* 0x02d2 */
|
||
uint16_t pwm_on_time; /* 0x02ce */
|
||
uint16_t pwm_off_time; /* 0x02d0 */
|
||
/* pwm_period — total period in Timer2 ticks = on_time + off_time.
|
||
* Mirrored from t06211 RAM[0x02e4] (computed each cycle by the PWM
|
||
* output stage). Exposed for family-1 FBKW.c API parity
|
||
* (UpdatePWM(&htim4, ch, pwm_on_time, pwm_period)). */
|
||
uint16_t pwm_period;
|
||
uint8_t pwm_duty_range_flag; /* 0x00d1 */
|
||
pwm_interp_slot_t pwm_slot_a; /* 0x02d4 */
|
||
pwm_interp_slot_t pwm_slot_b; /* 0x02dc */
|
||
/* pwm_shape_state[6] mirrors RAM[0x02e4..0x02ee]:
|
||
* [0] pwm_period working value (RAM 0x02e4)
|
||
* [1] (unused — was slew_increment, broken out as field below)
|
||
* [2] (RAM 0x02e8 — ROM band-array ptr, unused in C)
|
||
* [3] (RAM 0x02ea — ROM halfwidth ptr, unused in C)
|
||
* [4] (RAM 0x02ec — slew step magnitude, unused; read from cal)
|
||
* [5] shape_height (RAM 0x02ee, E2 output) */
|
||
int16_t pwm_shape_state[6]; /* 0x02e4-0x02ee */
|
||
/* Persistent slew_increment (RAM[0x02e6]). Carried across cycles so
|
||
* the hysteresis-margin HOLD path can preserve previous direction.
|
||
* Subtracted from pwm_period each cycle. See open-questions §5. */
|
||
int16_t pwm_slew_increment; /* 0x02e6 */
|
||
|
||
/* ── Bindings (set once by pwm_init) ── */
|
||
const pwm_calibration_t *bound_cal;
|
||
const pwm_input_getters_t *bound_getters;
|
||
} pwm_runtime_t;
|
||
|
||
/* ── Calibration (decoded ROM values) ───────────────────────────────── */
|
||
struct pwm_calibration {
|
||
/* Scalars (from families/t06211/cal_offsets.py FLASH_OFFSETS) */
|
||
int16_t large_pos_error_thresh; /* cal+0x10E */
|
||
int16_t large_neg_error_thresh; /* cal+0x110 */
|
||
int16_t pi_low_clamp; /* cal+0x120 = -512 — Block-4 low clamp + open-loop lower bound */
|
||
int16_t pi_high_clamp; /* cal+0x124 = +1707 — Block-4 high clamp */
|
||
|
||
/* CAN-decoded setpoint (FUN_64c3) cal constants */
|
||
int16_t b_fb_kw_upper_bound; /* cal+0x004 = +7680 — raw b_fb_kw upper sanity bound */
|
||
int16_t b_fb_kw_lower_bound; /* cal+0x006 = -768 — raw b_fb_kw lower sanity bound */
|
||
|
||
/* setpoint_offset — static bias added after halving raw b_fb_kw.
|
||
* Family-1 analog: CAL+0x0052 - CAL+0x0054 (compact_src/pwm.h:210).
|
||
* For t06211, FUN_6b7b computes the equivalent as cal+0x4c - cal+0x4e
|
||
* (= 3499 - 4156 = -657) at boot and stores at RAM[0x150].
|
||
* Copied into runtime.setpoint_offset by runtime_reset. */
|
||
int16_t setpoint_offset; /* = cal+0x4c - cal+0x4e */
|
||
int16_t target_5e_min_clamp; /* cal+0x122 = -512 — RW5E lower clamp */
|
||
int16_t can_aux_12e_max; /* cal+0x002 — upper bound for RAM[0x12e] (FUN_649e) */
|
||
|
||
/* CKP-zero acquisition (FUN_6b67 chain — caller @ 0x65c7).
|
||
* Slot offsets verified at FUN_6b67:0x6b71/0x6b81/0x6b93 + Stage-1
|
||
* caller @ 0x654f/0x656d. */
|
||
int16_t ckp_zero_anchor; /* cal+0x04E — FUN_6b67 additive anchor */
|
||
int16_t can_dckp_offset_bias; /* cal+0x050 — Stage-1/Stage-2 bias */
|
||
int16_t ckp_modulus; /* cal+0x0A0 — FUN_6b67 modulo wrap (also
|
||
* reused by FUN_6d4a process-tooth
|
||
* below; high byte = ckp_modulus>>8 = 30). */
|
||
|
||
/* CKP process-tooth derivation (FUN_6d4a @ 0x6d4a — analog of T06215
|
||
* FUN_7293 / default FUN_87ea). Same body shape; cal slot for the
|
||
* per-tick advance is at cal+0x124 (NOT cal+0x12C as in T06215),
|
||
* verified as the sole `TABLE[RWA4]` reader at FUN_6d4a:0x6d6a.
|
||
* Consumed by get_ckp_process_tooth() in ckp_acquisition.c.
|
||
*
|
||
* Note: cal+0x124 is **aliased** with `pi_high_clamp` (FUN_67c4
|
||
* reads it via the indirect cal-address idiom at 0x68c3/0x68d9).
|
||
* The value 1707 happens to serve both roles in this ROM. */
|
||
int16_t ckp_advance_per_tick; /* cal+0x124 — angular advance per tick
|
||
* (t06211: 1707 = same value as
|
||
* pi_high_clamp; alias by coincidence). */
|
||
int16_t ckp_seg_wrap_threshold; /* cal+0x09D — tooth-counter test
|
||
* (t06211: 29; if tooth > threshold
|
||
* → tooth -= 30). */
|
||
int16_t ckp_teeth_per_seg; /* cal+0x09E — per-segment clamp
|
||
* (t06211: 26 teeth / 90° segment;
|
||
* if tooth > teeth_per_seg → 0). */
|
||
int16_t error_thresh_114; /* cal+0x114 */
|
||
int16_t pi_thresh_116; /* cal+0x116 */
|
||
/* pi_state_118 saturation threshold — when the recovery counter
|
||
* reaches this, FUN_66a8 latches bit0 of system_flags_110, zeroes
|
||
* the counter, and reloads pi_state_c2 from error_thresh_114.
|
||
* Boot-cached at RAM[0x02c0] by FUN_6b7b:0x6bd4. ROM literal 0x0320 = 800. */
|
||
int16_t pi_sat_count_threshold; /* cal+0x112 */
|
||
int16_t rpm_threshold_11E; /* cal+0x11E — RPM gate inside FUN_66a8 */
|
||
/* Closed-loop entry RPM floor (FUN_67c4:0x67d9). Sourced from
|
||
* RAM[0x605c] (flash mirror; ROM literal 0x01A4 = 420). */
|
||
int16_t pi_cl_rpm_floor;
|
||
|
||
int16_t pwm_detail_x0; /* cal+0x0EE */
|
||
int16_t pwm_detail_x1; /* cal+0x0F0 */
|
||
int16_t pwm_cached_ptr_0F2; /* cal+0x0F2 — first RPM-window breakpoint (legacy scalar) */
|
||
int16_t pwm_cached_ptr_102; /* cal+0x102 — window halfwidth (legacy scalar) */
|
||
int16_t pwm_const_104; /* cal+0x104 */
|
||
|
||
/* RPM-window matching for pwm_period slew (ROM 0x538b-0x5575).
|
||
* Eight breakpoints at cal+0xF2 define four bands (lo,hi); the
|
||
* cal+0x102 halfwidth adds hysteresis. RPM INSIDE any band drives
|
||
* pwm_period toward pwm_period_min (→ non-zero shape contribution
|
||
* adds ~49 duty ticks). RPM OUTSIDE all bands drives pwm_period
|
||
* toward pwm_period_max (→ zero contribution; Y-table floor). */
|
||
int16_t pwm_rpm_windows[8]; /* cal+0xF2 — 4 (lo,hi) pairs */
|
||
int16_t pwm_window_halfwidth; /* cal+0x102 — hysteresis halfwidth */
|
||
/* Per-cycle slew step magnitude for pwm_period. Sourced from
|
||
* cal+0x104 (= 354); cached to RAM[0x02ec] at FUN_5314:0x53b9 and
|
||
* read at the Phase 1 and Phase 3 sites. Aliases pwm_const_104 —
|
||
* same offset, semantic name. */
|
||
int16_t pwm_slew_step; /* cal+0x104 — slew magnitude */
|
||
|
||
const int16_t *pwm_y_table; /* cal+0x154 (indirect) */
|
||
const int16_t *shape_y_table; /* cal+0x15E (indirect) */
|
||
|
||
int16_t closed_loop_gain_const; /* cached at ROM 0x6056 = 0x000A */
|
||
|
||
/* PI runtime-reset values — boot-derived in ROM (FUN_76aa @ 0x76aa);
|
||
* cal-resident here so each variant carries its own values without
|
||
* code edits. Trim bytes at RAM[0x0410-0x0416] have no writers in the
|
||
* ROM (EEPROM trim slots, default 0), so each cal base is used
|
||
* verbatim — see open-questions §2 closeout. Field shape mirrors
|
||
* T06215's pwm_calibration_t for cross-family consistency. */
|
||
int16_t init_p_shape_bound_pos; /* cal+0x10A — copied to rt->p_shape_bound_pos */
|
||
int16_t init_p_shape_bound_neg; /* cal+0x10C — copied to rt->p_shape_bound_neg */
|
||
int16_t init_p_gain_normal; /* cal+0x118 — copied to rt->p_gain_normal */
|
||
int16_t init_integ_step_normal; /* cal+0x11A — copied to rt->integ_step_normal */
|
||
int16_t init_open_loop_p_gain; /* cal+0x11C byte clamped to 15 — copied to rt->open_loop_p_gain */
|
||
int16_t init_pos_error_normalizer;/* cal+0x108 — copied to rt->pos_error_normalizer */
|
||
int16_t init_neg_error_normalizer;/* cal+0x106 — copied to rt->neg_error_normalizer */
|
||
|
||
uint16_t pwm_period_min; /* hypothesised; see cal_tables_rom.c */
|
||
uint16_t pwm_period_max; /* hypothesised */
|
||
|
||
/* PWM duty clamp bounds — RAM[0x6058]/RAM[0x605a] flash mirrors.
|
||
* Defaults 0x00CD/0x0F32 match the default family's pwm_min/pwm_max. */
|
||
uint16_t pwm_min; /* RAM[0x6058] cache */
|
||
uint16_t pwm_max; /* RAM[0x605a] cache */
|
||
};
|
||
|
||
/* ── Submap descriptor ──────────────────────────────────────────────── */
|
||
struct pwm_submap_descr {
|
||
uint16_t flags; /* +0 */
|
||
const int16_t *input_ptr; /* +2 (bound at runtime) */
|
||
uint16_t count; /* +4 */
|
||
const int16_t *x; /* +6 */
|
||
uint16_t input_addr;
|
||
};
|
||
|
||
/** Bind descriptor input_ptr fields to rt inputs. */
|
||
static inline void pwm_bind_submap_inputs(
|
||
pwm_runtime_t *rt,
|
||
pwm_submap_descr_t *descrs,
|
||
uint16_t n)
|
||
{
|
||
for (uint16_t i = 0; i < n; i++) {
|
||
switch (descrs[i].input_addr) {
|
||
case 0x0040: descrs[i].input_ptr = (const int16_t *)&rt->inputs.rpm; break;
|
||
case 0x0044: descrs[i].input_ptr = &rt->inputs.inj_qty_demand; break;
|
||
case 0x0046: descrs[i].input_ptr = &rt->active_request; break;
|
||
case 0x0142: descrs[i].input_ptr = (const int16_t *)&rt->inputs.supply_voltage; break;
|
||
default: descrs[i].input_ptr = NULL; break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════
|
||
* PUBLIC API
|
||
* ══════════════════════════════════════════════════════════════════════ */
|
||
|
||
/** pwm_flash_t — placeholder for family-1 API parity. Family-1 has Y-tables
|
||
* in a separate `pwm_flash_t` struct; t06211 keeps them inside
|
||
* pwm_calibration_t so this struct is empty. Kept so FBKW.c / callers
|
||
* can pass &pwm_flash_rom without the call failing. */
|
||
typedef struct pwm_flash { char _unused; } pwm_flash_t;
|
||
|
||
/** 4-argument init matching family-1's pwm_init signature. The `flash`
|
||
* pointer is accepted (must be non-NULL — pass &pwm_flash_rom) and
|
||
* ignored by the t06211 pipeline. */
|
||
void pwm_init(pwm_runtime_t *rt,
|
||
const pwm_calibration_t *cal,
|
||
const pwm_flash_t *flash,
|
||
const pwm_input_getters_t *getters);
|
||
|
||
void pwm_service(pwm_runtime_t *rt);
|
||
|
||
/** @brief CKP-edge reset hook — call from the CKP interrupt handler.
|
||
*
|
||
* Sets rt->reset_flag = 1 so the next pwm_service() call zeroes the
|
||
* closed-loop accumulator (supervisor_state) and the enable counter
|
||
* (cl_enable_counter). Byte store is atomic on all mainstream targets;
|
||
* no locking required as long as it is not called concurrently with
|
||
* pwm_service().
|
||
*
|
||
* Pattern parity with the default family. See
|
||
* docs/re-guide-ckp-reset-pattern.md for the full rationale.
|
||
*
|
||
* **Variant note for t06211:** the reset_flag producer is **absent from
|
||
* the ROM** (no STB #1 site writing to 0x002e). The supervisor at 0x7beb
|
||
* still consumes reset_flag == 1 and clears it, but nothing inside the
|
||
* ROM ever sets it. Consequence: the accumulator walk-off hazard is
|
||
* structurally identical to family-1, and the application layer must
|
||
* drive this hook itself at engine-rev rate (typically 4 pulses per
|
||
* revolution on VP44, ~12–15 scheduler cycles apart at 1200 rpm with a
|
||
* 1 kHz scheduler). Without it, supervisor_state integrates forever.
|
||
*/
|
||
static inline void pwm_ckp_isr(pwm_runtime_t *rt) { rt->reset_flag = 1; }
|
||
|
||
/** Utility: 1D descending-X piecewise-linear lookup. */
|
||
int16_t pwm_interp_lookup(const int16_t *x, const int16_t *y,
|
||
uint16_t n, int16_t in);
|
||
|
||
/** Utility: bypass-PI bilinear LUT — eval(pwm_A, rpm), eval(pwm_B, fbkw),
|
||
* combine over Y-table, then apply [205, 3890] clamp. Use this to query
|
||
* the ROM Y-table directly as a (rpm, fbkw) lookup, skipping the PI
|
||
* controller entirely. Returns the clamped duty. */
|
||
uint16_t pwm_lut_duty(const pwm_calibration_t *cal,
|
||
uint16_t rpm, int16_t fbkw);
|
||
|
||
/* ── ROM-decoded cal + flash placeholder (defined in cal_tables_rom.c) ── */
|
||
extern const pwm_calibration_t pwm_cal_rom;
|
||
extern const pwm_flash_t pwm_flash_rom;
|
||
|
||
/** Descriptor array indices. */
|
||
enum pwm_submap_id {
|
||
PWM_SUBMAP_SETPOINT_INTERP = 0,
|
||
PWM_SUBMAP_PWM_A = 1,
|
||
PWM_SUBMAP_PWM_B = 2,
|
||
PWM_SUBMAP_SHAPE_EVAL = 3,
|
||
PWM_SUBMAP_COUNT = 4
|
||
};
|
||
|
||
extern pwm_submap_descr_t pwm_submap_descrs[PWM_SUBMAP_COUNT];
|
||
extern const int16_t *pwm_submap_y_of(uint16_t idx);
|
||
|
||
#endif /* PWM_H */
|