|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
What the panel tells the SERVO screen and takes from it. More...
#include "servo_screen.h"#include <math.h>#include <stdio.h>#include <string.h>#include "outputs.h"#include "servo_limrun.h"#include "ui_plot.h"#include "ui_text.h"#include "ui_value_gate.h"#include "ui_widgets.h"#include "servo_screen_core_int.h"Functions | |
| void | value_edge (ui_value_edge_t edge, servo_test_abort_t why) |
| What an edge of the value's state does, in one place. | |
| void | servo_screen_set_armed (bool armed) |
| Whether the bench is armed for the surfaces, so the button can say it. An arm for the motor alone is not one: ARM on this screen arms the surfaces, and a bench armed for the motor disarms before it does. | |
| void | servo_screen_cancel_arm (void) |
| bool | servo_screen_take (servo_cmd_t *out) |
| void | servo_screen_feedback (uint16_t position_us, float current_a, bool valid) |
| void | servo_screen_encoder (const servo_test_enc_t *e) |
| int32_t | svs_fixed_of (float x, float per) |
x in units of 1/per, rounded half away from zero, held to +-10^8. | |
| rail_t | svs_rail_now (void) |
| The servo rail as its meter reads it: what arrived, at the precision it is drawn with. | |
| static int32_t | cur_key_of (const rail_t *r) |
| Returns the CURRENT row's key: twice the current in hundredths of an amp plus 1 when clipped, CUR_KEY_NONE for none. | |
| static void | cur_follow (void) |
| The CURRENT row after its meter or the meter's reading changed: redrawn only when what it shows did, hundredths of an amp and the colour. | |
| static void | rail_plot (void) |
| One point of the plot from the meter's reading: a quantity that did not arrive is a gap, not a zero. | |
| servo_test_reading_t | test_reading_of (const supply_state_t *st) |
| Gives the servo test the supply's reading. | |
| static void | supply_sample (const supply_state_t *st, bool in_force) |
One sample of the supply. in_force: it was taken under the meter in force, and not under one replaced since. | |
| static bool | source_take (servo_source_id_t id, uint32_t changes) |
The meter id as decided at change count changes, unless the meter in force was decided at a later one: the snapshot, the windows and the supply's samples reach the render task on three paths, and each can be the oldest. Returns whether it is the meter in force. | |
| void | servo_screen_source (servo_source_id_t id, uint32_t changes) |
The servo rail's meter, as servo_source.h decides it, every frame before that frame's windows and supply samples. changes is servo_source_changes() of the same poll: the meter is taken unless the one in force was decided at a later count, which a window's or a supply sample's can be (servo_screen_window(), servo_screen_supply_at()). | |
| void | servo_screen_source_why (servo_source_why_t why) |
| void | servo_screen_source_dropped (servo_source_why_t why, uint32_t at) |
| void | servo_screen_supply (const supply_state_t *st) |
| void | servo_screen_supply_at (const supply_state_t *st, servo_source_id_t id, uint32_t changes) |
| void | servo_screen_window (const sense_link_win_t *w, servo_source_id_t id, uint32_t changes) |
One 50ms window of INA3221 CH1 (sense_link_take_win()), each one once, in the order they arrive, with the meter id and its change count changes as decided in the poll that took the window. | |
| int | servo_screen_power_points (void) |
| float | servo_screen_power_sample (int series, int back) |
| Returns a sample of the power plot. | |
| uint16_t | servo_screen_commanded (void) |
| bool | servo_screen_log_pulse (uint16_t *us) |
| The commanded pulse for a SERVO run's log row, as COMMANDED shows it: none while a programming session holds the port. | |
| bool | servo_screen_log_angle (float *deg) |
| The encoder's angle for a SERVO run's log row, as MEASURED shows it: AS5600 on in SETUP, a valid reading and the link up. | |
| const ui_keypad_t * | servo_screen_keypad (void) |
| The keypad over the left column: a set point's, a settings row's or TRIM's, which is signed; for tests and the renderer. | |
| const char * | servo_screen_commanded_text (void) |
| uint16_t | servo_screen_drawn (void) |
| uint16_t | servo_screen_frame_hz (void) |
| const char * | servo_screen_type_name (void) |
| Returns the name of the servo type in force. | |
| void | servo_screen_rate (servo_rate_state_t st, uint16_t hz) |
| void | servo_screen_set_commanded (float deg) |
| void | servo_screen_set_link (bool up) |
| void | servo_screen_set_enc_held (bool held) |
| void | servo_screen_set_drive (uint8_t target, uint8_t gpio, bool pwm) |
| void | servo_screen_set_pins (uint64_t gpio_mask, bool known) |
| void | servo_screen_set_surfaces (bool any) |
| void | servo_screen_sweep_refused (void) |
| void | servo_screen_set_bench_armed (bool armed) |
What the panel tells the SERVO screen and takes from it.
What the panel tells the SERVO screen and takes from it: the bench's arm and disarm, a stop, the command collected, the servo's feedback, the output encoder, the servo rail's meter and the supply's samples, the link, the drive target and the bound pins; and the readings the panel and the tests ask of the screen. The state and the seam are servo_screen_int.h and servo_screen_core_int.h.
SPDX-License-Identifier: MIT
| void servo_screen_cancel_arm | ( | void | ) |
Abandon a hold that is under way, because the bench has been stopped.
Separate from servo_screen_set_armed() because a stop on a bench that was not armed changes nothing about whether it is armed, and the gesture must end all the same. A drag ends, and a position, a sweep or a hold still waiting to be taken is not sent, as at a disarm.
| uint16_t servo_screen_commanded | ( | void | ) |
Commanded pulse width, for the application and for tests.
| const char * servo_screen_commanded_text | ( | void | ) |
The value of the COMMANDED row as it is drawn: the pulse in us, and "---" while the servo programmer holds the port and no pulse is on it.
| uint16_t servo_screen_drawn | ( | void | ) |
The pulse width the solid arm is drawn at: measured, or without feedback the drawing's estimate of the output. For tests.
| void servo_screen_encoder | ( | const servo_test_enc_t * | e | ) |
A reading of the output encoder (an AS5600 on the horn shaft), each one once, in the order they arrive. With AS5600 on in SETUP the MEASURED row shows its angle from the centre count, and a run reads it for the angle results in its CSV and report. A reading that is not valid – the sensor not answering, or answering and detecting no magnet – shows "---", and ENC CENTRE takes nothing from it.
| void servo_screen_feedback | ( | uint16_t | position_us, |
| float | current_a, | ||
| bool | valid ) |
What the output is actually doing, from the bench or from the model. current_a is not shown: the CURRENT row reads the servo rail's meter (servo_screen_source()).
| uint16_t servo_screen_frame_hz | ( | void | ) |
The frame rate and the type in force, for the application and tests.
| const ui_keypad_t * servo_screen_keypad | ( | void | ) |
The keypad over the left column: a set point's, a settings row's or TRIM's, which is signed; for tests and the renderer.
| bool servo_screen_log_angle | ( | float * | deg | ) |
The encoder's angle for a SERVO run's log row, as MEASURED shows it: AS5600 on in SETUP, a valid reading and the link up.
| deg | Receives the horn's angle from the centre count, deg. |
deg NULL. | bool servo_screen_log_pulse | ( | uint16_t * | us | ) |
The commanded pulse for a SERVO run's log row, as COMMANDED shows it: none while a programming session holds the port.
| us | Receives the pulse, us. |
us NULL. | int servo_screen_power_points | ( | void | ) |
How many points the power plot holds, and its series series (0 the voltage in V, 1 the current in A, 2 the power in W) back points before the newest: ui_plot_sample(). For tests.
| float servo_screen_power_sample | ( | int | series, |
| int | back ) |
Returns a sample of the power plot.
| series | the series |
| back | samples back from the newest |
| void servo_screen_rate | ( | servo_rate_state_t | st, |
| uint16_t | hz ) |
What the panel last heard about the frame rate hz. Called every frame; a state about a rate other than the one the screen shows reads as SERVO_RATE_UNSENT for it.
| void servo_screen_set_armed | ( | bool | armed | ) |
Whether the bench is armed for the surfaces, so the button can say it. An arm for the motor alone is not one: ARM on this screen arms the surfaces, and a bench armed for the motor disarms before it does.
The screen does not decide whether it is armed: arming is asked of the policy and the answer comes back from the bench, the same way MOTOR & ESC learns it.
The commanded value follows the bench. At every arm it becomes the rest the pins drive – outputs_role_rest() across the range a command carries, 1500us for STANDARD PWM and 760us for NARROW 760 – and without feedback the horn is drawn there at once. While the bench is disarmed no input changes it: the dial, the knob, CENTRE, SWEEP and servo_screen_set_commanded() post nothing and move nothing, and the value shown is the one last driven. At a disarm a drag ends, and a position, a sweep or a hold still waiting to be taken is not sent; the knob's is withdrawn with the value it replaced.
RELEASE on an armed bench is the arm's rule again: the value becomes the rest, a drag ends, and the next input starts from there – for the button, the release a run's end posts, and the one a change of profile posts on a resting bench. On a disarmed bench RELEASE is posted and the value stays.
The state is ui_value_gate.h's. From a SERVO_CMD_DISARM being posted, taken or not, or servo_screen_cancel_arm() on an armed bench, until this is called with false, the value is not live: the inputs are refused as on a disarmed bench, and a sweep, a pause, a held position and a run of the automatic test end where it was asked.
| void servo_screen_set_bench_armed | ( | bool | armed | ) |
Whether the bench is armed for any role. servo_screen_set_armed() is told whether the surfaces are armed, which an arm for the motor alone leaves false; this is what keeps the servo programmer from taking a port under a motor that is armed, as it is kept from one under armed surfaces.
| void servo_screen_set_commanded | ( | float | deg | ) |
Set the commanded angle without a touch event, for the application and for tests. A position like the dial's: taken on an armed bench, and refused with nothing changed on a disarmed one.
| void servo_screen_set_drive | ( | uint8_t | target, |
| uint8_t | gpio, | ||
| bool | pwm ) |
Where the tester's commands go now (prog_link_drive()): target 0 for the standard outputs, 1 + n for programming port n, SERVO_SCREEN_TARGET_UNKNOWN or SERVO_SCREEN_TARGET_NO_LINK; gpio that port's GPIO or SERVO_SCREEN_NO_PIN, and pwm whether the port renders its pulse. The TARGET row follows it, not the TARGET setting. gpio and pwm are ignored without a port target.
| void servo_screen_set_enc_held | ( | bool | held | ) |
Whether the IOmcu holds the output encoder enabled, as SENSE's ENABLE register was last read or written. The SENSE set-up is written only while the bank is disarmed: a run started with the AS5600 setting on and this false has no angles, and takes no angle columns.
| void servo_screen_set_link | ( | bool | up | ) |
Whether the link to the IOmcu is up; a run ends when it goes.
| void servo_screen_set_pins | ( | uint64_t | gpio_mask, |
| bool | known ) |
The GPIOs bound as SERVO PWM outputs (bit n is GPIO n), from the binding the tester drives, and whether a binding has been read. The TARGET row names them while the drive uses the outputs: as many pins as the row's 23 cells hold, then "+N" (ui_pinlist()).
| void servo_screen_set_surfaces | ( | bool | any | ) |
Whether a surface is bound, as the panel last read the binding: SWEEP is offered only then, since a sweep of nothing never starts.
| void servo_screen_source | ( | servo_source_id_t | id, |
| uint32_t | changes ) |
The servo rail's meter, as servo_source.h decides it, every frame before that frame's windows and supply samples. changes is servo_source_changes() of the same poll: the meter is taken unless the one in force was decided at a later count, which a window's or a supply sample's can be (servo_screen_window(), servo_screen_supply_at()).
The CURRENT row, the line over the plot and the plot show that meter's reading. SERVO_SOURCE_INA3221: the last CH1 window – mean current, mean bus voltage at the load side of the shunt and their product – under the label INA3221 CH1. SERVO_SOURCE_PDMINI and SERVO_SOURCE_MODEL: the supply's last sample under the label PD MINI, the PD mini's reading or the model's; with the model (supply_screen_model()) the MODELLED pill sits on the plot's top-left corner. SERVO_SOURCE_MODEL until the first call. The name takes its cells from the readings beside it: beside INA3221 CH1 they are 60, 64 and 56px wide, and a power wider than that is drawn in whole watts.
The row is --- and a reading on the line -- while the meter has no reading: no window since the link came up, a window without samples of that quantity, a supply that does not answer. A current is signed and drawn in hundredths of an amp; one from a window with a sample at an end of the INA3221's range is drawn in the warning colour. The row is repainted, alone, when its digits or its colour change.
A change to or from the INA3221 empties the plot: its trace is of the meter its label names. So does a count more than one ahead of the one in force, with the same meter or another: changes went by unseen, and another meter may have been the rail's in between. The INA3221's last window is then forgotten as well. The plot's scale starts at 0, so a negative current is drawn on its lower edge.
| void servo_screen_source_dropped | ( | servo_source_why_t | why, |
| uint32_t | at ) |
Why the INA3221 last stopped being the meter, and the change count of that step (servo_source_dropped()), as decided at the same poll as the meter handed over next: before servo_screen_source(), and before each servo_screen_window() and servo_screen_supply_at(). SERVO_SOURCE_WHY_NONE says nothing.
The automatic test reads the meter that was the rail's at its start until it ends (servo_test_meter_now()). A run on the INA3221 ends when a meter handed over is not the INA3221 or carries another change count than the run's start: ABORTED, with the condition this call named for a change after the start, the output asked off and the servo let go. A run on the PD mini or the model goes on with it whatever the meter becomes.
| void servo_screen_source_why | ( | servo_source_why_t | why | ) |
Why the INA3221 is not the servo rail's meter now (servo_source_why()), every frame with servo_screen_source(). A run started on the PD mini with the INA3221 on in SETUP states it in its report.
| void servo_screen_supply | ( | const supply_state_t * | s | ) |
One sample of the supply that feeds the servo. The automatic test reads every one: as its reading in a run on the PD mini or the model, and for the supply's state alone in a run on the INA3221. While the servo rail's meter is not the INA3221 (servo_screen_source()) it is also the rail's reading: the CURRENT row, the line over the plot and one point of the plot.
| void servo_screen_supply_at | ( | const supply_state_t * | s, |
| servo_source_id_t | id, | ||
| uint32_t | changes ) |
servo_screen_supply() with the meter id and its change count changes as decided at the poll that took the sample, for samples that waited on a queue. A sample whose count is ahead of the meter in force brings its meter with it, as servo_screen_source() would. One whose count is behind was taken under a meter replaced since: the automatic test reads it and it is the supply's last reading on the line and in the row, and it is no point of the plot.
| void servo_screen_sweep_refused | ( | void | ) |
A sweep command found no surface to sweep and was not sent: the screen stops waiting for its start and ends the sweep.
| bool servo_screen_take | ( | servo_cmd_t * | out | ) |
Take the pending command, if any. Cleared by reading.
| const char * servo_screen_type_name | ( | void | ) |
Returns the name of the servo type in force.
| void servo_screen_window | ( | const sense_link_win_t * | w, |
| servo_source_id_t | id, | ||
| uint32_t | changes ) |
One 50ms window of INA3221 CH1 (sense_link_take_win()), each one once, in the order they arrive, with the meter id and its change count changes as decided in the poll that took the window.
The windows wait on a queue and the meter of servo_screen_source() is read from a snapshot, so either can be the older. A window whose count is behind the meter in force was taken under a meter replaced since and is dropped. One whose count is ahead brings its meter with it, as servo_screen_source() would. While the INA3221 is the rail's meter each window taken is one point of the plot and the reading of the row and the line. One that arrives while the link is down is dropped, and a link that goes down takes the last one with it (servo_screen_set_link()).
A run of the automatic test that started with the INA3221 as the meter reads these windows and no supply sample: each one taken under the meter in force is its reading.
| w | the window |
| id | the meter decided in the poll that took it |
| changes | that meter's change count |
| int32_t svs_fixed_of | ( | float | x, |
| float | per ) |
x in units of 1/per, rounded half away from zero, held to +-10^8.
| x | the reading |
| per | the units in one of x's |
| rail_t svs_rail_now | ( | void | ) |
The servo rail as its meter reads it: what arrived, at the precision it is drawn with.
| servo_test_reading_t test_reading_of | ( | const supply_state_t * | st | ) |
Gives the servo test the supply's reading.
| st | the supply's state |
| void value_edge | ( | ui_value_edge_t | edge, |
| servo_test_abort_t | why ) |
What an edge of the value's state does, in one place.
Into LIVE: the value is the rest.
Out of LIVE, to LEAVING or to OFF: everything that drives the servo by itself ends – a run, ended as why, a sweep, a pause, a drag, and a held position, which a change of SPEED or of the profile would otherwise say again – and a motion command not yet taken is not sent. The value shown stays where it was last driven: the release a run ends with moves none, because the value is no longer live (release()).
| edge | the edge of the value's state |
| why | why a run ended, when one does |