ACwing FC firmware
STM32WB55: attitude IEKF, altitude Kalman filter, flight core, protocol v2
Loading...
Searching...
No Matches
app_main.c
Go to the documentation of this file.
1
30#include "app_main.h"
31#include "app_config.h"
32#include "cmsis_os2.h"
33#include "imu_i2c.h"
34#include "imu_lsm6dsv16x.h"
35#include "est_pipeline.h"
36#include "est_config.h"
37#include "control_hooks.h"
38#include "motors.h"
39#include "telemetry.h"
40#include "tlm_uart.h"
41#include "tlm_ble.h"
42#include "so3.h"
43#include "tlm_build.h"
44#include "fc_core.h"
45#include "fc_shared.h"
46#include "app_task.h"
47#include "alt_est.h"
48#include "ext_sens.h"
49#include "proto_v2.h"
50#include "batt_task.h"
51#include "batt_adc.h"
52#include "log_task.h"
53#include "log_feed.h"
54#include "log_queue.h"
55#include "log_flash_ram.h"
56#include "usb_cdc.h"
57#include "usb_link.h"
58#include <string.h>
59#include <math.h>
60
61extern I2C_HandleTypeDef hi2c1; /* PB8 SCL (D15) / PB9 SDA (D14) */
62extern UART_HandleTypeDef huart1; /* PB6/PB7 -> ST-LINK virtual COM port */
63
64/* PA15 = D5 = LSM6DSV16X INT1 on the IKS4A1. CubeMX label "IMU_INT1". */
65#ifndef IMU_INT1_Pin
66#error "In CubeMX, assign the label IMU_INT1 to pin PA15 (GPIO_EXTI15)"
67#endif
68
69#define FLAG_IMU_IRQ 0x0001u
70
71/* ---- watchdog: LSI 32 kHz / 32 = 1 kHz, reload 500 -> 0.5 s ----
72 * Fed ONLY by the ImuTask (the control loop). A stalled BLE stack, CPU2,
73 * AppTask or flash write cannot keep it alive; a stalled control loop resets
74 * the MCU (motors off at reset). Frozen while the debugger halts the core. */
75#define APP_IWDG_RELOAD 500u
76#define FC_SERVICE_MS 4u /* flight core ~250 Hz */
77
78/* ------------------------------------------------------------------ */
79static osThreadId_t s_imuTask;
80static osMessageQueueId_t qTlm;
81static volatile uint32_t s_irqCyc;
82static volatile uint32_t s_irqCount, s_tlmDrop, s_busErr, s_fifoOvr;
83/* Bring-up diagnostics (Live Expressions in the debugger) */
84static volatile uint32_t s_procCycMax, s_procCycLast; /* CPU cycles of parse+est_process */
85static volatile uint32_t s_wordsMax;
86static volatile uint32_t s_drainReads; /* immediate re-reads (INT1 edge recovery) */
87static volatile float s_odrHz; /* IMU sample rate measured against the MCU clock */
88static volatile int8_t s_imuInit = 1; /* 1 = in progress, 0 = OK, <0 error */
89static imu_parse_stats_t s_psTot; /* cumulative FIFO counters */
90
91static imu_t s_imu;
92static est_t s_est;
95
96typedef struct {
97 uint16_t len;
98 uint8_t bytes[TLM_FRAME_MAX];
99} tlm_msg_t;
100
102volatile uint16_t g_initLen;
103
104/* ---- calibration in effect (ImuTask copy, applied at event level) ---- */
106static est_config_t s_ec; /* estimator config (restart after a calibration) */
107static volatile float g_level_trim_deg[2]; /* LEVEL: roll, pitch trim for the attitude loop */
108static volatile uint8_t s_est_restart;
109
110/* ---- vertical estimate, flow, statistics ---- */
112static uint8_t s_flow_q = 0xFFu;
113static float s_flow_v[2];
114static uint32_t s_flow_ms; /* last frame from the bridge */
115static uint8_t s_flow_ok; /* bridge alive and PMW3901 OK */
116static uint8_t s_flow_usable; /* above the PMW3901 minimum height */
117static uint16_t s_tof_mm = 0xFFFFu;
118static uint32_t s_tof_ms;
119static float s_vib2; /* EMA of (|a| - 1 g)^2, g^2 */
120static uint16_t s_acc_clip;
122static uint32_t s_cpu_cyc;
123static float s_baro_temp;
124static uint16_t s_imu_sensors;
125static volatile uint32_t s_lastGyroMs; /* last FIFO read with gyro samples */
126#define IMU_STALE_MS 50u /* no gyro for 50 ms: estimate not usable */
127
128/* ------------------------------------------------------------------ */
129static void dwt_init(void)
130{
131 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
132 DWT->CYCCNT = 0;
133 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
134}
135
136void app_exti(uint16_t pin)
137{
138 if (pin != IMU_INT1_Pin) return;
139 s_irqCyc = DWT->CYCCNT;
140 s_irqCount++;
141 if (s_imuTask) (void)osThreadFlagsSet(s_imuTask, FLAG_IMU_IRQ);
142}
143
144void app_i2c_rx_cplt(I2C_HandleTypeDef *h) { imu_i2c_rx_cplt_isr(h); }
145void app_i2c_error(I2C_HandleTypeDef *h) { imu_i2c_error_isr(h); }
146void app_uart_tx_cplt(UART_HandleTypeDef *h) { tlm_uart_tx_cplt_isr(h); }
147
148/* ---------------- pipeline callbacks (ImuTask context) ---------------- */
149static tlm_msg_t s_msg; /* built here, copied into the queue */
150
151static void push_frame(size_t len)
152{
153 if (!len) return;
154 s_msg.len = (uint16_t)len;
155 if (osMessageQueuePut(qTlm, &s_msg, 0, 0) != osOK) s_tlmDrop++;
156}
157
158static void on_state(const est_t *e, void *user)
159{
160 (void)user;
161 static uint32_t n;
162 {
163 float q[4];
164 iekf_get_quat(&e->f, q);
165 const float hwin = (float)APP_STEPS_PER_ACC / (float)APP_STEP_HZ;
166 alt_est_predict(&s_alt, alt_est_accel_up(q, e->acc_mean, e->f.nom_acc), hwin);
167 const float am = sqrtf(e->acc_mean[0]*e->acc_mean[0] + e->acc_mean[1]*e->acc_mean[1] +
168 e->acc_mean[2]*e->acc_mean[2]);
169 const float d = am - (e->f.nom_acc > 0.5f ? e->f.nom_acc : 1.0f);
170 s_vib2 += 0.004f * (d*d - s_vib2); /* ~1 s at 240 Hz */
171
172 /* raw samples for a calibration in progress (~48 Hz) */
173 static uint8_t k;
174 if (g_fsh_cal_want && (++k % 5u) == 0u) {
175 cal_sample_t c;
176 for (int i = 0; i < 3; ++i) {
177 const float sc = (s_calset.accel.scale[i] > 0.0f) ? s_calset.accel.scale[i] : 1.0f;
178 c.acc[i] = e->acc_mean[i] / sc + s_calset.accel.off[i];
179 c.gyro[i] = e->gyro_mean[i] + s_calset.gyro.bias[i];
180 const float ms = (s_calset.compass.scale[i] > 0.0f) ? s_calset.compass.scale[i] : 1.0f;
181 c.mag[i] = e->mag[i] / ms + s_calset.compass.hi[i];
182 }
183 memcpy(c.q, q, sizeof q);
184 c.mag_new = (uint8_t)((e->flags & EST_F_MAG_NEW) != 0u);
185 c.running = e->running;
186 (void)fsh_cal_push(&c);
187 }
188 }
189#if APP_TLM_REPLAY
190 const uint32_t dec = APP_TLM_DECIM_REPLAY;
191#else
192 const uint32_t dec = APP_TLM_DECIMATION;
193#endif
194 if ((n++ % dec) == 0)
196#if APP_TLM_REPLAY
198#endif
199}
200
201static void on_init(const est_t *e, void *user)
202{
203 (void)user;
204 g_initLen = (uint16_t)tlm_build_init(e, (float)APP_STEP_HZ / (float)APP_STEPS_PER_ACC,
205 g_initFrame, sizeof g_initFrame);
206 /* queued immediately: it must precede the first REPLAY */
209}
210
211/* ================================================================== */
212static void iwdg_start(void)
213{
214 DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_IWDG_STOP; /* halted by the debugger: no reset */
215 IWDG->KR = 0xCCCCu; /* start (LSI forced on) */
216 IWDG->KR = 0x5555u; /* unlock PR / RLR */
217 IWDG->PR = 3u; /* /32 -> 1 kHz */
218 IWDG->RLR = APP_IWDG_RELOAD;
219 uint32_t t = 0;
220 while (IWDG->SR != 0u && ++t < 100000u) { } /* registers updated */
221 IWDG->KR = 0xAAAAu;
222}
223
224/* Calibration in effect, applied to the raw events before the estimator:
225 * gyro bias, accelerometer offset/scale (the compass is applied by the
226 * estimator: hard iron + scale). Also counts accelerometer clipping. */
227static void correct_events(imu_event_t *ev, size_t n)
228{
229 for (size_t j = 0; j < n; ++j) {
230 float *v = ev[j].v;
231 switch (ev[j].type) {
232 case IMU_EV_GYRO:
233 for (int i = 0; i < 3; ++i) v[i] -= s_calset.gyro.bias[i];
234 break;
235 case IMU_EV_ACC:
236 if (fabsf(v[0]) > 15.9f || fabsf(v[1]) > 15.9f || fabsf(v[2]) > 15.9f) {
237 if (s_acc_clip < 0xFFFFu) s_acc_clip++;
238 }
239 for (int i = 0; i < 3; ++i) {
240 const float sc = (s_calset.accel.scale[i] > 0.0f) ? s_calset.accel.scale[i] : 1.0f;
241 v[i] = sc * (v[i] - s_calset.accel.off[i]);
242 }
243 break;
244 case IMU_EV_BARO:
245 alt_est_baro(&s_alt, v[0]);
246 s_baro_temp = v[1];
247 break;
248 default:
249 break;
250 }
251 }
252}
253
254/* A calibration from the AppTask (DONE or RESTORE): takes effect here. */
255static void take_calibrations(void)
256{
257 fsh_apply_t a;
258 while (fsh_apply_take(&a)) {
259 switch (a.type) {
260 case PV2_CAL_GYRO: {
261 /* the corrected gyro changes by -delta: move the IEKF bias with it */
262 for (int i = 0; i < 3; ++i) s_est.f.b[i] -= a.set.gyro.bias[i] - s_calset.gyro.bias[i];
263 s_calset.gyro = a.set.gyro;
264 break;
265 }
266 case PV2_CAL_ACCEL:
268 s_est_restart = 1u; /* gravity reference changes */
269 break;
270 case PV2_CAL_COMPASS:
272 memcpy(s_ec.mag_hard_iron, a.set.compass.hi, sizeof s_ec.mag_hard_iron);
273 memcpy(s_ec.mag_scale, a.set.compass.scale, sizeof s_ec.mag_scale);
274 s_est_restart = 1u; /* magnetic reference changes */
275 break;
276 case PV2_CAL_LEVEL:
280 break;
281 default:
283 break;
284 }
285 }
286 if (s_est_restart) {
287 /* only reachable while disarmed (calibrations are refused when armed) */
288 s_est_restart = 0u;
289 est_init(&s_est, &s_ec, on_state, on_init, NULL);
290 s_est.trace_enable = APP_TLM_REPLAY;
291 }
292}
293
294/* ToF and flow samples from the ExtTask */
295static void take_ext(uint32_t now, float cos_tilt, const float omega[3])
296{
297 ext_sample_t x;
298 while (ext_take(&x)) {
299 if (x.kind == EXT_TOF) {
300 alt_est_tof(&s_alt, (float)x.tof_mm * 1e-3f, (uint8_t)(x.status == 0u), cos_tilt);
301 s_tof_mm = (x.status == 0u) ? x.tof_mm : 0xFFFFu;
302 s_tof_ms = now;
303 } else if (x.kind == EXT_FLOW) {
304 s_flow_ms = now;
305 s_flow_ok = (uint8_t)((x.status & 0x01u) != 0u);
306 uint32_t q = (uint32_t)x.squal * 100u / 120u; /* SQUAL ~120 on a good floor */
307 if (!s_flow_ok) q = 0u;
308 s_flow_q = (uint8_t)(q > 100u ? 100u : q);
309 /* height gate: the PMW3901 works from 80 mm; the ToF gives the height
310 * above the floor (no valid ToF = no height = flow not usable) */
312 else if (s_alt.tof_h >= APP_FLOW_MIN_H + 0.02f) s_flow_usable = s_flow_ok;
313 if (!s_flow_usable) { s_flow_v[0] = s_flow_v[1] = 0.0f; continue; }
314 if (x.dt_s <= 1e-3f) continue; /* status only */
315 /* PMW3901: ~0.0205 rad per count (42 deg / 35 px). Flow rate minus the
316 * body rotation, times the height, is the ground speed. Axis signs
317 * (APP_FLOW_SIGN_*) follow the frame-C2 mounting and are checked by
318 * the FLOW_TOF calibration (match %). */
319 const float k = 0.0205f / x.dt_s;
320 const float fx = APP_FLOW_SIGN_X * (float)x.dy * k; /* about body y -> x motion */
321 const float fy = APP_FLOW_SIGN_Y * (float)x.dx * k;
322 const float h = s_alt.tof_h;
323 float v[2] = { (fx - omega[1]) * h, (fy + omega[0]) * h };
324 for (int i = 0; i < 2; ++i) s_flow_v[i] += 0.3f * (v[i] - s_flow_v[i]);
325 }
326 }
327 if (s_flow_ms && (now - s_flow_ms) > 200u) { /* bridge silent */
328 s_flow_q = 0u; s_flow_ok = 0u; s_flow_usable = 0u;
329 s_flow_v[0] = s_flow_v[1] = 0.0f;
330 }
331 if (!(ext_sensors() & PV2_S_FLOW)) { s_flow_q = 0xFFu; s_flow_ok = 0u; s_flow_usable = 0u; }
332 if (s_tof_ms && (now - s_tof_ms) > 200u) s_tof_mm = 0xFFFFu;
333}
334
335static void publish_est(uint32_t now)
336{
337 static uint32_t last, lastWin, winN;
338 static uint32_t lastIrq;
339 if ((now - last) < 20u) return; /* 50 Hz */
340 last = now;
341
342 /* loop rate, jitter, CPU over 1 s windows */
343 if ((now - lastWin) >= 1000u) {
344 const uint32_t dtw = now - lastWin;
345 const uint32_t irqs = s_irqCount - lastIrq;
346 s_loop_hz = (float)(s_est.win_count - winN) * 1000.0f / (float)dtw;
347 s_cpu_pct = (float)s_cpu_cyc * 100.0f / ((float)SystemCoreClock * (float)dtw * 1e-3f);
348 (void)irqs;
349 winN = s_est.win_count; lastIrq = s_irqCount; lastWin = now; s_cpu_cyc = 0;
350 }
351
352 fsh_est_t o;
353 memset(&o, 0, sizeof o);
354 o.imu_ok = (uint8_t)(s_imuInit == 0);
355 o.running = s_est.running;
356 o.acc_gated = (uint8_t)(s_est.f.acc_infl > 2.0f);
357 o.mag_gated = (uint8_t)(s_est.f.mag_infl > 2.0f);
358 iekf_get_quat(&s_est.f, o.q);
359 {
360 const float *P = s_est.f.P;
361 float m = P[0];
362 if (P[IEKF_NX + 1] > m) m = P[IEKF_NX + 1];
363 if (P[2 * IEKF_NX + 2] > m) m = P[2 * IEKF_NX + 2];
364 o.att_unc_deg = s_est.running ? sqrtf(m > 0.0f ? m : 0.0f) * 57.29578f : 180.0f;
365 }
366 o.mag_interf_pct = fabsf(s_est.f.last_mag_dev) * 100.0f;
367 o.vib_mg = sqrtf(s_vib2) * 1000.0f;
369 o.loop_hz = s_loop_hz;
371 o.cpu_pct = s_cpu_pct;
372 o.imu_temp_c = s_baro_temp; /* the board temperature next to the IMU */
376 o.h = alt_est_h(&s_alt);
377 o.vz = alt_est_vz(&s_alt);
378 o.tof_h = s_alt.tof_h;
379 o.tof_mm = s_tof_mm;
380 o.flow_q = s_flow_q;
381 o.flow_ok = s_flow_ok;
383 o.vel_body[0] = s_flow_v[0]; o.vel_body[1] = s_flow_v[1];
384 o.baro_pa = s_alt.baro_ok ? (uint32_t)(s_alt.pressure + 0.5f) : 0u;
386 fsh_est_write(&o);
387}
388
389/* flight log: 50 Hz attitude and vertical, 10 Hz flight state and estimator
390 * (records are queued only while a log is open, see log_feed.h) */
391static void log_records(uint32_t now, const float q[4], const fc_vert_t *v, const fc_setpoint_t *sp)
392{
393 static uint32_t last50, last10;
394 if (!log_q_active()) return;
395 if ((now - last50) >= 20u) {
396 last50 = now;
397 logf_att(now, q, s_est.omega, s_est.acc_mean);
398 logf_vert(now, v, sp, s_tof_mm, s_alt.baro_ok ? (uint32_t)(s_alt.pressure + 0.5f) : 0u);
399 }
400 if ((now - last10) >= 100u) {
401 last10 = now;
402 fc_state_t f;
403 memset(&f, 0, sizeof f);
404 fc_snapshot(&f);
405 logf_flight(now, &f);
406 const float *P = s_est.f.P;
407 float m = P[0];
408 if (P[IEKF_NX + 1] > m) m = P[IEKF_NX + 1];
409 if (P[2 * IEKF_NX + 2] > m) m = P[2 * IEKF_NX + 2];
410 logf_est(now, s_est.f.b, sqrtf(m > 0.0f ? m : 0.0f) * 57.29578f, sqrtf(s_vib2) * 1000.0f,
411 s_loop_hz, s_jitter_us, s_cpu_pct, fabsf(s_est.f.last_mag_dev) * 100.0f);
412 }
413}
414
415/* flight core + vertical sources, ~250 Hz from the ImuTask loop */
416static void flight_service(uint32_t *lastSvc)
417{
418 const uint32_t now = osKernelGetTickCount();
419 if ((now - *lastSvc) < FC_SERVICE_MS) return;
420 *lastSvc = now;
421
423
424 float q[4];
425 iekf_get_quat(&s_est.f, q);
426 const float ct = 1.0f - 2.0f * (q[1]*q[1] + q[2]*q[2]); /* R33 */
427 take_ext(now, ct, s_est.omega);
428
429 /* a stalled IMU (bus error, sensor reset) makes the estimate unusable at once */
430 const uint8_t est_ok = (uint8_t)(s_est.running && (now - s_lastGyroMs) < IMU_STALE_MS);
431 fc_vert_t v;
432 v.valid = s_alt.valid && est_ok;
433 v.h = alt_est_h(&s_alt);
434 v.vz = alt_est_vz(&s_alt);
437 v.tof_h = s_alt.tof_h;
438 v.cos_tilt = ct;
439 v.flow_q = s_flow_q;
441 fc_service(now, est_ok, &v);
442 {
443 static uint8_t wasArmed;
445 fc_setpoint(&sp);
446 if (sp.armed && !wasArmed) s_acc_clip = 0u; /* SYSTEM.accClip counts since arm */
447 wasArmed = sp.armed;
448 /* sp goes to the attitude / rate loops and the mixer in phase 3 */
449 log_records(now, q, &v, &sp);
450 }
451 publish_est(now);
452}
453
454/* ================================================================== */
455static void ImuTask(void *arg)
456{
457 (void)arg;
458 sensor_bus_t bus;
460
461 const imu_config_t icfg = {
462 .odr = APP_IMU_ODR, .watermark_words = APP_IMU_WATERMARK,
463 .gy_lpf1_bw = APP_GY_LPF1_BW, .xl_lpf2_enable = APP_XL_LPF2_ENABLE,
464 .xl_lpf2_bw = APP_XL_LPF2_BW, .mag_enable = 1, .mag_fast = APP_MAG_FAST,
465 .baro_enable = 1
466 };
467 for (;;) {
468 const int32_t st = imu_init(&s_imu, &bus, &icfg);
469 s_imuInit = (int8_t)st; /* visible in the STATUS packet */
470 if (st == IMU_OK) break;
471#ifdef LED1_GPIO_Port
472 HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
473#endif
474 /* no estimate: the core cannot arm, but it answers commands */
475 fc_service(osKernelGetTickCount(), 0u, NULL);
476 osDelay(200);
477 }
478 s_imu_sensors = (uint16_t)(PV2_S_IMU | (s_imu.mag_enabled ? PV2_S_MAG_FC : 0u) |
479 (s_imu.baro_enabled ? PV2_S_BARO : 0u));
480 {
481 alt_est_cfg_t ac;
483 alt_est_init(&s_alt, &ac);
484 }
485
486 est_config_t ec;
487 app_est_config(&ec); /* app_config.h values (est_config.c) */
488 static const float M[9] = MAG_AXIS_MAP;
489 memcpy(ec.mag_map, M, sizeof M);
490 /* saved calibrations (posted by app_task_boot) */
491 {
492 fsh_apply_t a;
493 while (fsh_apply_take(&a)) cal_copy_type(&s_calset, &a.set, a.type);
496 }
497 memcpy(ec.mag_hard_iron, s_calset.compass.hi, sizeof ec.mag_hard_iron);
498 memcpy(ec.mag_scale, s_calset.compass.scale, sizeof ec.mag_scale);
499 s_ec = ec;
500 est_init(&s_est, &ec, on_state, on_init, NULL);
501 s_est.trace_enable = APP_TLM_REPLAY;
502 ctrl_init();
503
504 /* Sample period of the IMU, measured against the MCU clock (HSE crystal).
505 * The LSM6DSV16X runs on its own RC oscillator, a few % off the nominal ODR
506 * (916 Hz measured on the board instead of 960): integrating the gyro with
507 * the nominal dt scales every rotation by that error. The period is the
508 * DWT time between two INT1 edges >= 2 s apart divided by the gyro samples
509 * read in between (counted from the FIFO tags), smoothed; until the first
510 * window the nominal value is used (static calibration, board still).
511 * A per-read measurement is not used: drain reads have no edge of their own
512 * and fell back to the nominal dt (filter time 1.3 % behind real time). */
513 const float dtNom = 1.0f / s_imu.odr_hz;
514 const float cyc2s = 1.0f / (float)SystemCoreClock;
515 const uint32_t winCyc = 2u * SystemCoreClock; /* 2 s, < 2^32 cycles */
516 float dtEst = dtNom;
517 uint32_t refCyc = 0, refN = 0, lastCyc = 0;
518 uint8_t haveRef = 0, haveEst = 0;
520
521 /* INT1 is a LEVEL ("FIFO level >= watermark") while the EXTI fires on its
522 * RISING EDGE. If one read falls behind (start-up of the BLE stack, a bus
523 * retry, ...), the FIFO stays above the watermark after the read, INT1 never
524 * goes low again and no further edge arrives: the task would live on the
525 * 5 ms timeout, read big chunks, hit the DMA timeout and lose samples
526 * (seen with BLE: 0 IRQ/s, ~39 % of the gyro samples, ~50 bus errors/s).
527 * Hence: after a read that found much more than the watermark, or after a
528 * wake-up by timeout, read again at once; the FIFO drops below the
529 * watermark, INT1 goes low and the next sample produces an edge again. */
530 iwdg_start();
531 uint32_t wait_ms = 5u;
532 uint32_t lastSvc = osKernelGetTickCount();
533 for (;;) {
534 IWDG->KR = 0xAAAAu; /* feed: the control loop is alive */
535 flight_service(&lastSvc);
536 const uint32_t fl = osThreadFlagsWait(FLAG_IMU_IRQ, osFlagsWaitAny, wait_ms);
537 const int by_timeout = (fl & osFlagsError) != 0u;
538 const uint32_t cyc = s_irqCyc;
539
540 uint16_t words = 0;
541 if (imu_fifo_level(&s_imu, &words) != IMU_OK) { s_busErr++; wait_ms = 5u; continue; }
542 if (words == 0) { wait_ms = 5u; continue; }
543 if (words > IMU_FIFO_MAX_WORDS) { words = IMU_FIFO_MAX_WORDS; s_fifoOvr++; }
544
545 /* timeout only for a stuck bus: 48 words = 336 bytes take ~8 ms at 400 kHz */
547 (uint16_t)(words * IMU_FIFO_WORD_BYTES), 12) != 0) { s_busErr++; wait_ms = 1u; continue; }
548 if (words >= 2u * APP_IMU_WATERMARK || (by_timeout && wait_ms != 0u)) {
549 wait_ms = 0u; /* drain now: let INT1 fall */
550 s_drainReads++;
551 } else {
552 wait_ms = 5u;
553 }
554
555 imu_parse_stats_t ps = { 0 };
556 const size_t n = imu_fifo_parse(&s_imu, s_fifo, words, s_ev, IMU_FIFO_MAX_WORDS, &ps);
558 if (ps.gyro) s_lastGyroMs = osKernelGetTickCount();
559 s_psTot.gyro += ps.gyro; s_psTot.acc += ps.acc; s_psTot.mag += ps.mag;
561
562 /* sample period estimate (see above) */
563 const uint8_t fresh = (cyc != lastCyc); /* a new INT1 edge since the last read */
564 lastCyc = cyc;
565 if (haveRef) refN += ps.gyro; /* gyro samples read after the reference */
566 if (fresh) {
567 /* INT1 period jitter (SYSTEM.jitter): edges one watermark apart */
568 static uint32_t prevEdge;
569 const float expP = dtEst * (float)(APP_IMU_WATERMARK / 2u);
570 const float per = (float)(uint32_t)(cyc - prevEdge) * cyc2s;
571 if (prevEdge && per < 1.5f * expP)
572 s_jitter_us += 0.05f * (fabsf(per - expP) * 1e6f - s_jitter_us);
573 prevEdge = cyc;
574 if (!haveRef) {
575 haveRef = 1; refCyc = cyc; refN = 0;
576 } else if ((uint32_t)(cyc - refCyc) >= winCyc && refN > 0u) {
577 const float meas = (float)(uint32_t)(cyc - refCyc) * cyc2s / (float)refN;
578 if (meas > 0.8f * dtNom && meas < 1.25f * dtNom) {
579 dtEst = haveEst ? dtEst + 0.2f * (meas - dtEst) : meas;
580 haveEst = 1;
581 s_odrHz = 1.0f / dtEst;
582 }
583 refCyc = cyc; refN = 0;
584 }
585 }
586 const float dt = dtEst;
587
588 const uint32_t c0 = DWT->CYCCNT;
589 est_process(&s_est, s_ev, n, dt);
590 s_procCycLast = DWT->CYCCNT - c0;
593 if (words > s_wordsMax) s_wordsMax = words;
594 }
595}
596
597/* ================================================================== */
598/* One frame to every link: UART always, USB when a program has the port open,
599 * BLE with its own selection */
600static void tx_frame(const uint8_t *f, uint16_t len)
601{
603#if APP_USB_CDC && APP_USB_TLM
604 if (usbl_open(osKernelGetTickCount())) (void)usbl_put_frame(USBL_SRC_TLM, f, len);
605#endif
606#if APP_TLM_BLE
607 if (len < TLM_OVERHEAD) return;
608 switch (f[2]) {
609 case TLM_TYPE_STATE: {
610 const uint16_t seq = (uint16_t)(f[4] | ((uint16_t)f[5] << 8)); /* tlm_state_t.seq */
611 if (tlm_ble_want_state(seq)) (void)tlm_ble_push(f, len);
612 break;
613 }
614 case TLM_TYPE_REPLAY:
615 break; /* ~44 kB/s: USB only */
616 default: /* INIT, STATUS */
617 (void)tlm_ble_push(f, len);
618 break;
619 }
620#endif
621}
622
623static void send_status(uint8_t *frame, size_t cap)
624{
626 st.uptime_ms = osKernelGetTickCount();
627 st.irq_count = s_irqCount;
628 st.bus_err = s_busErr;
629 st.fifo_ovr = s_fifoOvr;
630 st.tlm_drop = s_tlmDrop;
631 st.uart_drop = tlm_uart_dropped();
632 st.proc_cyc_max = s_procCycMax;
633 st.n_gyro = s_psTot.gyro; st.n_acc = s_psTot.acc; st.n_mag = s_psTot.mag;
634 st.n_mag_dup = s_psTot.mag_dup; st.n_sh_nack = s_psTot.sh_nack;
635 st.words_max = (uint16_t)s_wordsMax;
636 st.calib_count = (uint16_t)s_est.cal.n;
637 st.imu_init = s_imuInit;
638 st.running = s_est.running;
639 st.ble_drop = tlm_ble_dropped();
640 st.ble_mtu = tlm_ble_mtu();
641 st.ble_state = (uint8_t)tlm_ble_state();
642 st.ble_err = tlm_ble_error();
643 const size_t len = tlm_encode(TLM_TYPE_STATUS, &st, sizeof st, frame, cap);
644 if (len) tx_frame(frame, (uint16_t)len);
645}
646
647static void TelemetryTask(void *arg)
648{
649 (void)arg;
650 static tlm_msg_t msg;
651 uint8_t frame[128];
652 uint32_t n = 0;
653 (void)tlm_uart_init(&huart1);
654
655 uint32_t nextStatus = osKernelGetTickCount();
656 for (;;) {
657 /* STATUS at 10 Hz and LED heartbeat: independent of IMU and filter */
658 const uint32_t now = osKernelGetTickCount();
659 if ((int32_t)(now - nextStatus) >= 0) {
660 nextStatus += 100u;
661 send_status(frame, sizeof frame);
662#ifdef LED2_GPIO_Port
663 if ((now / 100u) % 5u == 0u) HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
664#endif
665 }
666
667#if APP_TLM_BLE
668 /* a BLE client has just subscribed (or asked): INIT right away */
671#endif
672 if (osMessageQueueGet(qTlm, &msg, NULL, 10) != osOK) continue;
673 tx_frame(msg.bytes, msg.len);
674 /* INIT repeated ~once per second for late listeners */
675 if (g_initLen && (++n % APP_INIT_EVERY_N) == 0)
677 }
678}
679
680/* ================================================================== */
681static size_t get_init(uint8_t *dst, size_t cap)
682{
683 const uint16_t n = g_initLen;
684 if (!n || n > cap) return 0;
685 memcpy(dst, g_initFrame, n);
686 return n;
687}
688
689static void motor_test_hook(uint8_t m, float duty) { motors_test(m, duty); }
690
691/* log flash of this board (APP_LOG_FLASH) */
692static const log_flash_t *log_flash(void)
693{
694#if APP_LOG_FLASH == 1
695 static uint8_t ram[APP_LOG_RAM_KB * 1024u];
696 static log_flash_t f;
697 static uint8_t init;
698 if (!init) { log_flash_ram_init(&f, ram, sizeof ram); init = 1u; }
699 return &f;
700#elif APP_LOG_FLASH == 2
701 /* W25Q128JV on SPI1 (shared with the IMU): bound with the rev A port (bsp/) */
702 return NULL;
703#else
704 return NULL;
705#endif
706}
707
708static uint16_t log_sensors(void)
709{
710 fsh_est_t e;
711 memset(&e, 0, sizeof e);
712 fsh_est_read(&e);
713 return (uint16_t)(e.sensors | ext_sensors());
714}
715
716void app_start(void)
717{
718 dwt_init();
719 motors_init(); /* disarmed, MOTORS_ENABLE = 0 in phase 1 */
720 qTlm = osMessageQueueNew(16, sizeof(tlm_msg_t), NULL);
722
723 /* flash image: parameters, name, calibrations (posted to the ImuTask) */
724 const cal_set_t *saved;
725 {
726 static const float H[3] = MAG_HARD_IRON;
727 saved = app_task_boot(H);
728 }
729 const uint8_t batt = (uint8_t)(APP_BATT_ADC && batt_adc_init() == 0);
730 {
731 const fc_config_t fc = { .sensors = 0u, .n_motors = 4u, .motors_built = MOTORS_ENABLE,
732 .motors_arm = motors_arm, .motor_test = motor_test_hook,
733 .on_event = logf_event, .batt_monitor = batt,
734#if APP_USB_CDC
735 .usb_host = usbl_host,
736#endif
737 };
738 fc_init(&fc);
739 }
740 logf_set_clock(osKernelGetTickCount);
741
742 static const osThreadAttr_t aI = { .name = "imu", .priority = osPriorityRealtime,
743 .stack_size = 3072 };
744 static const osThreadAttr_t aT = { .name = "tlm", .priority = osPriorityBelowNormal,
745 .stack_size = 1024 };
746 s_imuTask = osThreadNew(ImuTask, NULL, &aI);
747 (void)osThreadNew(TelemetryTask, NULL, &aT);
748 {
750 memset(&ac, 0, sizeof ac);
751 ac.hw_rev = APP_HW_REV;
752 ac.n_motors = 4u;
754 ac.uid[0] = HAL_GetUIDw0(); ac.uid[1] = HAL_GetUIDw1(); ac.uid[2] = HAL_GetUIDw2();
755 ac.get_init = get_init;
756 ac.batt_monitor = batt;
757 ac.board_sensors = (uint16_t)((batt && APP_BATT_CURRENT ? PV2_S_CURRENT : 0u) |
758 (log_flash() ? PV2_S_FLASH_LOG : 0u));
760 app_task_start(&ac);
761 }
763 if (batt) {
765 memset(&bc, 0, sizeof bc);
766 bc.batt.scale_v = saved->battery.scale_v;
767 bc.batt.scale_i = saved->battery.scale_i;
771 batt_task_start(&bc);
772 }
773 {
774#if APP_USB_CDC
775 const uint8_t usb = (uint8_t)(usb_cdc_init() == 0);
776#else
777 const uint8_t usb = 0u;
778#endif
780 memset(&lc, 0, sizeof lc);
781 lc.flash = log_flash();
782 lc.hw_rev = APP_HW_REV;
783 lc.fw[0] = APP_FW_MAJOR; lc.fw[1] = APP_FW_MINOR; lc.fw[2] = APP_FW_PATCH;
784 lc.sensors = log_sensors;
785#if APP_USB_CDC
786 lc.usb_pump = usb ? usb_cdc_pump : NULL;
787#endif
788 (void)usb;
789 log_task_start(&lc);
790 }
791}
void alt_est_baro(alt_est_t *e, float pa)
Definition alt_est.c:102
void alt_est_tof(alt_est_t *e, float range, uint8_t ok, float ct)
range_m: raw ToF range; status_ok: range status valid; cos_tilt: R33
Definition alt_est.c:120
void alt_est_default_cfg(alt_est_cfg_t *c)
Definition alt_est.c:14
void alt_est_init(alt_est_t *e, const alt_est_cfg_t *c)
Definition alt_est.c:27
float alt_est_accel_up(const float q[4], const float f[3], float nom_g)
vertical acceleration (m/s^2, + up, gravity removed) from the attitude and the specific force in body...
Definition alt_est.c:35
void alt_est_predict(alt_est_t *e, float a, float dt)
Definition alt_est.c:47
Vertical estimator: height, climb rate, baro bias, accelerometer bias.
static float alt_est_vz(const alt_est_t *e)
Definition alt_est.h:74
static float alt_est_h(const alt_est_t *e)
Definition alt_est.h:73
Phase-1 configuration: P-NUCLEO-WB55 (NUCLEO-WB55RG) + X-NUCLEO-IKS4A1 in Mode 3 (LSM6DSV16X is the s...
#define APP_TLM_REPLAY
Definition app_config.h:116
#define APP_LOG_RAM_KB
RAM log on the bench: ~14 s at ~3.4 kB/s
Definition app_config.h:171
#define APP_MAG_FAST
LIS2MDL 100 Hz, hub 120 Hz: halves the age of mag data.
Definition app_config.h:21
#define APP_HW_REV
CAPS hwRev: 'N' = NUCLEO-WB55 + IKS4A1 bench, 'A' = ACwing FC rev A.
Definition app_config.h:128
#define MAG_HARD_IRON
uT, body frame
Definition app_config.h:110
#define APP_FLOW_MIN_H
Definition app_config.h:136
#define APP_TLM_TX_TIMEOUT_MS
max wait for the UART DMA before dropping
Definition app_config.h:114
#define MAG_AXIS_MAP
Definition app_config.h:109
#define APP_INIT_EVERY_N
repeat INIT ~once per second (2 frames per window)
Definition app_config.h:115
#define APP_XL_LPF2_ENABLE
Definition app_config.h:19
#define APP_XL_LPF2_BW
Definition app_config.h:20
#define APP_FLOW_SIGN_Y
Definition app_config.h:132
#define APP_TLM_DECIM_REPLAY
Definition app_config.h:113
#define APP_IMU_ODR
Definition app_config.h:16
#define APP_BATT_SENS_V_A
ACwing ESC 1S: TSC213 + 2 mOhm = 0.1 V/A
Definition app_config.h:162
#define APP_BATT_ADC
Definition app_config.h:153
#define APP_FLOW_SIGN_X
Definition app_config.h:131
#define APP_TLM_DECIMATION
1 = every acc update (240 Hz), ~18.5 kB/s
Definition app_config.h:112
#define APP_GY_LPF1_BW
gyro anti-alias filter: to be tuned
Definition app_config.h:18
#define APP_IMU_WATERMARK
FIFO words: 2 gyro + 2 acc -> IRQ at 960 Hz.
Definition app_config.h:17
#define APP_STEPS_PER_ACC
-> 240 Hz acc update + attitude loop
Definition app_config.h:24
#define APP_BATT_CURRENT
a current sensor is wired (CAPS CURRENT)
Definition app_config.h:161
#define APP_STEP_HZ
propagation + rate loop
Definition app_config.h:23
void app_exti(uint16_t pin)
Definition app_main.c:136
static imu_parse_stats_t s_psTot
Definition app_main.c:89
#define FC_SERVICE_MS
Definition app_main.c:76
#define APP_IWDG_RELOAD
Definition app_main.c:75
static volatile uint32_t s_wordsMax
Definition app_main.c:85
static osThreadId_t s_imuTask
Definition app_main.c:79
static float s_baro_temp
Definition app_main.c:123
static void take_calibrations(void)
Definition app_main.c:255
void app_i2c_error(I2C_HandleTypeDef *h)
Definition app_main.c:145
static volatile uint32_t s_procCycMax
Definition app_main.c:84
static est_config_t s_ec
Definition app_main.c:106
static void tx_frame(const uint8_t *f, uint16_t len)
Definition app_main.c:600
UART_HandleTypeDef huart1
static tlm_msg_t s_msg
Definition app_main.c:149
static cal_set_t s_calset
Definition app_main.c:105
static uint16_t s_tof_mm
Definition app_main.c:117
#define FLAG_IMU_IRQ
Definition app_main.c:69
static volatile uint32_t s_procCycLast
Definition app_main.c:84
static float s_cpu_pct
Definition app_main.c:121
static imu_event_t s_ev[IMU_FIFO_MAX_WORDS]
Definition app_main.c:94
static volatile uint32_t s_tlmDrop
Definition app_main.c:82
static volatile float g_level_trim_deg[2]
Definition app_main.c:107
#define IMU_STALE_MS
Definition app_main.c:126
static uint8_t s_flow_ok
Definition app_main.c:115
static uint16_t log_sensors(void)
Definition app_main.c:708
static void correct_events(imu_event_t *ev, size_t n)
Definition app_main.c:227
static float s_jitter_us
Definition app_main.c:121
static volatile uint32_t s_irqCyc
Definition app_main.c:81
static volatile uint32_t s_fifoOvr
Definition app_main.c:82
static void on_init(const est_t *e, void *user)
Definition app_main.c:201
static volatile int8_t s_imuInit
Definition app_main.c:88
static void TelemetryTask(void *arg)
Definition app_main.c:647
static void push_frame(size_t len)
Definition app_main.c:151
volatile uint16_t g_initLen
Definition app_main.c:102
uint8_t g_initFrame[TLM_FRAME_MAX]
Definition app_main.c:101
static size_t get_init(uint8_t *dst, size_t cap)
Definition app_main.c:681
static volatile uint32_t s_drainReads
Definition app_main.c:86
static uint8_t s_flow_q
Definition app_main.c:112
void app_i2c_rx_cplt(I2C_HandleTypeDef *h)
Definition app_main.c:144
static volatile float s_odrHz
Definition app_main.c:87
static uint32_t s_tof_ms
Definition app_main.c:118
static imu_t s_imu
Definition app_main.c:91
static uint16_t s_imu_sensors
Definition app_main.c:124
static void log_records(uint32_t now, const float q[4], const fc_vert_t *v, const fc_setpoint_t *sp)
Definition app_main.c:391
static void ImuTask(void *arg)
Definition app_main.c:455
static uint16_t s_acc_clip
Definition app_main.c:120
static float s_vib2
Definition app_main.c:119
static volatile uint32_t s_lastGyroMs
Definition app_main.c:125
static alt_est_t s_alt
Definition app_main.c:111
static uint32_t s_cpu_cyc
Definition app_main.c:122
static void motor_test_hook(uint8_t m, float duty)
Definition app_main.c:689
static uint8_t s_fifo[IMU_FIFO_MAX_WORDS *IMU_FIFO_WORD_BYTES]
Definition app_main.c:93
static float s_flow_v[2]
Definition app_main.c:113
static volatile uint32_t s_busErr
Definition app_main.c:82
static void iwdg_start(void)
Definition app_main.c:212
static void send_status(uint8_t *frame, size_t cap)
Definition app_main.c:623
static volatile uint32_t s_irqCount
Definition app_main.c:82
static void flight_service(uint32_t *lastSvc)
Definition app_main.c:416
static void publish_est(uint32_t now)
Definition app_main.c:335
void app_uart_tx_cplt(UART_HandleTypeDef *h)
Definition app_main.c:146
static float s_loop_hz
Definition app_main.c:121
static est_t s_est
Definition app_main.c:92
static volatile uint8_t s_est_restart
Definition app_main.c:108
static uint32_t s_flow_ms
Definition app_main.c:114
void app_start(void)
Definition app_main.c:716
static void take_ext(uint32_t now, float cos_tilt, const float omega[3])
Definition app_main.c:295
static const log_flash_t * log_flash(void)
Definition app_main.c:692
static osMessageQueueId_t qTlm
Definition app_main.c:80
static void on_state(const est_t *e, void *user)
Definition app_main.c:158
static uint8_t s_flow_usable
Definition app_main.c:116
I2C_HandleTypeDef hi2c1
static void dwt_init(void)
Definition app_main.c:129
Integration into main.c (CubeMX, CMSIS-RTOS2):
void app_task_start(const app_task_cfg_t *cfg)
app_init + the RTOS task
Definition app_task.c:929
const cal_set_t * app_task_boot(const float mag_hi_default[3])
Before the scheduler starts: loads the flash image (parameters, name, calibrations) and posts the sav...
Definition app_task.c:806
AppTask: ACwing Pilot protocol v2 (HANDOVER_FW.md) on the drone side.
#define APP_FW_MINOR
Definition app_task.h:25
#define APP_FW_PATCH
Definition app_task.h:26
#define APP_FW_MAJOR
Definition app_task.h:24
int batt_adc_read(float *vbat_pin, float *cur_pin)
0 = a new sample: pin voltages in volts
Definition batt_adc.c:79
int batt_adc_init(void)
0 = ok
Definition batt_adc.c:30
ADC1 binding of the BattTask: VBAT and current pins, VREFINT.
void batt_task_start(const batt_task_cfg_t *cfg)
Definition batt_task.c:124
BattTask: owns the battery ADC and the battery monitor (battery.c).
double sp
Definition calib_mgr.c:42
uint32_t n
Definition calib_mgr.c:35
float cap[6][3]
Definition calib_mgr.c:36
void cal_copy_type(cal_set_t *d, const cal_set_t *s, uint8_t type)
copies the part of 'src' that belongs to 'type' into 'dst'
Definition calib_mgr.c:72
void ctrl_init(void)
Control hook points, already called by the estimation task at the right rates.
void app_est_config(est_config_t *ec)
Fills every field from app_config.h, including APP_COMPARE_ORIGINAL.
Definition est_config.c:10
The production configuration of the attitude estimator, built from app_config.h in one place,...
void est_init(est_t *e, const est_config_t *cfg, est_cb_t on_state, est_cb_t on_init, void *user)
void est_process(est_t *e, const imu_event_t *ev, size_t n, float dt)
dt_sample: IMU sample period measured by the MCU (s)
Multi-rate estimation pipeline, WITHOUT any HAL or RTOS dependency.
#define EST_F_MAG_NEW
int ext_take(ext_sample_t *s)
flight context: 1 if a sample was taken
Definition ext_sens.c:81
uint16_t ext_sensors(void)
PV2_S_TOF | PV2_S_FLOW currently detected.
Definition ext_sens.c:82
void ext_sens_start(void)
creates the task (after imu_i2c_bus_init)
Definition ext_sens.c:100
Expansion sensors on the Qwiic bus (ACwing Flow rev A): VL53L1X ToF and the optical-flow bridge.
@ EXT_FLOW
Definition ext_sens.h:23
@ EXT_TOF
Definition ext_sens.h:23
void fc_service(uint32_t now, uint8_t est_running, const fc_vert_t *v)
v: NULL = no vertical estimate (in air only via g_fc_dbg_in_air)
Definition fc_core.c:699
void fc_setpoint(fc_setpoint_t *out)
last setpoints, same context
Definition fc_core.c:98
void fc_init(const fc_config_t *cfg)
Definition fc_core.c:71
void fc_snapshot(fc_state_t *out)
Definition fc_core.c:121
Flight core: arming, modes, automatic actions, failsafes, motor test (HANDOVER_FW....
int fsh_cal_push(const cal_sample_t *s)
Definition fc_shared.c:44
int fsh_apply_take(fsh_apply_t *a)
Definition fc_shared.c:55
volatile uint8_t g_fsh_cal_want
calibration samples (ImuTask -> AppTask)
Definition fc_shared.c:7
void fsh_est_write(const fsh_est_t *s)
ImuTask.
Definition fc_shared.c:23
void fsh_est_read(fsh_est_t *out)
AppTask: keeps *out on a race.
Definition fc_shared.c:32
Data exchanged between the flight context (ImuTask) and the AppTask.
void iekf_get_quat(const iekf_t *f, float q[4])
Definition iekf.c:408
#define IEKF_NX
Definition iekf.h:29
void imu_i2c_make_bus(sensor_bus_t *bus, I2C_HandleTypeDef *hi2c, uint8_t addr7)
Definition imu_i2c.c:66
void imu_i2c_error_isr(I2C_HandleTypeDef *hi2c)
Definition imu_i2c.c:96
void imu_i2c_rx_cplt_isr(I2C_HandleTypeDef *hi2c)
To be called from HAL_I2C_MemRxCpltCallback / HAL_I2C_ErrorCallback.
Definition imu_i2c.c:91
int32_t imu_i2c_read_dma(uint8_t reg, uint8_t *buf, uint16_t len, uint32_t timeout_ms)
Starts the DMA read and blocks the calling task until completion.
Definition imu_i2c.c:76
void imu_i2c_bus_init(I2C_HandleTypeDef *hi2c)
Creates the bus lock: call once before starting the tasks that use the bus.
Definition imu_i2c.c:21
STM32 HAL (STM32WB55) binding of the sensor_bus_t interface + DMA burst read of the FIFO with task no...
size_t imu_fifo_parse(imu_t *imu, const uint8_t *raw, uint16_t words, imu_event_t *ev, size_t cap, imu_parse_stats_t *st)
Decodes 'words' words read in a burst from IMU_FIFO_REG.
int32_t imu_init(imu_t *imu, const sensor_bus_t *bus, const imu_config_t *cfg)
int32_t imu_fifo_level(imu_t *imu, uint16_t *words)
Number of 7-byte words in the FIFO.
IMU library: LSM6DSV16X (gyro + acc) with the LIS2MDL and the barometer (LPS22DF on the X-NUCLEO-IKS4...
@ IMU_OK
@ IMU_EV_BARO
@ IMU_EV_ACC
@ IMU_EV_GYRO
#define IMU_FIFO_REG
0x78
#define IMU_FIFO_WORD_BYTES
#define IMU_I2C_ADDR_7BIT
SA0 = 1 (default IKS4A1: 0xD6/0xD7)
#define IMU_FIFO_MAX_WORDS
burst buffer: 336 bytes
void logf_vert(uint32_t t, const fc_vert_t *v, const fc_setpoint_t *sp, uint16_t tof_mm, uint32_t baro_pa)
Definition log_feed.c:30
void logf_est(uint32_t t, const float b[3], float unc, float vib, float hz, float jit, float cpu, float mi)
Definition log_feed.c:63
void logf_att(uint32_t t, const float q[4], const float w[3], const float a[3])
Definition log_feed.c:20
void logf_set_clock(uint32_t(*now_ms)(void))
clock for the event records (ms)
Definition log_feed.c:13
void logf_event(const fc_event_t *e)
fc_config_t.on_event
Definition log_feed.c:78
void logf_flight(uint32_t t, const fc_state_t *s)
Definition log_feed.c:50
Producers' side of the flight log: build a record from the values the task already has and queue it (...
void log_flash_ram_init(log_flash_t *f, uint8_t *buf, uint32_t size)
buf: size bytes (rounded down to whole sectors)
log_flash_t on a RAM buffer with NOR semantics (erase sets 0xFF, program can only clear bits).
uint8_t log_q_active(void)
Definition log_queue.c:24
Record queues from the producing tasks to the LogTask.
uint8_t seq
Definition log_task.c:48
uint8_t log_task_pct(void)
flash used %, 0xFF = no log flash (SYSTEM.log); any task
Definition log_task.c:70
uint16_t ms
Definition log_task.c:42
void log_task_start(const log_task_cfg_t *cfg)
Definition log_task.c:385
LogTask: owns the log flash, writes the flight log and serves the log download over USB.
void motors_arm(uint8_t armed)
disarmed: duty forced to 0
Definition motors.c:24
void motors_init(void)
Definition motors.c:13
void motors_test(uint8_t motor, float duty)
MOTOR_TEST (props off, disarmed): motor 1..4 at duty, the others off; motor 0 = all off.
Definition motors.c:44
PWM for 4 brushed coreless motors (8520, 1S) - PREPARED, unused in phase 1.
#define MOTORS_ENABLE
1 only with the power stage connected
Definition motors.h:16
ACwing Pilot app protocol v2: identifiers and frame layouts.
#define PV2_S_MAG_FC
Definition proto_v2.h:123
#define PV2_S_FLASH_LOG
Definition proto_v2.h:132
#define PV2_S_FLOW
Definition proto_v2.h:126
#define PV2_S_BARO
Definition proto_v2.h:122
@ PV2_CAL_COMPASS
Definition proto_v2.h:152
@ PV2_CAL_ACCEL
Definition proto_v2.h:152
@ PV2_CAL_GYRO
Definition proto_v2.h:152
@ PV2_CAL_LEVEL
Definition proto_v2.h:152
#define PV2_S_CURRENT
Definition proto_v2.h:129
#define PV2_S_IMU
Definition proto_v2.h:121
Single-precision SO(3) Lie-group utilities (port of expSO3.m / skewSym.m + firmware helpers).
uint8_t baro_ok
Definition alt_est.h:47
uint8_t tof_valid
last ToF sample used
Definition alt_est.h:53
uint8_t valid
an altitude source is initialised
Definition alt_est.h:58
uint8_t tof_ground
below the minimum range: on the floor
Definition alt_est.h:54
float tof_h
last height above the floor (m)
Definition alt_est.h:52
float pressure
last Pa
Definition alt_est.h:49
uint16_t board_sensors
CAPS bits of the board itself (CURRENT, FLASH_LOG)
Definition app_task.h:36
char hw_rev
CAPS hwRev: 'N' Nucleo bench, 'A' ACwing FC rev A.
Definition app_task.h:29
uint32_t uid[3]
MCU unique id -> serial "ACW-xxxxxxxx"
Definition app_task.h:32
uint8_t(* log_pct)(void)
flash log used %, 0xFF = no log; may be NULL
Definition app_task.h:38
uint8_t motors_built
Definition app_task.h:31
size_t(* get_init)(uint8_t *dst, size_t cap)
INIT frame of the estimator (copy; 0 if not ready yet)
Definition app_task.h:34
uint8_t batt_monitor
a BattTask runs: POWER, CHECKS item 1, BATTERY calibration
Definition app_task.h:35
uint8_t n_motors
Definition app_task.h:30
float sens_v_per_a
current sensor gain, V/A
Definition battery.h:46
uint8_t has_current
a current sensor is wired
Definition battery.h:47
float scale_i
current multiplier (1 = nominal sensor)
Definition battery.h:45
float scale_v
×10 divider ratio (110 = ×11)
Definition battery.h:44
int(* adc_read)(float *vbat_pin, float *cur_pin)
ADC binding: pin voltages of VBAT and CUR; 0 = sample ready.
Definition batt_task.h:31
batt_cfg_t batt
sensor gains, scales at boot
Definition batt_task.h:29
float scale[3]
Definition calib_mgr.h:25
float off[3]
Definition calib_mgr.h:25
float scale_v
Definition calib_mgr.h:30
float scale_i
Definition calib_mgr.h:30
float scale[3]
Definition calib_mgr.h:28
float hi[3]
Definition calib_mgr.h:28
float bias[3]
Definition calib_mgr.h:27
float pitch_deg
Definition calib_mgr.h:26
float roll_deg
Definition calib_mgr.h:26
float q[4]
attitude, body -> world NWU [w x y z]
Definition calib_mgr.h:51
float gyro[3]
rad/s, body
Definition calib_mgr.h:49
float acc[3]
g, body
Definition calib_mgr.h:48
uint8_t running
estimator running
Definition calib_mgr.h:53
float mag[3]
uT, body (axis map applied, no hard/soft iron)
Definition calib_mgr.h:50
uint8_t mag_new
Definition calib_mgr.h:52
cal_level_t level
Definition calib_mgr.h:34
cal_battery_t battery
Definition calib_mgr.h:38
cal_compass_t compass
Definition calib_mgr.h:36
cal_accel_t accel
Definition calib_mgr.h:33
cal_gyro_t gyro
Definition calib_mgr.h:35
float mag_hard_iron[3]
uT, body frame
float mag_map[9]
LIS2MDL sensor -> body (LSM6DSV16X)
float mag_scale[3]
float dt_s
flow: time covered by dx/dy, 0 = status only
Definition ext_sens.h:31
uint16_t tof_mm
Definition ext_sens.h:28
int16_t dy
flow counts (0 without new motion)
Definition ext_sens.h:29
uint8_t kind
EXT_TOF / EXT_FLOW.
Definition ext_sens.h:26
int16_t dx
Definition ext_sens.h:29
uint8_t status
ToF range status (0 valid) / flow STATUS byte.
Definition ext_sens.h:27
uint8_t squal
Definition ext_sens.h:30
uint16_t sensors
CAPS bits detected at boot
Definition fc_core.h:70
setpoints for the attitude / rate loops (phase 3) and the motor mixer
Definition fc_core.h:115
published state (AppTask builds FLIGHT / MOTORS from it)
Definition fc_core.h:85
vertical state from the estimator (flight-control context)
Definition fc_core.h:103
float h
Definition fc_core.h:105
uint8_t tof_valid
Definition fc_core.h:106
uint8_t valid
an altitude source is running (baro and/or ToF)
Definition fc_core.h:104
float vz
m, m/s, + up (alt_est)
Definition fc_core.h:105
uint8_t flow_usable
Definition fc_core.h:110
float cos_tilt
R33 of the attitude
Definition fc_core.h:108
uint8_t tof_ground
ToF used / ToF below its minimum range
Definition fc_core.h:106
float tof_h
m above the floor
Definition fc_core.h:107
uint8_t flow_q
0..100, 0xFF n/a
Definition fc_core.h:109
calibration to apply (AppTask -> ImuTask)
Definition fc_shared.h:46
uint8_t type
Definition fc_shared.h:46
cal_set_t set
Definition fc_shared.h:46
float loop_hz
Definition fc_shared.h:24
uint8_t alt_valid
vertical / navigation
Definition fc_shared.h:26
float cpu_pct
Definition fc_shared.h:24
uint8_t flow_ok
bridge alive, PMW3901 OK
Definition fc_shared.h:30
uint16_t tof_mm
raw, 0xFFFF n/a
Definition fc_shared.h:28
float tof_h
Definition fc_shared.h:27
uint8_t running
Definition fc_shared.h:18
uint8_t flow_q
0..100, 0xFF n/a
Definition fc_shared.h:29
uint16_t sensors
IMU / MAG_FC / BARO found by the IMU init.
Definition fc_shared.h:34
float mag_interf_pct
Definition fc_shared.h:21
float vib_mg
RMS of |a| - 1 g over ~1 s
Definition fc_shared.h:22
float imu_temp_c
Definition fc_shared.h:24
float h
Definition fc_shared.h:27
uint8_t tof_valid
Definition fc_shared.h:26
float vel_body[2]
m/s from flow x height (x fwd, y left); 0 when not usable
Definition fc_shared.h:32
float jitter_us
Definition fc_shared.h:24
uint8_t acc_gated
Definition fc_shared.h:18
uint8_t imu_ok
Definition fc_shared.h:18
float att_unc_deg
1 sigma, largest axis
Definition fc_shared.h:20
uint16_t acc_clip
samples near full scale since arm
Definition fc_shared.h:23
uint8_t flow_usable
above the PMW3901 minimum height (80 mm)
Definition fc_shared.h:31
uint8_t mag_gated
Definition fc_shared.h:18
float vz
Definition fc_shared.h:27
uint32_t baro_pa
0 n/a
Definition fc_shared.h:33
float q[4]
body -> world NWU
Definition fc_shared.h:19
uint8_t tof_ground
Definition fc_shared.h:26
imu_odr_t odr
same ODR for gyro and acc
float odr_hz
uint8_t mag_enabled
uint8_t baro_enabled
flash backend; addresses in bytes from the start of the log area
Definition log_store.h:28
const log_flash_t * flash
NULL: no log flash on this board
Definition log_task.h:40
void(* usb_pump)(void)
board binding: move queued USB TX bytes to the endpoint; may be NULL
Definition log_task.h:45
uint8_t fw[3]
Definition log_task.h:42
uint16_t(* sensors)(void)
CAPS bits for LR_INFO; may be NULL
Definition log_task.h:43
uint16_t len
Definition app_main.c:97
uint8_t bytes[TLM_FRAME_MAX]
Definition app_main.c:98
uint32_t uptime_ms
Definition telemetry.h:83
size_t tlm_encode(uint8_t type, const void *payload, uint8_t len, uint8_t *out, size_t cap)
Writes the complete frame into out; returns its length, 0 if cap is too small.
Definition telemetry.c:18
Telemetry packet format (shared with 02 - Tools/00 - Matlab/Telemetry/parseTelemetry....
#define TLM_FRAME_MAX
Definition telemetry.h:116
#define TLM_TYPE_STATE
Definition telemetry.h:28
#define TLM_OVERHEAD
Definition telemetry.h:32
#define TLM_TYPE_STATUS
10 Hz diagnostics, ALWAYS sent (also during calibration)
Definition telemetry.h:30
#define TLM_TYPE_REPLAY
trace of the filter calls + resulting state (parity)
Definition telemetry.h:31
int tlm_ble_push(const uint8_t *frame, uint16_t len)
Queue a whole frame or nothing.
Definition tlm_ble.c:98
int tlm_ble_want_state(uint16_t seq)
1 if a STATE frame with this sequence number must go over BLE (decimation)
Definition tlm_ble.c:115
uint16_t tlm_ble_mtu(void)
Definition tlm_ble.c:236
uint32_t tlm_ble_dropped(void)
Definition tlm_ble.c:238
tlm_ble_state_t tlm_ble_state(void)
Definition tlm_ble.c:235
uint8_t tlm_ble_error(void)
Definition tlm_ble.c:239
int tlm_ble_take_init_request(void)
1 once after the legacy [0x01] command: send INIT now (TelemetryTask)
Definition tlm_ble.c:128
BLE telemetry, transport-independent part (phase 2 + protocol v2).
size_t tlm_build_init(const est_t *e, float fs_state_hz, uint8_t *frame, size_t cap)
Definition tlm_build.c:26
size_t tlm_build_replay(const est_t *e, uint8_t *frame, size_t cap)
Definition tlm_build.c:63
size_t tlm_build_state(const est_t *e, uint8_t *frame, size_t cap)
Definition tlm_build.c:10
Builds telemetry frames from the pipeline state.
int tlm_uart_send(const uint8_t *buf, uint16_t len, uint32_t timeout_ms)
Non-blocking beyond timeout_ms: if the previous DMA has not finished, the frame is dropped.
Definition tlm_uart.c:23
uint32_t tlm_uart_dropped(void)
Definition tlm_uart.c:41
int tlm_uart_init(UART_HandleTypeDef *huart)
Definition tlm_uart.c:16
void tlm_uart_tx_cplt_isr(UART_HandleTypeDef *huart)
To be called from HAL_UART_TxCpltCallback() (see 03 - Docs/firmware/build.md)
Definition tlm_uart.c:36
USB transport: UART -> ST-LINK Virtual COM Port (USART1 on the NUCLEO-WB55).
void usb_cdc_pump(void)
Definition usb_cdc.c:72
int usb_cdc_init(void)
0 = started
Definition usb_cdc.c:61
USB CDC (virtual COM port) binding of the USB link (telemetry/usb_link.h).
static USBD_StatusTypeDef st(HAL_StatusTypeDef h)
Definition usbd_conf.c:86