blob: a1938427b29c4e4b7eebf442ea729c0a05aa13f6 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "HAL/PWM.h"
9
10#include <cmath>
11
12#include "ConstantsInternal.h"
13#include "DigitalInternal.h"
14#include "HAL/handles/HandlesInternal.h"
15#include "PortsInternal.h"
16
17using namespace hal;
18
19static inline int32_t GetMaxPositivePwm(DigitalPort* port) {
20 return port->maxPwm;
21}
22static inline int32_t GetMinPositivePwm(DigitalPort* port) {
23 return port->eliminateDeadband ? port->deadbandMaxPwm : port->centerPwm + 1;
24}
25static inline int32_t GetCenterPwm(DigitalPort* port) {
26 return port->centerPwm;
27}
28static inline int32_t GetMaxNegativePwm(DigitalPort* port) {
29 return port->eliminateDeadband ? port->deadbandMinPwm : port->centerPwm - 1;
30}
31static inline int32_t GetMinNegativePwm(DigitalPort* port) {
32 return port->minPwm;
33}
34static inline int32_t GetPositiveScaleFactor(DigitalPort* port) {
35 return GetMaxPositivePwm(port) - GetMinPositivePwm(port);
36} ///< The scale for positive speeds.
37static inline int32_t GetNegativeScaleFactor(DigitalPort* port) {
38 return GetMaxNegativePwm(port) - GetMinNegativePwm(port);
39} ///< The scale for negative speeds.
40static inline int32_t GetFullRangeScaleFactor(DigitalPort* port) {
41 return GetMaxPositivePwm(port) - GetMinNegativePwm(port);
42} ///< The scale for positions.
43
44extern "C" {
45
46HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle,
47 int32_t* status) {
48 initializeDigital(status);
49
50 if (*status != 0) return HAL_kInvalidHandle;
51
52 int16_t channel = getPortHandleChannel(portHandle);
53 if (channel == InvalidHandleIndex || channel >= kNumPWMChannels) {
54 *status = PARAMETER_OUT_OF_RANGE;
55 return HAL_kInvalidHandle;
56 }
57
58 uint8_t origChannel = static_cast<uint8_t>(channel);
59
60 if (origChannel < kNumPWMHeaders) {
61 channel += kNumDigitalChannels; // remap Headers to end of allocations
62 } else {
63 channel = remapMXPPWMChannel(channel) + 10; // remap MXP to proper channel
64 }
65
66 auto handle =
67 digitalChannelHandles.Allocate(channel, HAL_HandleEnum::PWM, status);
68
69 if (*status != 0)
70 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
71
72 auto port = digitalChannelHandles.Get(handle, HAL_HandleEnum::PWM);
73 if (port == nullptr) { // would only occur on thread issue.
74 *status = HAL_HANDLE_ERROR;
75 return HAL_kInvalidHandle;
76 }
77
78 port->channel = origChannel;
79
80 int32_t bitToSet = 1 << remapMXPPWMChannel(port->channel);
81 uint16_t specialFunctions =
82 digitalSystem->readEnableMXPSpecialFunction(status);
83 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions | bitToSet,
84 status);
85
86 return handle;
87}
88void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
89 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
90 if (port == nullptr) {
91 *status = HAL_HANDLE_ERROR;
92 return;
93 }
94
95 if (port->channel > tPWM::kNumHdrRegisters - 1) {
96 int32_t bitToUnset = 1 << remapMXPPWMChannel(port->channel);
97 uint16_t specialFunctions =
98 digitalSystem->readEnableMXPSpecialFunction(status);
99 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToUnset,
100 status);
101 }
102
103 digitalChannelHandles.Free(pwmPortHandle, HAL_HandleEnum::PWM);
104}
105
106HAL_Bool HAL_CheckPWMChannel(int32_t channel) {
107 return channel < kNumPWMChannels && channel >= 0;
108}
109
110void HAL_SetPWMConfig(HAL_DigitalHandle pwmPortHandle, double max,
111 double deadbandMax, double center, double deadbandMin,
112 double min, int32_t* status) {
113 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
114 if (port == nullptr) {
115 *status = HAL_HANDLE_ERROR;
116 return;
117 }
118
119 // calculate the loop time in milliseconds
120 double loopTime =
121 HAL_GetLoopTiming(status) / (kSystemClockTicksPerMicrosecond * 1e3);
122 if (*status != 0) return;
123
124 int32_t maxPwm = static_cast<int32_t>((max - kDefaultPwmCenter) / loopTime +
125 kDefaultPwmStepsDown - 1);
126 int32_t deadbandMaxPwm = static_cast<int32_t>(
127 (deadbandMax - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
128 int32_t centerPwm = static_cast<int32_t>(
129 (center - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
130 int32_t deadbandMinPwm = static_cast<int32_t>(
131 (deadbandMin - kDefaultPwmCenter) / loopTime + kDefaultPwmStepsDown - 1);
132 int32_t minPwm = static_cast<int32_t>((min - kDefaultPwmCenter) / loopTime +
133 kDefaultPwmStepsDown - 1);
134
135 port->maxPwm = maxPwm;
136 port->deadbandMaxPwm = deadbandMaxPwm;
137 port->deadbandMinPwm = deadbandMinPwm;
138 port->centerPwm = centerPwm;
139 port->minPwm = minPwm;
140 port->configSet = true;
141}
142
143void HAL_SetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t maxPwm,
144 int32_t deadbandMaxPwm, int32_t centerPwm,
145 int32_t deadbandMinPwm, int32_t minPwm,
146 int32_t* status) {
147 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
148 if (port == nullptr) {
149 *status = HAL_HANDLE_ERROR;
150 return;
151 }
152
153 port->maxPwm = maxPwm;
154 port->deadbandMaxPwm = deadbandMaxPwm;
155 port->deadbandMinPwm = deadbandMinPwm;
156 port->centerPwm = centerPwm;
157 port->minPwm = minPwm;
158}
159
160void HAL_GetPWMConfigRaw(HAL_DigitalHandle pwmPortHandle, int32_t* maxPwm,
161 int32_t* deadbandMaxPwm, int32_t* centerPwm,
162 int32_t* deadbandMinPwm, int32_t* minPwm,
163 int32_t* status) {
164 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
165 if (port == nullptr) {
166 *status = HAL_HANDLE_ERROR;
167 return;
168 }
169 *maxPwm = port->maxPwm;
170 *deadbandMaxPwm = port->deadbandMaxPwm;
171 *deadbandMinPwm = port->deadbandMinPwm;
172 *centerPwm = port->centerPwm;
173 *minPwm = port->minPwm;
174}
175
176void HAL_SetPWMEliminateDeadband(HAL_DigitalHandle pwmPortHandle,
177 HAL_Bool eliminateDeadband, int32_t* status) {
178 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
179 if (port == nullptr) {
180 *status = HAL_HANDLE_ERROR;
181 return;
182 }
183 port->eliminateDeadband = eliminateDeadband;
184}
185
186HAL_Bool HAL_GetPWMEliminateDeadband(HAL_DigitalHandle pwmPortHandle,
187 int32_t* status) {
188 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
189 if (port == nullptr) {
190 *status = HAL_HANDLE_ERROR;
191 return false;
192 }
193 return port->eliminateDeadband;
194}
195
196/**
197 * Set a PWM channel to the desired value. The values range from 0 to 255 and
198 * the period is controlled
199 * by the PWM Period and MinHigh registers.
200 *
201 * @param channel The PWM channel to set.
202 * @param value The PWM value to set.
203 */
204void HAL_SetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t value,
205 int32_t* status) {
206 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
207 if (port == nullptr) {
208 *status = HAL_HANDLE_ERROR;
209 return;
210 }
211
212 if (port->channel < tPWM::kNumHdrRegisters) {
213 pwmSystem->writeHdr(port->channel, value, status);
214 } else {
215 pwmSystem->writeMXP(port->channel - tPWM::kNumHdrRegisters, value, status);
216 }
217}
218
219/**
220 * Set a PWM channel to the desired scaled value. The values range from -1 to 1
221 * and
222 * the period is controlled
223 * by the PWM Period and MinHigh registers.
224 *
225 * @param channel The PWM channel to set.
226 * @param value The scaled PWM value to set.
227 */
228void HAL_SetPWMSpeed(HAL_DigitalHandle pwmPortHandle, double speed,
229 int32_t* status) {
230 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
231 if (port == nullptr) {
232 *status = HAL_HANDLE_ERROR;
233 return;
234 }
235 if (!port->configSet) {
236 *status = INCOMPATIBLE_STATE;
237 return;
238 }
239
240 DigitalPort* dPort = port.get();
241
242 if (speed < -1.0) {
243 speed = -1.0;
244 } else if (speed > 1.0) {
245 speed = 1.0;
246 } else if (!std::isfinite(speed)) {
247 speed = 0.0;
248 }
249
250 // calculate the desired output pwm value by scaling the speed appropriately
251 int32_t rawValue;
252 if (speed == 0.0) {
253 rawValue = GetCenterPwm(dPort);
254 } else if (speed > 0.0) {
255 rawValue = static_cast<int32_t>(
256 speed * static_cast<double>(GetPositiveScaleFactor(dPort)) +
257 static_cast<double>(GetMinPositivePwm(dPort)) + 0.5);
258 } else {
259 rawValue = static_cast<int32_t>(
260 speed * static_cast<double>(GetNegativeScaleFactor(dPort)) +
261 static_cast<double>(GetMaxNegativePwm(dPort)) + 0.5);
262 }
263
264 if (!((rawValue >= GetMinNegativePwm(dPort)) &&
265 (rawValue <= GetMaxPositivePwm(dPort))) ||
266 rawValue == kPwmDisabled) {
267 *status = HAL_PWM_SCALE_ERROR;
268 return;
269 }
270
271 HAL_SetPWMRaw(pwmPortHandle, rawValue, status);
272}
273
274/**
275 * Set a PWM channel to the desired position value. The values range from 0 to 1
276 * and
277 * the period is controlled
278 * by the PWM Period and MinHigh registers.
279 *
280 * @param channel The PWM channel to set.
281 * @param value The scaled PWM value to set.
282 */
283void HAL_SetPWMPosition(HAL_DigitalHandle pwmPortHandle, double pos,
284 int32_t* status) {
285 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
286 if (port == nullptr) {
287 *status = HAL_HANDLE_ERROR;
288 return;
289 }
290 if (!port->configSet) {
291 *status = INCOMPATIBLE_STATE;
292 return;
293 }
294 DigitalPort* dPort = port.get();
295
296 if (pos < 0.0) {
297 pos = 0.0;
298 } else if (pos > 1.0) {
299 pos = 1.0;
300 }
301
302 // note, need to perform the multiplication below as floating point before
303 // converting to int
304 int32_t rawValue = static_cast<int32_t>(
305 (pos * static_cast<double>(GetFullRangeScaleFactor(dPort))) +
306 GetMinNegativePwm(dPort));
307
308 if (rawValue == kPwmDisabled) {
309 *status = HAL_PWM_SCALE_ERROR;
310 return;
311 }
312
313 HAL_SetPWMRaw(pwmPortHandle, rawValue, status);
314}
315
316void HAL_SetPWMDisabled(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
317 HAL_SetPWMRaw(pwmPortHandle, kPwmDisabled, status);
318}
319
320/**
321 * Get a value from a PWM channel. The values range from 0 to 255.
322 *
323 * @param channel The PWM channel to read from.
324 * @return The raw PWM value.
325 */
326int32_t HAL_GetPWMRaw(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
327 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
328 if (port == nullptr) {
329 *status = HAL_HANDLE_ERROR;
330 return 0;
331 }
332
333 if (port->channel < tPWM::kNumHdrRegisters) {
334 return pwmSystem->readHdr(port->channel, status);
335 } else {
336 return pwmSystem->readMXP(port->channel - tPWM::kNumHdrRegisters, status);
337 }
338}
339
340/**
341 * Get a scaled value from a PWM channel. The values range from -1 to 1.
342 *
343 * @param channel The PWM channel to read from.
344 * @return The scaled PWM value.
345 */
346double HAL_GetPWMSpeed(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
347 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
348 if (port == nullptr) {
349 *status = HAL_HANDLE_ERROR;
350 return 0;
351 }
352 if (!port->configSet) {
353 *status = INCOMPATIBLE_STATE;
354 return 0;
355 }
356
357 int32_t value = HAL_GetPWMRaw(pwmPortHandle, status);
358 if (*status != 0) return 0;
359 DigitalPort* dPort = port.get();
360
361 if (value == kPwmDisabled) {
362 return 0.0;
363 } else if (value > GetMaxPositivePwm(dPort)) {
364 return 1.0;
365 } else if (value < GetMinNegativePwm(dPort)) {
366 return -1.0;
367 } else if (value > GetMinPositivePwm(dPort)) {
368 return static_cast<double>(value - GetMinPositivePwm(dPort)) /
369 static_cast<double>(GetPositiveScaleFactor(dPort));
370 } else if (value < GetMaxNegativePwm(dPort)) {
371 return static_cast<double>(value - GetMaxNegativePwm(dPort)) /
372 static_cast<double>(GetNegativeScaleFactor(dPort));
373 } else {
374 return 0.0;
375 }
376}
377
378/**
379 * Get a position value from a PWM channel. The values range from 0 to 1.
380 *
381 * @param channel The PWM channel to read from.
382 * @return The scaled PWM value.
383 */
384double HAL_GetPWMPosition(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
385 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
386 if (port == nullptr) {
387 *status = HAL_HANDLE_ERROR;
388 return 0;
389 }
390 if (!port->configSet) {
391 *status = INCOMPATIBLE_STATE;
392 return 0;
393 }
394
395 int32_t value = HAL_GetPWMRaw(pwmPortHandle, status);
396 if (*status != 0) return 0;
397 DigitalPort* dPort = port.get();
398
399 if (value < GetMinNegativePwm(dPort)) {
400 return 0.0;
401 } else if (value > GetMaxPositivePwm(dPort)) {
402 return 1.0;
403 } else {
404 return static_cast<double>(value - GetMinNegativePwm(dPort)) /
405 static_cast<double>(GetFullRangeScaleFactor(dPort));
406 }
407}
408
409void HAL_LatchPWMZero(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
410 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
411 if (port == nullptr) {
412 *status = HAL_HANDLE_ERROR;
413 return;
414 }
415
416 pwmSystem->writeZeroLatch(port->channel, true, status);
417 pwmSystem->writeZeroLatch(port->channel, false, status);
418}
419
420/**
421 * Set how how often the PWM signal is squelched, thus scaling the period.
422 *
423 * @param channel The PWM channel to configure.
424 * @param squelchMask The 2-bit mask of outputs to squelch.
425 */
426void HAL_SetPWMPeriodScale(HAL_DigitalHandle pwmPortHandle, int32_t squelchMask,
427 int32_t* status) {
428 auto port = digitalChannelHandles.Get(pwmPortHandle, HAL_HandleEnum::PWM);
429 if (port == nullptr) {
430 *status = HAL_HANDLE_ERROR;
431 return;
432 }
433
434 if (port->channel < tPWM::kNumPeriodScaleHdrElements) {
435 pwmSystem->writePeriodScaleHdr(port->channel, squelchMask, status);
436 } else {
437 pwmSystem->writePeriodScaleMXP(
438 port->channel - tPWM::kNumPeriodScaleHdrElements, squelchMask, status);
439 }
440}
441
442/**
443 * Get the loop timing of the PWM system
444 *
445 * @return The loop time
446 */
447int32_t HAL_GetLoopTiming(int32_t* status) {
448 initializeDigital(status);
449 if (*status != 0) return 0;
450 return pwmSystem->readLoopTiming(status);
451}
452}