-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathdcmotor.hpp
266 lines (218 loc) · 10.9 KB
/
dcmotor.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2017-2023 plan44.ch / Lukas Zeller, Zurich, Switzerland
//
// Author: Lukas Zeller <luz@plan44.ch>
//
// This file is part of p44utils.
//
// p44ayabd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// p44ayabd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with p44ayabd. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __p44wiperd__dcmotordriver__
#define __p44wiperd__dcmotordriver__
#include "p44utils_main.hpp"
#if ENABLE_P44SCRIPT && !defined(ENABLE_DCMOTOR_SCRIPT_FUNCS)
#define ENABLE_DCMOTOR_SCRIPT_FUNCS 1
#endif
#if ENABLE_DCMOTOR_SCRIPT_FUNCS
#include "p44script.hpp"
#endif
#include "serialcomm.hpp"
#include "digitalio.hpp"
#include "analogio.hpp"
using namespace std;
namespace p44 {
class DcMotorDriverError : public Error
{
public:
enum {
OK,
overcurrentStop,
endswitchStop,
timedStop,
numErrorCodes
};
typedef uint16_t ErrorCodes;
static const char *domain() { return "DCMotorDriver"; }
virtual const char *getErrorDomain() const P44_OVERRIDE { return DcMotorDriverError::domain(); };
DcMotorDriverError(ErrorCodes aError) : Error(ErrorCode(aError)) {};
#if ENABLE_NAMED_ERRORS
protected:
virtual const char* errorName() const P44_OVERRIDE { return errNames[getErrorCode()]; };
private:
static constexpr const char* const errNames[numErrorCodes] = {
"OK",
"overcurrentStop",
"endswitchStop",
"timedStop",
};
#endif // ENABLE_NAMED_ERRORS
};
class DcMotorDriver;
namespace P44Script { class DcMotorStatusObj; }
typedef boost::function<void (double aCurrentPower, int aDirection, ErrorPtr aError)> DCMotorStatusCB;
typedef boost::intrusive_ptr<DcMotorDriver> DcMotorDriverPtr;
class DcMotorDriver :
public P44LoggingObj
#if ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
, public P44Script::EventSource
#endif
{
typedef P44Obj inherited;
#if ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
friend class P44Script::DcMotorStatusObj;
P44Script::EventHandler mCurrentHandler;
#endif
AnalogIoPtr mPwmOutput;
DigitalIoPtr mCWdirectionOutput;
DigitalIoPtr mCCWdirectionOutput;
double mPowerOffset; ///< value to add to power output value for any power>0
double mPowerScaling; ///< multiplicator to use on 0..100 power value to get output power
int mCurrentDirection;
double mCurrentPower;
ErrorPtr mStopCause;
DCMotorStatusCB mRampDoneCB; ///< called when ramp is done or motor was stopped by endswitch/overcurrent
MLTicket mSequenceTicket;
AnalogIoPtr mCurrentSensor;
MLMicroSeconds mSampleInterval; ///< current sampling interval
double mStopCurrent; ///< current limit that will stop motor
MLMicroSeconds mCurrentLimiterHoldoffTime; ///< delay after applying power until current limiter kicks in (to allow surges at powerup)
double mMaxStartCurrent; ///< if >0, maximum allowd current during current limiter holdoff time
MLMicroSeconds mStartMonitoring;
DCMotorStatusCB mStoppedCB; ///< called when motor was stopped by endswitch/overcurrent
DigitalIoPtr mPositiveEndInput;
DigitalIoPtr mNegativeEndInput;
#if ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
P44Script::EventHandler mEndSwitchHandler;
#endif
public:
/// Create a motor controller
/// @param aPWMOutput a 0..100 Analog output controlling the PWM signal for the DC motor
/// @param aCWDirectionOutput a digital output enabling clockwise motor operation.
/// If no CCW output is set, this is assumed to just switch the direction (1=CW, 0=CCW)
/// If no CW output is set, this is assumed to be a unidirectional motor which is only controlled via the PWM
/// @param aCCWDirectionOutput a digital output enabling counter clockwise motor operation.
/// If this is set, CW and CCW are assumed to each control one of the half bridges,
/// so using CCW!=CW will drive the motor, and CCW==CW will brake it
DcMotorDriver(AnalogIoPtr aPWMOutput, DigitalIoPtr aCWDirectionOutput = NULL, DigitalIoPtr aCCWDirectionOutput = NULL);
virtual ~DcMotorDriver();
/// Set output scaling and offset (in case it is not 0..100)
/// @param aPowerscaling the multiplicator to use on 0..100 power input requested by rampToPower()
/// @param aPowerOffset the offset to add to any non-zero power output value
void setOutputParams(double aPowerScaling, double aPowerOffset = 0);
/// set stop callback
/// @param aStopCB this will be called whenever the motor stops by itself.
/// If no error is passed, the stop is just because a ramp/sequence has completed
/// An error indicates a unexpeced stop (end switch or current limit)
/// @note the stop callback will receive the power and direction values present BEFORE the motor stopped;
/// but actual power will be 0 (as we have stopped)
void setStopCallback(DCMotorStatusCB aStoppedCB);
/// Enable current monitoring for stopping at mechanical endpoints and/or obstacles (prevent motor overload)
/// @param aCurrentSensor a analog input sensing the current used by the motor to allow stopping on overcurrent.
/// The current limiter will use the processedValue() of the analog input, so averaged current can be used to eliminate spikes
/// @param aSampleInterval the sample interval for the current
void setCurrentSensor(AnalogIoPtr aCurrentSensor, MLMicroSeconds aSampleInterval);
/// @param aStopCurrent sensor value that will stop the motor
/// @param aHoldOffTime how long to suspend current limiting after beginning a powerup ramp
/// @param aMaxStartCurrent max current allowed during aHoldOffTime, 0 = no limit
void setCurrentLimits(double aStopCurrent, MLMicroSeconds aHoldOffTime = 0, double aMaxStartCurrent = 0);
/// Enable monitoring for end switches
/// @param aPositiveEnd a digital input which signals motor at the positive end of its movement
/// @param aNegativeEnd a digital input which signals motor at the negative end of its movement
/// @param aPollInterval interval for polling the input (only if needed, that is when HW does not have edge detection anyway). 0 = default poll strategy
void setEndSwitches(DigitalIoPtr aPositiveEnd, DigitalIoPtr aNegativeEnd = NULL, MLMicroSeconds aDebounceTime = 0, MLMicroSeconds aPollInterval = 0);
/// ramp motor from current power to another power
/// @param aPower 0..100 new brake or drive power to apply
/// @param aDirection driving direction: 1 = CW, -1 = CCW, 0 = hold/brake
/// @param aRampTime number of seconds for running this ramp.
/// If negative, this specifies the time for a full scale (0..100 or vice versa) power change, actual time will
/// be proportional to power range actually run trough.
/// Note that ramping from one aDirection to another will execute two separate ramps in sequence
/// @param aRampExp ramp exponent (0=linear, + or - = logarithmic bulging up or down)
/// @param aRampDoneCB will be called at end of ramp
/// @note aRampDoneCB callback will receive the current power and direction as active at the end of the ramp
/// (if the motor runs into a stop condition during ramp, power and direction will be 0)
void rampToPower(double aPower, int aDirection, double aRampTime = 0, double aRampExp = 0, DCMotorStatusCB aRampDoneCB = NoOP);
/// stop immediately, no braking
void stop();
/// stop ramps and sequences, but do not turn off motor
void stopSequences();
/// run sequence
typedef struct {
double power; ///< power to ramp to, negative = step list terminator
int direction; ///< new direction
double rampTime; ///< ramp speed
double rampExp; ///< ramp exponent (0=linear, + or - = logarithmic bulging up or down)
double runTime; ///< time to run
} SequenceStep;
typedef std::list<SequenceStep> SequenceStepList;
/// ramp motor from current power to another power
/// @param aSteps list of sequence steps
/// @param aSequenceDoneCB will be called at end of ramp
void runSequence(SequenceStepList aSteps, DCMotorStatusCB aSequenceDoneCB = NoOP);
#if ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
/// get a motor status object. This is also what is sent to event sinks
P44Script::ScriptObjPtr getStatusObj();
#endif
private:
void setPower(double aPower, int aDirection);
void setDirection(int aDirection);
void rampStep(double aStartPower, double aTargetPower, int aNumSteps, int aStepNo , double aRampExp);
void sequenceStepDone(SequenceStepList aSteps, DCMotorStatusCB aSequenceDoneCB, ErrorPtr aError);
void checkCurrent();
#if ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
void endSwitchEvent(P44Script::ScriptObjPtr aEvent, P44Script::EventSource &aSource);
#endif
void endSwitch(bool aPositiveEnd, bool aNewState);
void autoStopped(double aPower, int aDirection, ErrorPtr aError);
void motorStatusUpdate(ErrorPtr aStopCause);
};
#if ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
namespace P44Script {
class DcMotorObj;
/// represents a DC motor state
class DcMotorStatusObj : public ObjectValue
{
typedef ObjectValue inherited;
DcMotorDriverPtr mDcMotorDriver;
public:
DcMotorStatusObj(DcMotorDriverPtr aDcMotorDriver);
virtual void deactivate() P44_OVERRIDE;
virtual string getAnnotation() const P44_OVERRIDE;
virtual TypeInfo getTypeInfo() const P44_OVERRIDE;
virtual bool isEventSource() const P44_OVERRIDE;
virtual void registerForFilteredEvents(EventSink* aEventSink, intptr_t aRegId = 0) P44_OVERRIDE;
};
/// represents a DC motor
/// @note is an event source, but does not expose it directly, only via DcMotorEventObjs
class DcMotorObj : public StructuredLookupObject, public EventSource
{
typedef StructuredLookupObject inherited;
DcMotorDriverPtr mDCMotor;
public:
DcMotorObj(DcMotorDriverPtr aDCMotor);
virtual string getAnnotation() const P44_OVERRIDE { return "DC motor"; };
DcMotorDriverPtr dcMotor() { return mDCMotor; }
};
/// represents the global objects related to DC motors
class DcMotorLookup : public BuiltInMemberLookup
{
typedef BuiltInMemberLookup inherited;
public:
DcMotorLookup();
};
}
#endif // ENABLE_DCMOTOR_SCRIPT_FUNCS && ENABLE_P44SCRIPT
} // namespace p44
#endif /* defined(__p44wiperd__dcmotordriver__) */