Stepdance Software Library
Loading...
Searching...
No Matches
core.hpp
1#include "WString.h"
2#include <sys/_stdint.h>
3#include <cstddef>
4#include <stdint.h>
5#include <functional>
6#include "arm_math.h"
7#include "Arduino.h"
8/*
9Core Module of the StepDance Control System
10
11This contains core system functions such as the frame interrupt timer.
12
13A part of the Mixing Metaphors Project
14(c) 2025 Ilan Moyer, Jennifer Jacobs, Devon Frost, Emilie Yu
15*/
16#ifndef core_h //prevent importing twice
17#define core_h
18
19class RPC; //forward declaration of RPC from rpc.hpp
20
21typedef volatile float64_t DecimalPosition; //used to store positions across the system. We are using double-precision to allow incremental moves with acceptable error (~0.05 steps/day at 25khz)
22typedef volatile int32_t IntegerPosition; //previously used to store positions
23typedef volatile float32_t ControlParameter; //controls plugin parameters, typically from an analog input value
24
25typedef void (*frame_function_pointer)(); //defines function pointers that can be called at each frame
26
27#define CORE_FRAME_PERIOD_US 40 //microseconds. This yields a max output step rate of 25k steps/sec.
28#define CORE_FRAME_PERIOD_S CORE_FRAME_PERIOD_US / 1000000 //duration of each frame in seconds
29#define CORE_FRAME_FREQ_HZ 1000000 / CORE_FRAME_PERIOD_US //framerate in Hz. This is 25k
30#define MAX_NUM_FRAME_FUNCTIONS 10 //maximum number of functions that can be called on the frame interrupt
31
32#define KILOHERTZ_PLUGIN_PERIOD_US 1000 //microseconds, for the kilohertz plugin timer
33
34// Signal Indices
35// This is ordered by pulse length.
36#define SIGNAL_X 0 //index of the X signal in the active_signal and signal_directions arrrays
37#define SIGNAL_Y 1
38#define SIGNAL_R 2 // Polar Radial Axis
39#define SIGNAL_T 3 // Polar Theta Axis
40#define SIGNAL_Z 4
41#define SIGNAL_E 5 // Extruder
42
43// Standard Step Ratio
44#define STANDARD_RATIO_MM 0.01 // world mm / steps. At 25KHz this provides a max velocity of 250mm/sec.
45#define STANDARD_RATIO_IN 0.0003937 // world inches / step
46
47// Plugin Execution Context
48#define PLUGIN_FRAME_PRE_CHANNEL 0 //runs on the frame, before channels are evaluated
49#define PLUGIN_FRAME_POST_CHANNEL 1 //runs on the frame, after channels are evaluated
50#define PLUGIN_KILOHERTZ 2 //runs in an independent 1khz context
51#define PLUGIN_LOOP 3 //runs in the main loop
52#define PLUGIN_INPUT_PORT 4 //runs on the frame, at the start before all other plugins
53
54// Position Mode
55// Throughout stepdance, there is a question of whether to operate incrementally or in absolute coordinates.
56// By default, we operate incrementally. But some modules require data to be in absolute values (e.g. non-linear functions)
57
58enum{
59 INCREMENTAL, //indicates a position is being provided incrementally
60 ABSOLUTE, //position should be interpreted as an absolute position, relative to the local state of the component
61 GLOBAL //position should be interpreted as absolute, relative to the global (e.g. channel) state of the machine
62};
63// #define INCREMENTAL 0 //data is handled incrementally
64// #define ABSOLUTE 1 //data is handled in absolute values
65
66#define MIN 0
67#define MAX 1
68
69enum{
70 BLOCKPORT_INPUT, //blockport is an input
71 BLOCKPORT_OUTPUT, //blockport is an output
72 BLOCKPORT_UNDEFINED //blockport is undefined
73};
74
75void add_function_to_frame(frame_function_pointer target_function);
76void dance_start();
77
78void stepdance_metrics_reset(); //resets the CPU usage metrics
79float stepdance_get_cpu_usage(); //returns a value from 0-1 indicating the maximum CPU usage.
80static volatile float stepdance_max_cpu_usage = 0; //stores a running count of the maximum CPU usage, in the range 0-1;
81static volatile uint32_t stepdance_interrupt_entry_cycle_count = 0; //stores the entry value of ARM_DWT_CYCCNT
82
83// Forward declaration (because we use BlockPort in Plugin class read_deep method signature declaration)
84class BlockPort;
85
86// -- Plug-In Base Class --
87//
88// This provides a common interface for any filters, synthesizers, kinematics, etc that need access to the core frame.
89#define MAX_NUM_INPUT_PORT_FRAME_PLUGINS 10 //plugins that execute at the start of the frame. This is reserved for input ports
90#define MAX_NUM_PRE_CHANNEL_FRAME_PLUGINS 20 //plugins that execute in the frame, before the channels are evaluated
91#define MAX_NUM_POST_CHANNEL_FRAME_PLUGINS 10 //plugins that execute in the frame, after the channels are evaluated
92#define MAX_NUM_KILOHERTZ_PLUGINS 10 //plugins that execute at a 1khz rate, independent of the frame, and with a lower priority
93#define MAX_NUM_LOOP_PLUGINS 20 //plugins that execute in the main loop.
98class Plugin{
99 // Base class for all plugins that need to run in the core frame.
100 public:
101 Plugin();
102 static void run_input_port_frame_plugins(); //runs all input port frame plugins.
103 static void run_pre_channel_frame_plugins(); //runs all pre-channel frame plugins, in the order they appear in the registered_plugins list
104 static void run_post_channel_frame_plugins(); //runs all post-channel frame plugins, in the order they appear in the registered_plugins list
105 static void run_kilohertz_plugins(); //runs all post-channel frame plugins, in the order they appear in the registered_plugins list
106 static void run_loop_plugins(); //runs all loop plugins, in the order they appear in the registered_plugins list
107
108 virtual void enable();
109 virtual void disable();
110 virtual void enroll(RPC *rpc, const String& instance_name); //enrolls the plugin in an RPC. This should be overridden by the derived class, and is responsible for enrolling any members.
111 virtual void push_deep(); //deep push across the plugin (e.g. from input to output blockports) for state sync.
112 virtual void pull_deep(); //performs a deep pull across the plugin (e.g. from output to input blockports) for state sync
113 virtual DecimalPosition read_deep(BlockPort& in_blockport); //performs a deep read across the plugin (e.g. from output to input blockports) for state sync
114
115 private:
116 static Plugin* registered_input_port_frame_plugins[MAX_NUM_INPUT_PORT_FRAME_PLUGINS]; //stores all registered input port plugins
117 static Plugin* registered_pre_channel_frame_plugins[MAX_NUM_PRE_CHANNEL_FRAME_PLUGINS]; //stores all registered pre-channel frame plugins
118 static Plugin* registered_post_channel_frame_plugins[MAX_NUM_POST_CHANNEL_FRAME_PLUGINS]; //stores all registered post-channel frame plugins
119 static Plugin* registered_kilohertz_plugins[MAX_NUM_KILOHERTZ_PLUGINS]; //stores all registered kilohertz plugins
120 static Plugin* registered_loop_plugins[MAX_NUM_LOOP_PLUGINS]; //stores all registered loop plugins
121 static uint8_t num_registered_input_port_frame_plugins; //tracks the number of registered input port frame plugins
122 static uint8_t num_registered_pre_channel_frame_plugins; //tracks the number of registered pre-channel frame plugins
123 static uint8_t num_registered_post_channel_frame_plugins; //tracks the number of registered post-channel frame plugins
124 static uint8_t num_registered_kilohertz_plugins; //tracks the number of registered kilohertz plugins
125 static uint8_t num_registered_loop_plugins; //tracks the number of registered loop plugins
126
127 protected: //these need to be accessed from derived classes
128 void register_plugin(); //registers the plugin
129 void register_plugin(uint8_t execution_target); //registers the plugin
130 virtual void run(); //this should be overridden in the derived class. Runs each frame.
131 virtual void loop(); //this can be overridden in the derived class. Runs in the main loop context.
132};
134
135// -- BlockPort --
136// BlockPorts provide a unified interface into and out of component blocks (i.e. "blocks")
137// These are designed to be flexibly used depending on the component to which they belong.
138
150
151// Main BlockPort Class
153 public:
158
159 // -- User Functions -- these are called within user code, not (just) the library
165 void set_ratio(float world_units, float block_units = 1.0); // sets the ratio between world and block units, for automatic conversion. Default is 1.
166 // conversion always happens within the write/read functions when data enters and exits the BlockPort
172 void map(BlockPort *map_target, uint8_t mode); //maps this BlockPort's pipe to a target BlockPort
173
178 inline void map(BlockPort *map_target){
179 map(map_target, INCREMENTAL); //default internal mode is INCREMENTAL
180 }
181
182 // -- External Functions -- these are called outside the block that contains this BlockPort
187 float64_t read(uint8_t mode); // externally reads the BlockPort's target via the BlockPort buffers.
188 // an incremental read will reflect the change to the target, either pending or after the block has run.
189 // an absolute read will reflect the upcoming or last state of the target, depending on whether the block has run.
190 // In some cases this function can be overridden by a custom read function.
194 float64_t read_absolute(); // returns the absolute value of the BlockPort's target in world units.
195 // This is provided for simplified access via RPC.
196
201 void write(float64_t value, uint8_t mode); // writes to the BlockPort's absolute or incremental buffers
202
203 void write_now(float64_t); //writes directly to the target. REMEMBER TO UPDATE ABSOLUTE_BUFFER AT SAME TIME.
204 float64_t read_target(); //reads directly from the target
206
207 // -- Internal Functions -- called by the block containing this BlockPort
212 void begin(volatile float64_t *target, uint8_t direction = BLOCKPORT_UNDEFINED, Plugin *parent = nullptr); //initializes the BlockPort
213 void set_target(volatile float64_t *target); //sets a target variable for the BlockPort
214 void update(); //called by the block, to update the target and the buffers. Note that this does not handle pulling or pushing, which must be done first or after update.
215 void reverse_update(); //updates the buffers based on changes made by direct writes to the target. Used by input_ports, which run before all other blocks.
216 void set(float64_t value, uint8_t mode); //sets a new value for the target.
217 inline void set(float64_t value){ //default for set is ABSOLUTE
218 set(value, ABSOLUTE);
219 };
220 void reset(float64_t value, bool raw = false); //resets the target, and updates buffers to reflect new value WITHOUT an incremental update.
221
222 void push(uint8_t mode); // pushes this BlockPort's buffer state to a target.
223 inline void push(){
224 push(this->mode); //uses internal mode
225 }
226
227 void pull(uint8_t mode); // pulls a target BlockPort's buffer state into this BlockPort's buffers.
228 inline void pull(){
229 pull(this->mode); //uses internal mode
230 }
231
232 // State Synchronization Functions
233 // Internal
234 void push_deep(DecimalPosition abs_value); //Pushes an ABSOLUTE value across a mapping chain.
235 DecimalPosition pull_deep(); //pulls an ABSOLUTE value thru from the terminal of the mapping chain.
236
237 // User Facing
238 inline void reset_deep(DecimalPosition abs_value){
239 push_deep(abs_value);
240 }
241
242 DecimalPosition read_deep();
243
244 void enable(); // enables push/pull on blockport
245 void disable(); // disables push/pull
246
247 volatile float64_t incremental_buffer = 0;
248 volatile float64_t absolute_buffer = 0; //contains a new value if absolute_buffer_is_written, otherwise the last value of the associated variable.
249
250 inline float64_t convert_block_to_world_units(float64_t block_units){
251 return block_units * world_to_block_ratio;
252 }
253 inline float64_t convert_world_to_block_units(float64_t world_units){
254 return world_units / world_to_block_ratio;
255 }
256
257 volatile float64_t* target = nullptr;
258
259 void enroll(RPC *rpc, const String& instance_name); //used to enroll the blockport in an RPC
261 private:
262 volatile bool update_has_run = false; //set to true when an update has run, and false when write() is called.
263 uint8_t mode = INCREMENTAL; //default mode used by push and pull, unless specified in that function call. This is set by the map function.
264 volatile uint8_t push_pull_enabled = true; //controlled by enable() and disable(). This enables/disables push and pull. NOTE: We could optimize by removing volatile,
265 // but then this couldn't be operated inside any interrupts incl. the kilohertz interrupt, which could be confusing.
266 // Can re-examine if we start running out of compute overhead.
267 float64_t world_to_block_ratio = 1;
268
269 BlockPort* target_BlockPort = nullptr;
270 Plugin* parent_Plugin = nullptr; //This should only be set on INPUTS.
271 uint8_t blockport_direction = BLOCKPORT_UNDEFINED; //direction is not explicitly set by the parent Plugin
272};
273
274
275// -- LOOP FUNCTION AND CLASSES --
276// These allow non-blocking functions to be called within the loop, at an approximately given frequency.
277
282void dance_loop();
283
284static volatile float stepdance_loop_time_ms; //tracks the time spent in the last loop
285static volatile uint32_t stepdance_loop_entry_cycle_count = 0; //cycle count when dance_loop was last called
286
287class LoopDelay{
288 public:
289 LoopDelay();
290 void periodic_call(void (*callback_function)(), float interval_ms);
291
292 private:
293 float time_since_last_call_ms; //stores time since the function was last called
294};
295
297#endif
BlockPorts provide a unified interface for mapping inputs and outputs of different StepDance componen...
Definition core.hpp:152
float64_t read_absolute()
Returns the absolute position value of the BlockPort in world units.
float64_t read(uint8_t mode)
Returns the position value of the BlockPort in world units. Reading in INCREMENTAL mode returns the c...
void map(BlockPort *map_target, uint8_t mode)
Maps this BlockPort to a target BlockPort with a specified mode (INCREMENTAL or ABSOLUTE).
BlockPort()
Default constructor for BlockPort. Initializes a BlockPort instance. You do not need to call this dir...
void map(BlockPort *map_target)
Maps this BlockPort to a target BlockPort in INCREMENTAL mode.
Definition core.hpp:178
void set_ratio(float world_units, float block_units=1.0)
Sets the ratio between world units and block units for this BlockPort for automatic conversion....
RPC class for handling remote procedure calls over serial streams.
Definition rpc.hpp:35