rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
panel_control.c File Reference

The control task: touch, STOP, arming, the outputs, the link and the heartbeat. More...

#include <math.h>
#include <stdatomic.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/temperature_sensor.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "arming.h"
#include "board_pins.h"
#include "frame_watch.h"
#include "gfx.h"
#include "heartbeat.h"
#include "outputs.h"
#include "panel.h"
#include "panel_main.h"
#include "panel_trial.h"
#include "servo_link.h"
#include "telemetry_sim.h"
#include "touch.h"
#include "touch_loss.h"
#include "ui_band.h"
#include "ui_text.h"

Functions

void heartbeat_init (void)
 Set the heartbeat's pin up as an output, low.
void tsens_init (void)
 Start the die temperature sensor.
static void tsens_read (void)
 Reads the MCU's own die temperature into s_mcu_c.
static void beat (bool alive)
 Steps the heartbeat generator and drives PANEL_HEARTBEAT_PIN with its level.
static void render_watch (void)
 The render loop, watched while the bench is ARMED or the supply's output is on (frame_watch.h).
static bool touch_q_put (const touch_event_t *evt, bool lost)
 Offer one item to s_touch_q, numbered. Returns whether the queue holds it.
static void drv_lost (uint32_t n)
 The driver's stream lost n events. Found on the event after the gap, before that event goes through the STOP branch, or at the end of a drain against the number the driver last published.
void control_pump (void)
 Drains the touch driver's queue, answers sequence gaps, applies a STOP at its press and passes the events on to the render loop.
void control_task (void *arg)
 The control task; app_main creates it on core 1.

Variables

static const char * TAG = "rcbench"
 Log tag.
static heartbeat_gen_t s_beat
 Driven from the loop that reads touch and draws STOP. The line asserts that the processor owning the STOP button is running its loop, which a level cannot assert and a crashed panel cannot fake.
static temperature_sensor_handle_t s_tsens
 The panel's own die, not the IOmcu's. The two boards sit in different air and run different loads, and this is the one the ESP32-P4 can measure without a wire. Read once a second; the sensor is slow and nothing on the screen changes faster.
float s_mcu_c = NAN
 Read and published by the control task; app_main takes it from the snapshot with everything else that crosses between the two.
atomic_bool s_stop_live
 Whether a STOP is drawn to press, from the render loop to the control task's hit test.
static touch_seq_rx_t s_drv_rx = { .next = 1u }
static uint32_t s_tq_seq
 S_touch_q's numbers, this task's alone; see s_tq_published, main.c.
static frame_watch_t s_frame_watch
 The render loop's watchdog, this task's alone; see render_watch().

Detailed Description

The control task: touch, STOP, arming, the outputs, the link and the heartbeat.

The task runs at a fixed CONTROL_PERIOD_MS on the core the renderer does not use. control_pump() is the part of it with a deadline; it also runs inside every link exchange's wait. The heartbeat's pin and the die temperature are this task's too. app_main (main.c) creates it.

SPDX-License-Identifier: MIT

Function Documentation

◆ control_task()

void control_task ( void * arg)

The control task; app_main creates it on core 1.

Parameters
argUnused.

◆ drv_lost()

void drv_lost ( uint32_t n)
static

The driver's stream lost n events. Found on the event after the gap, before that event goes through the STOP branch, or at the end of a drain against the number the driver last published.

The render loop is told by an item in s_touch_q, ahead of the events that followed the gap, so it drops the screens' gestures before it handles any of them.

A STOP press needs nothing here: it stopped the bench at its DOWN, and its release does nothing. Losses in s_touch_q itself cannot lose a STOP either: this task evicts from that queue only after the event has been through the STOP branch.

◆ render_watch()

void render_watch ( void )
static

The render loop, watched while the bench is ARMED or the supply's output is on (frame_watch.h).

A render loop that has stopped leaves STOP working, through this task's own hit test, and nothing else: DISARM, the throttle slider, the servo dial and OUTPUT OFF are the loop's, and the outputs hold their last command. A counter that has stood still for FRAME_WATCH_MS is answered as a STOP of the bench's own, the way an arm the watch undoes is: arming_stop() drops the heartbeat in this pass, the IOmcu fails safe within HEARTBEAT_MAX_GAP_MS (150ms), supply_pump() switches the output off in this pass, and service_arming() lets go of the rest. Nothing here arms or switches anything on, so a loop that comes back finds the latch as a STOP leaves it. The alert shows when the loop draws again.

Called from control_pump(), here and inside every exchange's wait, before the supply's pump.

◆ touch_q_put()

bool touch_q_put ( const touch_event_t * evt,
bool lost )
static

Offer one item to s_touch_q, numbered. Returns whether the queue holds it.

The consumer is behind when the send fails. The oldest entry is dropped and the newest kept, which is what the GT911's own event queue and the command queue do – but which of the two is lost is not what makes this safe, and no choice is safe on its own: a release that never arrives leaves a screen holding a press, a DOWN that never arrives orphans the release after it, and the MOVE where a finger leaves ARM is what abandons the hold. What makes it safe is that the dropped entry's number is missing from what the render loop takes, and it drops the gesture before it handles the next event (touch_loss.h). Nothing is counted here.

The retry runs whether or not the receive took anything: the render loop can empty the queue between the failed send and the receive, and then there is room without anything having been evicted.

Variable Documentation

◆ s_beat

heartbeat_gen_t s_beat
static

Driven from the loop that reads touch and draws STOP. The line asserts that the processor owning the STOP button is running its loop, which a level cannot assert and a crashed panel cannot fake.

The monostable the line gates is on no board: the edges reach a header pin of the panel (PANEL_HEARTBEAT_PIN) and the IOmcu's monitor. They are emitted regardless, at one GPIO (general-purpose input/output) write per pass, so the line is running and can be scoped.

◆ s_drv_rx

touch_seq_rx_t s_drv_rx = { .next = 1u }
static