Tarek Lule / MotorLib

Fork of MotorLib by CreaLab

Committer:
sepp_nepp
Date:
Thu Nov 01 15:29:33 2018 +0000
Revision:
15:88fecbdd191c
Parent:
14:f0667bfc2e98
Child:
16:d818c1a4dafb
Further extended the helps. ; Functions not yet validated.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
sepp_nepp 15:88fecbdd191c 1 /**
sepp_nepp 15:88fecbdd191c 2 * @file motor.h
sepp_nepp 15:88fecbdd191c 3 * @brief File containing Crealab Motor Library.
sepp_nepp 15:88fecbdd191c 4
sepp_nepp 15:88fecbdd191c 5 * motor.h contains the class Motor, and related enums and structs.
sepp_nepp 15:88fecbdd191c 6 * Includes only "mbed.h"
sepp_nepp 15:88fecbdd191c 7 * Doxygens Tags are words preceeded by either a backslash @\ or by an at symbol @@.
sepp_nepp 15:88fecbdd191c 8
sepp_nepp 15:88fecbdd191c 9 * @author Tarek Lule, Francois Druilhe, et al.
sepp_nepp 15:88fecbdd191c 10 * @date 01. Nov. 2018.
sepp_nepp 15:88fecbdd191c 11 * @see https://os.mbed.com/users/sepp_nepp/code/MotorLib/ */
sepp_nepp 15:88fecbdd191c 12
sepp_nepp 15:88fecbdd191c 13 // -------------------- Motor ---------------------------
sepp_nepp 13:4563244c4071 14 #ifndef MOTOR_H
garphil 5:9886fd337a4b 15 #define MOTOR_H
garphil 5:9886fd337a4b 16
garphil 0:bd05fd602a6e 17 #include "mbed.h"
garphil 0:bd05fd602a6e 18
sepp_nepp 13:4563244c4071 19 #define MOTOR_STEP_TIME_MIN_US 700 // was MOTOR_STEP_TIME_MIN
sepp_nepp 13:4563244c4071 20 #define MOTOR_STEP_TIME_DEFAULT_US 5000 // was MOTOR_STEP_TIME_DEFAULT
sepp_nepp 13:4563244c4071 21 #define MOTOR_STEPS_FOR_A_TURN 4096
garphil 9:5983c10d5f8e 22
sepp_nepp 15:88fecbdd191c 23 /** \enum motorStates
sepp_nepp 15:88fecbdd191c 24 * \brief Motor States of motor state machine
sepp_nepp 15:88fecbdd191c 25 *
sepp_nepp 15:88fecbdd191c 26 * Motor_CALIB is deprecated, was removed from the enum structure */
sepp_nepp 15:88fecbdd191c 27 typedef enum {
sepp_nepp 15:88fecbdd191c 28 Motor_OFF = 0, /**< All phase currents is off, replaces Motor_STOP. */
sepp_nepp 15:88fecbdd191c 29 Motor_ZERO, /**< Motor at phase position 0 and ON, only reached by call of Zero() command. */
sepp_nepp 15:88fecbdd191c 30 Motor_ON, /**< Phases are engaged, but motor is not running, replaces Motor_PAUSE. */
sepp_nepp 15:88fecbdd191c 31 Motor_RUN /**< Phases are engaged, and motor state machine is running*/
sepp_nepp 15:88fecbdd191c 32 } motorStates;
sepp_nepp 15:88fecbdd191c 33
sepp_nepp 15:88fecbdd191c 34 /** \enum motorCommands
sepp_nepp 15:88fecbdd191c 35 * \brief Commands that are handled by the Motor state machine
sepp_nepp 15:88fecbdd191c 36 *
sepp_nepp 15:88fecbdd191c 37 * These Commands are issued asynchonously by calling Motor class methods.
sepp_nepp 15:88fecbdd191c 38 * They are executed in the state machine called by the ticker handler.
sepp_nepp 15:88fecbdd191c 39
sepp_nepp 15:88fecbdd191c 40 * OFF and STOP commands do not go through the state machine.
sepp_nepp 15:88fecbdd191c 41
sepp_nepp 15:88fecbdd191c 42 * MOTOR_restart is equivalent to and replaced by MOTOR_run.
sepp_nepp 15:88fecbdd191c 43 */
sepp_nepp 14:f0667bfc2e98 44 typedef enum { // Define Motor State Machine Commands
sepp_nepp 15:88fecbdd191c 45 MOTOR_nop = 0, /**< No active command to execute. */
sepp_nepp 15:88fecbdd191c 46 MOTOR_run, /**< Run Motor until Nsteps are achieved. */
sepp_nepp 15:88fecbdd191c 47 MOTOR_stop, /**< Stop immediately all activity, turn off Motor. */
sepp_nepp 15:88fecbdd191c 48 MOTOR_pause /**< Motor is temporarily paused from the state run. */
sepp_nepp 15:88fecbdd191c 49 } motorCommands;
garphil 0:bd05fd602a6e 50
sepp_nepp 15:88fecbdd191c 51 /** \enum motorDir
sepp_nepp 15:88fecbdd191c 52 * \brief Define Motor direction to be Clockwise or Anticlockwise
sepp_nepp 15:88fecbdd191c 53 */
sepp_nepp 15:88fecbdd191c 54 typedef enum motorDir {
sepp_nepp 15:88fecbdd191c 55 CLOCKWISE = 0, /**< Turn Motor in clockwise direction. */
sepp_nepp 15:88fecbdd191c 56 COUNTERCLOCKWISE /**< Turn Motor in anti-clockwise direction. */
sepp_nepp 15:88fecbdd191c 57 } motorDir;
sepp_nepp 13:4563244c4071 58
sepp_nepp 15:88fecbdd191c 59 /** \struct MotStatus
sepp_nepp 15:88fecbdd191c 60 * \brief Structure of Motor Status registers
sepp_nepp 15:88fecbdd191c 61 * Is used by Motor Class to hold all Status 'Registers'.
sepp_nepp 15:88fecbdd191c 62 * The structure can be requested to get by Motor.getStatus(). */
sepp_nepp 13:4563244c4071 63 typedef struct {
sepp_nepp 15:88fecbdd191c 64 motorStates state; /**< General state that the moter state machine is in.*/
sepp_nepp 15:88fecbdd191c 65 motorCommands cmd; /**< Command asked to be executed currently by the state machine.*/
sepp_nepp 15:88fecbdd191c 66 motorDir dir; /**< Direction that the motor is asked to run.*/
sepp_nepp 15:88fecbdd191c 67 int32_t NSteps;/**< Number of steps left for the motor to run.
sepp_nepp 15:88fecbdd191c 68 NSteps=0: all steps finsihed; NSteps<0: indicates to run "forever" */
sepp_nepp 15:88fecbdd191c 69 bool TickIsAttached; /**< Indicates if Ticker is attached.
sepp_nepp 15:88fecbdd191c 70 Ticker is automatically attached while Motor is running, or paused;
sepp_nepp 15:88fecbdd191c 71 detaches when finished a run, or stopped. */
sepp_nepp 15:88fecbdd191c 72 /** Helper to set Command, Direction and NSteps in one call. */
sepp_nepp 15:88fecbdd191c 73 void set(motorCommands aCmd, motorDir aDir, int32_t aNSteps);
sepp_nepp 15:88fecbdd191c 74 } MotStatus;
sepp_nepp 15:88fecbdd191c 75
sepp_nepp 15:88fecbdd191c 76 /** ATTENTION UNDER CONSTRUCTION, DO NOT YET USE
sepp_nepp 15:88fecbdd191c 77 * Class of a Four Phase Stepper Motor
sepp_nepp 15:88fecbdd191c 78 *Handles single steps but also running a number of steps, or given amount angle.
sepp_nepp 14:f0667bfc2e98 79 *
sepp_nepp 15:88fecbdd191c 80 *Higher level function rely on ticker and a state-machine evaluating new incoming commands versus the motor state at every tick.
sepp_nepp 15:88fecbdd191c 81 *
sepp_nepp 15:88fecbdd191c 82 *Callbacks can be attached to react to 'end of run' events.
sepp_nepp 14:f0667bfc2e98 83 *
sepp_nepp 15:88fecbdd191c 84 *Lower level functions directly talk to the hardware without ticker.
sepp_nepp 14:f0667bfc2e98 85 */
sepp_nepp 15:88fecbdd191c 86 class Motor
sepp_nepp 15:88fecbdd191c 87 {
sepp_nepp 15:88fecbdd191c 88 public:
sepp_nepp 15:88fecbdd191c 89 /** Class Creator.
sepp_nepp 15:88fecbdd191c 90 * Receives the names of the 4 Digital Pins in an array of PinNames.
sepp_nepp 15:88fecbdd191c 91 * the time in between two steps defaults here to MOTOR_STEP_TIME_DEFAULT_US=5000usec.
sepp_nepp 15:88fecbdd191c 92 * Uses Ticker callback to handle step times.
sepp_nepp 15:88fecbdd191c 93 * Call this creator for example like this:
sepp_nepp 15:88fecbdd191c 94 * @code
sepp_nepp 15:88fecbdd191c 95 * PinName MotPhases[] = {PB_1, PB_15, PB_14, PB_13};
sepp_nepp 15:88fecbdd191c 96 * Motor MotorName(MotPhases);
sepp_nepp 15:88fecbdd191c 97 * @endcode */
sepp_nepp 13:4563244c4071 98 Motor(PinName _MPh[4] );
sepp_nepp 15:88fecbdd191c 99 /** Class Creator.
sepp_nepp 15:88fecbdd191c 100 * receives the names of the 4 Digital Pins.
sepp_nepp 15:88fecbdd191c 101 * Uses Ticker callback to handle step times.
sepp_nepp 15:88fecbdd191c 102 * the time between two steps defaults here to MOTOR_STEP_TIME_DEFAULT_US=5000usec.
sepp_nepp 15:88fecbdd191c 103 * Call this creator for example like this:
sepp_nepp 15:88fecbdd191c 104 * @code
sepp_nepp 15:88fecbdd191c 105 * Motor MotorName(PB_1, PB_15, PB_14, PB_13);
sepp_nepp 15:88fecbdd191c 106 * @endcode */
garphil 9:5983c10d5f8e 107 Motor(PinName _MPh0, PinName _MPh1, PinName _MPh2, PinName _MPh3);
sepp_nepp 15:88fecbdd191c 108 /** Class Creator:
sepp_nepp 15:88fecbdd191c 109 * receives the names of the 4 Digital Pins.
sepp_nepp 15:88fecbdd191c 110 * Uses Ticker callback to handle step times.
sepp_nepp 13:4563244c4071 111 * aStepTime_us is the time in usec between two steps, thats used initially. */
sepp_nepp 13:4563244c4071 112 Motor(PinName _MPh0, PinName _MPh1, PinName _MPh2, PinName _MPh3, uint32_t aStepTime_us);
sepp_nepp 15:88fecbdd191c 113 private:
sepp_nepp 13:4563244c4071 114 // void initialization(PinName _MPh0, PinName _MPh1, PinName _MPh2, PinName _MPh3, uint32_t aStepTime_us);
sepp_nepp 13:4563244c4071 115 void initialization(PinName _MPh[4], uint32_t aStepTime_us);
sepp_nepp 15:88fecbdd191c 116 public:
sepp_nepp 15:88fecbdd191c 117 /** Attach a basic Callback function.
sepp_nepp 15:88fecbdd191c 118 * @param <mIT> Callback function, not member of a class.
sepp_nepp 14:f0667bfc2e98 119 * It is only called when a Run Command reaches it's target.
sepp_nepp 13:4563244c4071 120 * It is not called when the Motor is stopped by calling Stop Function, or any other events.
sepp_nepp 15:88fecbdd191c 121 * Attention: the attached Callback is called within a Ticker Callback.
sepp_nepp 15:88fecbdd191c 122 * Your code you execute in the Callback should be short, must not use waits, or any long routines.
sepp_nepp 15:88fecbdd191c 123 * Do not call any motor run commands in the callback, as it creates conflicting situations.
sepp_nepp 15:88fecbdd191c 124 * Long Callback code may impair this and any other Ticker functions that are running in your application. */
garphil 9:5983c10d5f8e 125 void setMotorCallback(void (*mIT)());
sepp_nepp 15:88fecbdd191c 126
sepp_nepp 14:f0667bfc2e98 127 /** Attaching a Callback function, member of a class.
sepp_nepp 14:f0667bfc2e98 128 * It is only called when a Run Command reaches it's target.
sepp_nepp 15:88fecbdd191c 129 * It is not called when the Motor is stopped by calling Stop Function.
sepp_nepp 15:88fecbdd191c 130 * at note Attention: the attached Callback is called within a Ticker Callback.
sepp_nepp 15:88fecbdd191c 131 * Your code you execute in the Callback should be short, must not use waits, or any long routines.
sepp_nepp 15:88fecbdd191c 132 * Do not call any motor run commands in the callback, as it creates conflicting situations.
sepp_nepp 13:4563244c4071 133 * Long Callback code may impair this and any other Ticker functions that are running in your application.
sepp_nepp 15:88fecbdd191c 134 at note AText
sepp_nepp 15:88fecbdd191c 135 @code
sepp_nepp 15:88fecbdd191c 136 // Class Creator:
sepp_nepp 15:88fecbdd191c 137 AClass::AClass(Class Creation Parameters)
sepp_nepp 15:88fecbdd191c 138 { ...
sepp_nepp 15:88fecbdd191c 139 // Attach callback function:
sepp_nepp 15:88fecbdd191c 140 MotorInstance->setMotorCallback(this, &AClass::CallBackMemberFunction);
sepp_nepp 15:88fecbdd191c 141 ...
sepp_nepp 15:88fecbdd191c 142 }
sepp_nepp 15:88fecbdd191c 143
sepp_nepp 15:88fecbdd191c 144 // Simple callback function
sepp_nepp 15:88fecbdd191c 145 void AClass::CallBackMemberFunction()
sepp_nepp 15:88fecbdd191c 146 {
sepp_nepp 15:88fecbdd191c 147 endMove=true;
sepp_nepp 15:88fecbdd191c 148 }
sepp_nepp 15:88fecbdd191c 149 @endcode
sepp_nepp 15:88fecbdd191c 150 */
sepp_nepp 13:4563244c4071 151 template<typename T>
sepp_nepp 15:88fecbdd191c 152 void setMotorCallback(T *object, void (T::*member)(void)) {
sepp_nepp 15:88fecbdd191c 153 _callback = callback(object,member);
sepp_nepp 15:88fecbdd191c 154 }
sepp_nepp 15:88fecbdd191c 155
sepp_nepp 13:4563244c4071 156 /** Removing the Callback function that may have been attached previously. */
garphil 9:5983c10d5f8e 157 void removeMotorCallback();
sepp_nepp 15:88fecbdd191c 158
sepp_nepp 15:88fecbdd191c 159 /** RunSteps Main Motor Running Function.
sepp_nepp 14:f0667bfc2e98 160 * Runs motor for a number Nsteps>0 of steps; Nsteps<=0 will not be executed.
sepp_nepp 15:88fecbdd191c 161 * Given Direction can be: CLOCKWISE, or COUNTERCLOCKWISE.
sepp_nepp 14:f0667bfc2e98 162 * Call Pause() or Stop() to pause or end the motor running prematurely.
sepp_nepp 14:f0667bfc2e98 163 * While running: Uses ticker; State = first Motor_ON then Motor_RUN; cmd=MOTOR_run.
sepp_nepp 14:f0667bfc2e98 164 * At the end: calls the Callback, stops ticker; State = Motor_OFF. */
sepp_nepp 13:4563244c4071 165 void RunSteps (motorDir direction, uint32_t Nsteps);
sepp_nepp 15:88fecbdd191c 166
sepp_nepp 15:88fecbdd191c 167 /** RunDegrees = Main Motor Running Function.
sepp_nepp 14:f0667bfc2e98 168 * Runs motor for a given angle>0 in degrees; Angles<=0 will not be executed.
sepp_nepp 15:88fecbdd191c 169 * Given Dicection can be: CLOCKWISE, or COUNTERCLOCKWISE.
sepp_nepp 14:f0667bfc2e98 170 * Call Pause() or Stop() to pause or end the motor running prematurely.
sepp_nepp 14:f0667bfc2e98 171 * While running: Uses ticker; State = first Motor_ON then Motor_RUN; cmd=MOTOR_run.
sepp_nepp 14:f0667bfc2e98 172 * At the end: calls the Callback, stops ticker; State = Motor_OFF; cmd=MOTOR_stop then MOTOR_nop. */
sepp_nepp 13:4563244c4071 173 void RunDegrees (motorDir direction, float angle);
garphil 9:5983c10d5f8e 174
sepp_nepp 15:88fecbdd191c 175 /** RunInfinite = Main Motor Running Function:
sepp_nepp 15:88fecbdd191c 176 * Runs motor "unlimited", more precisely it runs 4Billion Steps.
sepp_nepp 15:88fecbdd191c 177 * Dicection can be: CLOCKWISE, or COUNTERCLOCKWISE.
sepp_nepp 14:f0667bfc2e98 178 * While running: Uses ticker; State = first Motor_ON then Motor_RUN; cmd=MOTOR_run.
sepp_nepp 14:f0667bfc2e98 179 * Call Pause() or Stop() to pause or end the motor running.*/
sepp_nepp 13:4563244c4071 180 void RunInfinite (motorDir direction);
sepp_nepp 15:88fecbdd191c 181
sepp_nepp 14:f0667bfc2e98 182 /** Pause puts Motor into Pause state, stepping is suspended.
sepp_nepp 14:f0667bfc2e98 183 * Only effective if Status.cmd=MOTOR_run.
sepp_nepp 15:88fecbdd191c 184 * Retains the number of steps that remain to be run.
sepp_nepp 14:f0667bfc2e98 185 * While pausing: still uses ticker; State = Motor_RUN; cmd=MOTOR_pause.
sepp_nepp 14:f0667bfc2e98 186 * Use Restart(); to continue. */
sepp_nepp 13:4563244c4071 187 void Pause();
sepp_nepp 15:88fecbdd191c 188
sepp_nepp 15:88fecbdd191c 189 /** Restart continues with the paused stepping activity.
sepp_nepp 15:88fecbdd191c 190 * Only effective if Status.cmd=MOTOR_pause, otherwise no action.
sepp_nepp 14:f0667bfc2e98 191 * Status afterwards is same as afterRun commands. */
sepp_nepp 13:4563244c4071 192 void Restart();
sepp_nepp 15:88fecbdd191c 193
sepp_nepp 14:f0667bfc2e98 194 /** Stop completely ends any running/stepping activity.
sepp_nepp 15:88fecbdd191c 195 * Does not call the Callback function.
sepp_nepp 14:f0667bfc2e98 196 * Emits first cmd=MOTOR_stop then cmd=MOTOR_nop.
sepp_nepp 13:4563244c4071 197 * Aftewards: ticker is detached; State = Motor_OFF; */
garphil 4:c009bcd5518c 198 void Stop();
sepp_nepp 15:88fecbdd191c 199 /** Access all motor status registers.
sepp_nepp 14:f0667bfc2e98 200 See documentation of MotStatus Structure.
sepp_nepp 15:88fecbdd191c 201 @return <MotStatus> The full structure of Motor status registers.
sepp_nepp 14:f0667bfc2e98 202 */
sepp_nepp 13:4563244c4071 203 MotStatus getStatus();
sepp_nepp 15:88fecbdd191c 204 public: // All the ticker timing related parameters
sepp_nepp 15:88fecbdd191c 205 /** Get number of Steps per Full turn.
sepp_nepp 15:88fecbdd191c 206 * Defaults to MOTOR_STEPS_FOR_A_TURN = 4096.
sepp_nepp 15:88fecbdd191c 207 * Needed to translate from degrees to number of steps.
sepp_nepp 14:f0667bfc2e98 208 * Old Name was: getCalibration, but not good explicit name. */
sepp_nepp 15:88fecbdd191c 209 uint32_t getStepsFullTurn();
sepp_nepp 15:88fecbdd191c 210
sepp_nepp 15:88fecbdd191c 211 /** Set number of Steps per Full turn.
sepp_nepp 15:88fecbdd191c 212 * Defaults to MOTOR_STEPS_FOR_A_TURN = 4096.
sepp_nepp 15:88fecbdd191c 213 * Needed to translate from degrees to number of steps.
sepp_nepp 14:f0667bfc2e98 214 * Old Name was: setCalibration, but not good explicit name. */
sepp_nepp 13:4563244c4071 215 void setStepsFullTurn(uint32_t StepsFullTurn);
sepp_nepp 15:88fecbdd191c 216
sepp_nepp 15:88fecbdd191c 217 /** Set the time in microseconds between two motor steps.
sepp_nepp 14:f0667bfc2e98 218 * Was previously called setStepTime(), but was not clear which units. */
sepp_nepp 15:88fecbdd191c 219 void setStepTime_us(uint32_t aStepTime_us);
sepp_nepp 15:88fecbdd191c 220
sepp_nepp 15:88fecbdd191c 221 /** Set the time in seconds to get one full turn, rotation of 360°.
sepp_nepp 14:f0667bfc2e98 222 * e.g. setRotationPeriodSec( 20.0 ); then motor will do 360° in 20 seconds.
sepp_nepp 14:f0667bfc2e98 223 * was previously called setSpeed(). */
sepp_nepp 13:4563244c4071 224 void setRotationPeriodSec(float Seconds_Per_Turn) ;
sepp_nepp 15:88fecbdd191c 225
sepp_nepp 15:88fecbdd191c 226 private:
sepp_nepp 13:4563244c4071 227 // all the Ticker and Timing procedures, used to run the motor for a duration
sepp_nepp 13:4563244c4071 228 void StartTick();
garphil 1:9519ac966b79 229 void ProcessMotorStateMachine();
sepp_nepp 13:4563244c4071 230 // The call back function pointer that is called when the Processor
sepp_nepp 15:88fecbdd191c 231 // State Machine reaches its end.
sepp_nepp 13:4563244c4071 232 Callback<void()> _callback;
sepp_nepp 13:4563244c4071 233 void StopTick();
sepp_nepp 15:88fecbdd191c 234 MotStatus Status;
sepp_nepp 13:4563244c4071 235 timestamp_t StepTime_us; // Time in µs for one motor step
sepp_nepp 15:88fecbdd191c 236 Ticker MotorSysTick; // System Timer for Motor
sepp_nepp 15:88fecbdd191c 237
sepp_nepp 15:88fecbdd191c 238 public: // all the low level direct motor HW access, States are immediately reached
sepp_nepp 15:88fecbdd191c 239 /** Turn off all motor Phases, no more current flowing.
sepp_nepp 14:f0667bfc2e98 240 * Equivalent to what previously the function "void Stop();" did . */
sepp_nepp 13:4563244c4071 241 void MotorOFF();
sepp_nepp 15:88fecbdd191c 242 /** Turn on the motor Phase, In the last used phase, memorized in StepPhases
sepp_nepp 14:f0667bfc2e98 243 * Equivalent to what previously the function "void Start();" did. */
sepp_nepp 13:4563244c4071 244 void MotorON();
sepp_nepp 15:88fecbdd191c 245 /** Motor phases turned on and put to Zero Position. */
sepp_nepp 13:4563244c4071 246 void MotorZero();
sepp_nepp 13:4563244c4071 247 void TestMotor();
sepp_nepp 15:88fecbdd191c 248 private:
sepp_nepp 14:f0667bfc2e98 249 /** Motor advances one step rotating in direction of variable direction. */
sepp_nepp 13:4563244c4071 250 void StepOnce();
sepp_nepp 14:f0667bfc2e98 251 /** Motor advances one step rotating left. */
sepp_nepp 13:4563244c4071 252 void StepLeft();
sepp_nepp 14:f0667bfc2e98 253 /** Motor advances one step rotating right. */
sepp_nepp 13:4563244c4071 254 void StepRight();
sepp_nepp 14:f0667bfc2e98 255 /** Engage Motor Phases according to MotorIndex. */
sepp_nepp 13:4563244c4071 256 void SetPhases(); // Engage Motor Phases according to StepPhase
sepp_nepp 13:4563244c4071 257
sepp_nepp 13:4563244c4071 258 DigitalOut *MPh[4]; // Digital outputs, one per phase
sepp_nepp 13:4563244c4071 259 int StepPhase; // Motor Phase Variable, counts up and down with every step
sepp_nepp 13:4563244c4071 260 uint32_t Steps_FullTurn;// Number of step for a complete turn
sepp_nepp 15:88fecbdd191c 261
sepp_nepp 15:88fecbdd191c 262 private: /* Deprecated, unused members of the class
sepp_nepp 13:4563244c4071 263 void SetDirection(motorDir direction); // direction is set anyway by all the other commands
garphil 10:1df5a7a265e8 264 Timer tuneTimings;
sepp_nepp 13:4563244c4071 265 uint32_t last;
sepp_nepp 15:88fecbdd191c 266 */
garphil 5:9886fd337a4b 267 };
garphil 5:9886fd337a4b 268
garphil 5:9886fd337a4b 269 #endif