blob: 6b3b4b7ec02698e1c16fffa74bd850f79a2f16e3 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. All Rights Reserved.
3 */
4/* Open Source Software - may be modified and shared by FRC teams. The code */
5/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
6/*----------------------------------------------------------------------------*/
7
8#include "PWM.h"
9
10#include "Resource.h"
11#include "Utility.h"
12#include "WPIErrors.h"
13#include "HAL/HAL.hpp"
14
15#include <sstream>
16
17constexpr float PWM::kDefaultPwmPeriod;
18constexpr float PWM::kDefaultPwmCenter;
19const int32_t PWM::kDefaultPwmStepsDown;
20const int32_t PWM::kPwmDisabled;
21
22/**
23 * Allocate a PWM given a channel number.
24 *
25 * Checks channel value range and allocates the appropriate channel.
26 * The allocation is only done to help users ensure that they don't double
27 * assign channels.
28 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP
29 * port
30 */
31PWM::PWM(uint32_t channel) {
32 std::stringstream buf;
33
34 if (!CheckPWMChannel(channel)) {
35 buf << "PWM Channel " << channel;
36 wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf.str());
37 return;
38 }
39
40 int32_t status = 0;
41 allocatePWMChannel(m_pwm_ports[channel], &status);
42 wpi_setErrorWithContext(status, getHALErrorMessage(status));
43
44 m_channel = channel;
45
46 setPWM(m_pwm_ports[m_channel], kPwmDisabled, &status);
47 wpi_setErrorWithContext(status, getHALErrorMessage(status));
48
49 m_eliminateDeadband = false;
50
51 HALReport(HALUsageReporting::kResourceType_PWM, channel);
52}
53
54/**
55 * Free the PWM channel.
56 *
57 * Free the resource associated with the PWM channel and set the value to 0.
58 */
59PWM::~PWM() {
60 int32_t status = 0;
61
62 setPWM(m_pwm_ports[m_channel], kPwmDisabled, &status);
63 wpi_setErrorWithContext(status, getHALErrorMessage(status));
64
65 freePWMChannel(m_pwm_ports[m_channel], &status);
66 wpi_setErrorWithContext(status, getHALErrorMessage(status));
67
68 if (m_table != nullptr) m_table->RemoveTableListener(this);
69}
70
71/**
72 * Optionally eliminate the deadband from a speed controller.
73 * @param eliminateDeadband If true, set the motor curve on the Jaguar to
74 * eliminate
75 * the deadband in the middle of the range. Otherwise, keep the full range
76 * without
77 * modifying any values.
78 */
79void PWM::EnableDeadbandElimination(bool eliminateDeadband) {
80 if (StatusIsFatal()) return;
81 m_eliminateDeadband = eliminateDeadband;
82}
83
84/**
85 * Set the bounds on the PWM values.
86 * This sets the bounds on the PWM values for a particular each type of
87 * controller. The values
88 * determine the upper and lower speeds as well as the deadband bracket.
89 * @param max The Minimum pwm value
90 * @param deadbandMax The high end of the deadband range
91 * @param center The center speed (off)
92 * @param deadbandMin The low end of the deadband range
93 * @param min The minimum pwm value
94 */
95void PWM::SetBounds(int32_t max, int32_t deadbandMax, int32_t center,
96 int32_t deadbandMin, int32_t min) {
97 if (StatusIsFatal()) return;
98 m_maxPwm = max;
99 m_deadbandMaxPwm = deadbandMax;
100 m_centerPwm = center;
101 m_deadbandMinPwm = deadbandMin;
102 m_minPwm = min;
103}
104
105/**
106 * Set the bounds on the PWM pulse widths.
107 * This sets the bounds on the PWM values for a particular type of controller.
108 * The values
109 * determine the upper and lower speeds as well as the deadband bracket.
110 * @param max The max PWM pulse width in ms
111 * @param deadbandMax The high end of the deadband range pulse width in ms
112 * @param center The center (off) pulse width in ms
113 * @param deadbandMin The low end of the deadband pulse width in ms
114 * @param min The minimum pulse width in ms
115 */
116void PWM::SetBounds(double max, double deadbandMax, double center,
117 double deadbandMin, double min) {
118 // calculate the loop time in milliseconds
119 int32_t status = 0;
120 double loopTime =
121 getLoopTiming(&status) / (kSystemClockTicksPerMicrosecond * 1e3);
122 wpi_setErrorWithContext(status, getHALErrorMessage(status));
123
124 if (StatusIsFatal()) return;
125
126 m_maxPwm = (int32_t)((max - kDefaultPwmCenter) / loopTime +
127 kDefaultPwmStepsDown - 1);
128 m_deadbandMaxPwm = (int32_t)((deadbandMax - kDefaultPwmCenter) / loopTime +
129 kDefaultPwmStepsDown - 1);
130 m_centerPwm = (int32_t)((center - kDefaultPwmCenter) / loopTime +
131 kDefaultPwmStepsDown - 1);
132 m_deadbandMinPwm = (int32_t)((deadbandMin - kDefaultPwmCenter) / loopTime +
133 kDefaultPwmStepsDown - 1);
134 m_minPwm = (int32_t)((min - kDefaultPwmCenter) / loopTime +
135 kDefaultPwmStepsDown - 1);
136}
137
138/**
139 * Set the PWM value based on a position.
140 *
141 * This is intended to be used by servos.
142 *
143 * @pre SetMaxPositivePwm() called.
144 * @pre SetMinNegativePwm() called.
145 *
146 * @param pos The position to set the servo between 0.0 and 1.0.
147 */
148void PWM::SetPosition(float pos) {
149 if (StatusIsFatal()) return;
150 if (pos < 0.0) {
151 pos = 0.0;
152 } else if (pos > 1.0) {
153 pos = 1.0;
154 }
155
156 // note, need to perform the multiplication below as floating point before
157 // converting to int
158 unsigned short rawValue =
159 (int32_t)((pos * (float)GetFullRangeScaleFactor()) + GetMinNegativePwm());
160 // printf("MinNegPWM: %d FullRangeScaleFactor: %d Raw value: %5d Input
161 //value: %4.4f\n", GetMinNegativePwm(), GetFullRangeScaleFactor(), rawValue,
162 //pos);
163
164 // wpi_assert((rawValue >= GetMinNegativePwm()) && (rawValue <=
165 //GetMaxPositivePwm()));
166 wpi_assert(rawValue != kPwmDisabled);
167
168 // send the computed pwm value to the FPGA
169 SetRaw((unsigned short)rawValue);
170}
171
172/**
173 * Get the PWM value in terms of a position.
174 *
175 * This is intended to be used by servos.
176 *
177 * @pre SetMaxPositivePwm() called.
178 * @pre SetMinNegativePwm() called.
179 *
180 * @return The position the servo is set to between 0.0 and 1.0.
181 */
182float PWM::GetPosition() const {
183 if (StatusIsFatal()) return 0.0;
184 int32_t value = GetRaw();
185 if (value < GetMinNegativePwm()) {
186 return 0.0;
187 } else if (value > GetMaxPositivePwm()) {
188 return 1.0;
189 } else {
190 return (float)(value - GetMinNegativePwm()) /
191 (float)GetFullRangeScaleFactor();
192 }
193}
194
195/**
196 * Set the PWM value based on a speed.
197 *
198 * This is intended to be used by speed controllers.
199 *
200 * @pre SetMaxPositivePwm() called.
201 * @pre SetMinPositivePwm() called.
202 * @pre SetCenterPwm() called.
203 * @pre SetMaxNegativePwm() called.
204 * @pre SetMinNegativePwm() called.
205 *
206 * @param speed The speed to set the speed controller between -1.0 and 1.0.
207 */
208void PWM::SetSpeed(float speed) {
209 if (StatusIsFatal()) return;
210 // clamp speed to be in the range 1.0 >= speed >= -1.0
211 if (speed < -1.0) {
212 speed = -1.0;
213 } else if (speed > 1.0) {
214 speed = 1.0;
215 }
216
217 // calculate the desired output pwm value by scaling the speed appropriately
218 int32_t rawValue;
219 if (speed == 0.0) {
220 rawValue = GetCenterPwm();
221 } else if (speed > 0.0) {
222 rawValue = (int32_t)(speed * ((float)GetPositiveScaleFactor()) +
223 ((float)GetMinPositivePwm()) + 0.5);
224 } else {
225 rawValue = (int32_t)(speed * ((float)GetNegativeScaleFactor()) +
226 ((float)GetMaxNegativePwm()) + 0.5);
227 }
228
229 // the above should result in a pwm_value in the valid range
230 wpi_assert((rawValue >= GetMinNegativePwm()) &&
231 (rawValue <= GetMaxPositivePwm()));
232 wpi_assert(rawValue != kPwmDisabled);
233
234 // send the computed pwm value to the FPGA
235 SetRaw(rawValue);
236}
237
238/**
239 * Get the PWM value in terms of speed.
240 *
241 * This is intended to be used by speed controllers.
242 *
243 * @pre SetMaxPositivePwm() called.
244 * @pre SetMinPositivePwm() called.
245 * @pre SetMaxNegativePwm() called.
246 * @pre SetMinNegativePwm() called.
247 *
248 * @return The most recently set speed between -1.0 and 1.0.
249 */
250float PWM::GetSpeed() const {
251 if (StatusIsFatal()) return 0.0;
252 int32_t value = GetRaw();
253 if (value == PWM::kPwmDisabled) {
254 return 0.0;
255 } else if (value > GetMaxPositivePwm()) {
256 return 1.0;
257 } else if (value < GetMinNegativePwm()) {
258 return -1.0;
259 } else if (value > GetMinPositivePwm()) {
260 return (float)(value - GetMinPositivePwm()) /
261 (float)GetPositiveScaleFactor();
262 } else if (value < GetMaxNegativePwm()) {
263 return (float)(value - GetMaxNegativePwm()) /
264 (float)GetNegativeScaleFactor();
265 } else {
266 return 0.0;
267 }
268}
269
270/**
271 * Set the PWM value directly to the hardware.
272 *
273 * Write a raw value to a PWM channel.
274 *
275 * @param value Raw PWM value.
276 */
277void PWM::SetRaw(unsigned short value) {
278 if (StatusIsFatal()) return;
279
280 int32_t status = 0;
281 setPWM(m_pwm_ports[m_channel], value, &status);
282 wpi_setErrorWithContext(status, getHALErrorMessage(status));
283}
284
285/**
286 * Get the PWM value directly from the hardware.
287 *
288 * Read a raw value from a PWM channel.
289 *
290 * @return Raw PWM control value.
291 */
292unsigned short PWM::GetRaw() const {
293 if (StatusIsFatal()) return 0;
294
295 int32_t status = 0;
296 unsigned short value = getPWM(m_pwm_ports[m_channel], &status);
297 wpi_setErrorWithContext(status, getHALErrorMessage(status));
298
299 return value;
300}
301
302/**
303 * Slow down the PWM signal for old devices.
304 *
305 * @param mult The period multiplier to apply to this channel
306 */
307void PWM::SetPeriodMultiplier(PeriodMultiplier mult) {
308 if (StatusIsFatal()) return;
309
310 int32_t status = 0;
311
312 switch (mult) {
313 case kPeriodMultiplier_4X:
314 setPWMPeriodScale(m_pwm_ports[m_channel], 3,
315 &status); // Squelch 3 out of 4 outputs
316 break;
317 case kPeriodMultiplier_2X:
318 setPWMPeriodScale(m_pwm_ports[m_channel], 1,
319 &status); // Squelch 1 out of 2 outputs
320 break;
321 case kPeriodMultiplier_1X:
322 setPWMPeriodScale(m_pwm_ports[m_channel], 0,
323 &status); // Don't squelch any outputs
324 break;
325 default:
326 wpi_assert(false);
327 }
328
329 wpi_setErrorWithContext(status, getHALErrorMessage(status));
330}
331
332void PWM::SetZeroLatch() {
333 if (StatusIsFatal()) return;
334
335 int32_t status = 0;
336
337 latchPWMZero(m_pwm_ports[m_channel], &status);
338 wpi_setErrorWithContext(status, getHALErrorMessage(status));
339}
340
341void PWM::ValueChanged(ITable* source, llvm::StringRef key,
342 std::shared_ptr<nt::Value> value, bool isNew) {
343 if (!value->IsDouble()) return;
344 SetSpeed(value->GetDouble());
345}
346
347void PWM::UpdateTable() {
348 if (m_table != nullptr) {
349 m_table->PutNumber("Value", GetSpeed());
350 }
351}
352
353void PWM::StartLiveWindowMode() {
354 SetSpeed(0);
355 if (m_table != nullptr) {
356 m_table->AddTableListener("Value", this, true);
357 }
358}
359
360void PWM::StopLiveWindowMode() {
361 SetSpeed(0);
362 if (m_table != nullptr) {
363 m_table->RemoveTableListener(this);
364 }
365}
366
367std::string PWM::GetSmartDashboardType() const { return "Speed Controller"; }
368
369void PWM::InitTable(std::shared_ptr<ITable> subTable) {
370 m_table = subTable;
371 UpdateTable();
372}
373
374std::shared_ptr<ITable> PWM::GetTable() const { return m_table; }