#ifndef IMU_CAL_FLASH_H #define IMU_CAL_FLASH_H #include #include /* * IMU mount angle calibration flash storage (Issue #680). * * Sector 7 (128 KB at 0x08060000) layout: * 0x0807FF00 imu_cal_flash_t (64 bytes) ← this module * 0x0807FF40 pid_sched_flash_t (128 bytes) ← pid_flash.c * 0x0807FFC0 pid_flash_t (64 bytes) ← pid_flash.c * * Calibration flow: * 1. Mount robot at its installed angle, power on, let IMU converge (~5s). * 2. Send 'O' via USB CDC (dev-only path). * 3. Firmware captures current pitch + roll as mount offsets, saves to flash. * 4. mpu6000_read() subtracts offsets from output on every subsequent read. * * The sector erase preserves existing PID data by reading it first. */ #define IMU_CAL_FLASH_ADDR 0x0807FF00UL #define IMU_CAL_FLASH_MAGIC 0x534C5403UL /* 'SLT\x03' — version 3 */ typedef struct __attribute__((packed)) { uint32_t magic; /* IMU_CAL_FLASH_MAGIC when valid */ float pitch_offset; /* degrees subtracted from IMU pitch output */ float roll_offset; /* degrees subtracted from IMU roll output */ uint8_t _pad[52]; /* padding to 64 bytes */ } imu_cal_flash_t; /* 64 bytes total */ /* * imu_cal_flash_load() — read saved mount offsets from flash. * Returns true and fills *pitch_offset / *roll_offset if magic is valid. * Returns false if no valid calibration stored (caller keeps 0.0f defaults). */ bool imu_cal_flash_load(float *pitch_offset, float *roll_offset); /* * imu_cal_flash_save() — erase sector 7 and write all three records atomically: * imu_cal_flash_t at 0x0807FF00 * pid_sched_flash_t at 0x0807FF40 (preserved from existing flash) * pid_flash_t at 0x0807FFC0 (preserved from existing flash) * Must be called while disarmed — sector erase stalls CPU ~1s. * Returns true on success. */ bool imu_cal_flash_save(float pitch_offset, float roll_offset); #endif /* IMU_CAL_FLASH_H */