-
Notifications
You must be signed in to change notification settings - Fork 9
/
Servo.cpp
126 lines (104 loc) · 3.32 KB
/
Servo.cpp
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
#include "Servo.h"
Servo::Servo(PinName pin) : m_DigitalOut(pin), m_Thread(osPriorityAboveNormal1)
{
// set default motion profile
setMaxVelocity();
setMaxAcceleration();
// start thread
m_Thread.start(callback(this, &Servo::threadTask));
}
Servo::~Servo()
{
m_Ticker.detach();
m_Timeout.detach();
m_Thread.terminate();
}
void Servo::calibratePulseMinMax(float pulse_min, float pulse_max)
{
// set minimal and maximal pulse width
m_pulse_min = pulse_min;
m_pulse_max = pulse_max;
}
void Servo::setMaxVelocity(float velocity)
{
// convert velocity from calibrated normalised pulse width to normalised pulse width
velocity *= (m_pulse_max - m_pulse_min);
m_Motion.setProfileVelocity(velocity);
}
void Servo::setMaxAcceleration(float acceleration)
{
// convert acceleration from calibrated normalised pulse width to normalised pulse width
acceleration *= (m_pulse_max - m_pulse_min);
m_Motion.setProfileAcceleration(acceleration);
m_Motion.setProfileDeceleration(acceleration);
}
void Servo::setNormalisedPulseWidth(float pulse)
{
// after calibrating the mapping from setNormalisedPulseWidth() is (0, 1) -> (pulse_min, pulse_max)
m_pulse = calculateNormalisedPulseWidth(pulse);
}
void Servo::enable(float pulse)
{
m_enabled = true;
// set pulse width when enabled
m_pulse = calculateNormalisedPulseWidth(pulse);
m_Motion.setPosition(m_pulse);
// attach sendThreadFlag() to ticker so that sendThreadFlag() is called periodically, which signals the thread to execute
m_Ticker.attach(callback(this, &Servo::sendThreadFlag), std::chrono::microseconds{PERIOD_MUS});
}
void Servo::disable()
{
m_enabled = false;
// detach ticker and timeout
m_Ticker.detach();
m_Timeout.detach();
}
bool Servo::isEnabled() const
{
return m_enabled;
}
float Servo::calculateNormalisedPulseWidth(float pulse)
{
// it is assumed that after the calibration m_pulse_min != 0.0f and if so
// we constrain the pulse to the range (0.0f, 1.0f)
if (m_pulse_min != 0.0f)
pulse = (pulse > 1.0f) ? 1.0f : (pulse < 0.0f) ? 0.0f : pulse;
return constrainPulse((m_pulse_max - m_pulse_min) * pulse + m_pulse_min);
}
void Servo::threadTask()
{
while (true) {
ThisThread::flags_wait_any(m_ThreadFlag);
if (isEnabled()) {
// increment to position
m_Motion.incrementToPosition(m_pulse, TS);
// convert to pulse width
const uint16_t pulse_mus = static_cast<uint16_t>(m_Motion.getPosition() * static_cast<float>(PERIOD_MUS));
// enable digital output and attach disableDigitalOutput() to timeout for soft PWM
enableDigitalOutput();
m_Timeout.attach(callback(this, &Servo::disableDigitalOutput), std::chrono::microseconds{pulse_mus});
}
}
}
void Servo::enableDigitalOutput()
{
// set the digital output to high
m_DigitalOut = 1;
}
void Servo::disableDigitalOutput()
{
// set the digital output to low
m_DigitalOut = 0;
}
void Servo::sendThreadFlag()
{
// set the thread flag to trigger the thread task
m_Thread.flags_set(m_ThreadFlag);
}
float Servo::constrainPulse(float pulse) const
{
// constrain pulse to range (PWM_MIN, PWM_MAX)
return (pulse > PWM_MAX) ? PWM_MAX :
(pulse < PWM_MIN) ? PWM_MIN :
pulse;
}