blob: c668039b2eacfb9ed266def95610a2c24798403c [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "hal/PWM.h"
6
James Kuszmaulb13e13f2023-11-22 20:44:04 -08007#include <algorithm>
8#include <cmath>
9
Brian Silverman8fce7482020-01-05 13:18:21 -080010#include "ConstantsInternal.h"
11#include "DigitalInternal.h"
12#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070013#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080014#include "PortsInternal.h"
15#include "hal/handles/HandlesInternal.h"
16#include "mockdata/PWMDataInternal.h"
17
18using namespace hal;
19
Austin Schuh812d0d12021-11-04 20:16:48 -070020namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080021void InitializePWM() {}
Austin Schuh812d0d12021-11-04 20:16:48 -070022} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080023
James Kuszmaulb13e13f2023-11-22 20:44:04 -080024static inline int32_t GetMaxPositivePwm(DigitalPort* port) {
25 return port->maxPwm;
26}
27
28static inline int32_t GetMinPositivePwm(DigitalPort* port) {
29 if (port->eliminateDeadband) {
30 return port->deadbandMaxPwm;
31 } else {
32 return port->centerPwm + 1;
33 }
34}
35
36static inline int32_t GetCenterPwm(DigitalPort* port) {
37 return port->centerPwm;
38}
39
40static inline int32_t GetMaxNegativePwm(DigitalPort* port) {
41 if (port->eliminateDeadband) {
42 return port->deadbandMinPwm;
43 } else {
44 return port->centerPwm - 1;
45 }
46}
47
48static inline int32_t GetMinNegativePwm(DigitalPort* port) {
49 return port->minPwm;
50}
51
52static inline int32_t GetPositiveScaleFactor(DigitalPort* port) {
53 return GetMaxPositivePwm(port) - GetMinPositivePwm(port);
54} ///< The scale for positive speeds.
55
56static inline int32_t GetNegativeScaleFactor(DigitalPort* port) {
57 return GetMaxNegativePwm(port) - GetMinNegativePwm(port);
58} ///< The scale for negative speeds.
59
60static inline int32_t GetFullRangeScaleFactor(DigitalPort* port) {
61 return GetMaxPositivePwm(port) - GetMinNegativePwm(port);
62} ///< The scale for positions.
63
Brian Silverman8fce7482020-01-05 13:18:21 -080064extern "C" {
65
66HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle,
Austin Schuh812d0d12021-11-04 20:16:48 -070067 const char* allocationLocation,
Brian Silverman8fce7482020-01-05 13:18:21 -080068 int32_t* status) {
69 hal::init::CheckInit();
Brian Silverman8fce7482020-01-05 13:18:21 -080070
71 int16_t channel = getPortHandleChannel(portHandle);
72 if (channel == InvalidHandleIndex) {
Austin Schuh812d0d12021-11-04 20:16:48 -070073 *status = RESOURCE_OUT_OF_RANGE;
74 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0,
75 kNumPWMChannels, channel);
Brian Silverman8fce7482020-01-05 13:18:21 -080076 return HAL_kInvalidHandle;
77 }
78
79 uint8_t origChannel = static_cast<uint8_t>(channel);
80
81 if (origChannel < kNumPWMHeaders) {
82 channel += kNumDigitalChannels; // remap Headers to end of allocations
83 } else {
84 channel = remapMXPPWMChannel(channel) + 10; // remap MXP to proper channel
85 }
86
Austin Schuh812d0d12021-11-04 20:16:48 -070087 HAL_DigitalHandle handle;
Brian Silverman8fce7482020-01-05 13:18:21 -080088
Austin Schuh812d0d12021-11-04 20:16:48 -070089 auto port = digitalChannelHandles->Allocate(channel, HAL_HandleEnum::PWM,
90 &handle, status);
91
92 if (*status != 0) {
93 if (port) {
94 hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel,
95 port->previousAllocation);
96 } else {
97 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0,
98 kNumPWMChannels, channel);
99 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800100 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
Brian Silverman8fce7482020-01-05 13:18:21 -0800101 }
102
103 port->channel = origChannel;
104
105 SimPWMData[origChannel].initialized = true;
106
107 // Defaults to allow an always valid config.
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800108 HAL_SetPWMConfigMicroseconds(handle, 2000, 1501, 1500, 1499, 1000, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800109
Austin Schuh812d0d12021-11-04 20:16:48 -0700110 port->previousAllocation = allocationLocation ? allocationLocation : "";
111
Brian Silverman8fce7482020-01-05 13:18:21 -0800112 return handle;
113}
114void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
115 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
116 if (port == nullptr) {
117 *status = HAL_HANDLE_ERROR;
118 return;
119 }
120
121 SimPWMData[port->channel].initialized = false;
122
123 digitalChannelHandles->Free(pwmPortHandle, HAL_HandleEnum::PWM);
124}
125
126HAL_Bool HAL_CheckPWMChannel(int32_t channel) {
127 return channel < kNumPWMChannels && channel >= 0;
128}
129
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800130void HAL_SetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle, int32_t max,
131 int32_t deadbandMax, int32_t center,
132 int32_t deadbandMin, int32_t min,
133 int32_t* status) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800134 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
135 if (port == nullptr) {
136 *status = HAL_HANDLE_ERROR;
137 return;
138 }
139
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800140 port->maxPwm = max;
141 port->deadbandMaxPwm = deadbandMax;
142 port->deadbandMinPwm = deadbandMin;
143 port->centerPwm = center;
144 port->minPwm = min;
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 port->configSet = true;
146}
147
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800148void HAL_GetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle,
149 int32_t* maxPwm, int32_t* deadbandMaxPwm,
150 int32_t* centerPwm, int32_t* deadbandMinPwm,
151 int32_t* minPwm, int32_t* status) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800152 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
153 if (port == nullptr) {
154 *status = HAL_HANDLE_ERROR;
155 return;
156 }
157 *maxPwm = port->maxPwm;
158 *deadbandMaxPwm = port->deadbandMaxPwm;
159 *deadbandMinPwm = port->deadbandMinPwm;
160 *centerPwm = port->centerPwm;
161 *minPwm = port->minPwm;
162}
163
164void HAL_SetPWMEliminateDeadband(HAL_DigitalHandle pwmPortHandle,
165 HAL_Bool eliminateDeadband, int32_t* status) {
166 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
167 if (port == nullptr) {
168 *status = HAL_HANDLE_ERROR;
169 return;
170 }
171 port->eliminateDeadband = eliminateDeadband;
172}
173
174HAL_Bool HAL_GetPWMEliminateDeadband(HAL_DigitalHandle pwmPortHandle,
175 int32_t* status) {
176 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
177 if (port == nullptr) {
178 *status = HAL_HANDLE_ERROR;
179 return false;
180 }
181 return port->eliminateDeadband;
182}
183
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800184void HAL_SetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
185 int32_t value, int32_t* status) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800186 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
187 if (port == nullptr) {
188 *status = HAL_HANDLE_ERROR;
189 return;
190 }
191
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800192 SimPWMData[port->channel].pulseMicrosecond = value;
193
194 DigitalPort* dPort = port.get();
195 double speed = 0.0;
196
197 if (value == kPwmDisabled) {
198 speed = 0.0;
199 } else if (value > GetMaxPositivePwm(dPort)) {
200 speed = 1.0;
201 } else if (value < GetMinNegativePwm(dPort)) {
202 speed = -1.0;
203 } else if (value > GetMinPositivePwm(dPort)) {
204 speed = static_cast<double>(value - GetMinPositivePwm(dPort)) /
205 static_cast<double>(GetPositiveScaleFactor(dPort));
206 } else if (value < GetMaxNegativePwm(dPort)) {
207 speed = static_cast<double>(value - GetMaxNegativePwm(dPort)) /
208 static_cast<double>(GetNegativeScaleFactor(dPort));
209 } else {
210 speed = 0.0;
211 }
212
213 SimPWMData[port->channel].speed = speed;
214
215 double pos = 0.0;
216
217 if (value < GetMinNegativePwm(dPort)) {
218 pos = 0.0;
219 } else if (value > GetMaxPositivePwm(dPort)) {
220 pos = 1.0;
221 } else {
222 pos = static_cast<double>(value - GetMinNegativePwm(dPort)) /
223 static_cast<double>(GetFullRangeScaleFactor(dPort));
224 }
225
226 SimPWMData[port->channel].position = pos;
Brian Silverman8fce7482020-01-05 13:18:21 -0800227}
228
229void HAL_SetPWMSpeed(HAL_DigitalHandle pwmPortHandle, double speed,
230 int32_t* status) {
231 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
232 if (port == nullptr) {
233 *status = HAL_HANDLE_ERROR;
234 return;
235 }
236 if (!port->configSet) {
237 *status = INCOMPATIBLE_STATE;
238 return;
239 }
240
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800241 if (std::isfinite(speed)) {
242 speed = std::clamp(speed, -1.0, 1.0);
243 } else {
244 speed = 0.0;
Brian Silverman8fce7482020-01-05 13:18:21 -0800245 }
246
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800247 DigitalPort* dPort = port.get();
248
249 // calculate the desired output pwm value by scaling the speed appropriately
250 int32_t rawValue;
251 if (speed == 0.0) {
252 rawValue = GetCenterPwm(dPort);
253 } else if (speed > 0.0) {
254 rawValue =
255 std::lround(speed * static_cast<double>(GetPositiveScaleFactor(dPort)) +
256 static_cast<double>(GetMinPositivePwm(dPort)));
257 } else {
258 rawValue =
259 std::lround(speed * static_cast<double>(GetNegativeScaleFactor(dPort)) +
260 static_cast<double>(GetMaxNegativePwm(dPort)));
261 }
262
263 if (!((rawValue >= GetMinNegativePwm(dPort)) &&
264 (rawValue <= GetMaxPositivePwm(dPort))) ||
265 rawValue == kPwmDisabled) {
266 *status = HAL_PWM_SCALE_ERROR;
267 return;
268 }
269
270 HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800271}
272
273void HAL_SetPWMPosition(HAL_DigitalHandle pwmPortHandle, double pos,
274 int32_t* status) {
275 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
276 if (port == nullptr) {
277 *status = HAL_HANDLE_ERROR;
278 return;
279 }
280 if (!port->configSet) {
281 *status = INCOMPATIBLE_STATE;
282 return;
283 }
284
285 if (pos < 0.0) {
286 pos = 0.0;
287 } else if (pos > 1.0) {
288 pos = 1.0;
289 }
290
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800291 DigitalPort* dPort = port.get();
292
293 // note, need to perform the multiplication below as floating point before
294 // converting to int
295 int32_t rawValue = static_cast<int32_t>(
296 (pos * static_cast<double>(GetFullRangeScaleFactor(dPort))) +
297 GetMinNegativePwm(dPort));
298
299 if (rawValue == kPwmDisabled) {
300 *status = HAL_PWM_SCALE_ERROR;
301 return;
302 }
303
304 HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800305}
306
307void HAL_SetPWMDisabled(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
308 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
309 if (port == nullptr) {
310 *status = HAL_HANDLE_ERROR;
311 return;
312 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800313 SimPWMData[port->channel].pulseMicrosecond = 0;
Brian Silverman8fce7482020-01-05 13:18:21 -0800314 SimPWMData[port->channel].position = 0;
315 SimPWMData[port->channel].speed = 0;
316}
317
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800318int32_t HAL_GetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
319 int32_t* status) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800320 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
321 if (port == nullptr) {
322 *status = HAL_HANDLE_ERROR;
323 return 0;
324 }
325
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800326 return SimPWMData[port->channel].pulseMicrosecond;
Brian Silverman8fce7482020-01-05 13:18:21 -0800327}
328
329double HAL_GetPWMSpeed(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
330 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
331 if (port == nullptr) {
332 *status = HAL_HANDLE_ERROR;
333 return 0;
334 }
335 if (!port->configSet) {
336 *status = INCOMPATIBLE_STATE;
337 return 0;
338 }
339
340 double speed = SimPWMData[port->channel].speed;
Austin Schuh812d0d12021-11-04 20:16:48 -0700341 if (speed > 1) {
342 speed = 1;
343 }
344 if (speed < -1) {
345 speed = -1;
346 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800347 return speed;
348}
349
350double HAL_GetPWMPosition(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
351 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
352 if (port == nullptr) {
353 *status = HAL_HANDLE_ERROR;
354 return 0;
355 }
356 if (!port->configSet) {
357 *status = INCOMPATIBLE_STATE;
358 return 0;
359 }
360
361 double position = SimPWMData[port->channel].position;
Austin Schuh812d0d12021-11-04 20:16:48 -0700362 if (position > 1) {
363 position = 1;
364 }
365 if (position < 0) {
366 position = 0;
367 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800368 return position;
369}
370
371void HAL_LatchPWMZero(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
372 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
373 if (port == nullptr) {
374 *status = HAL_HANDLE_ERROR;
375 return;
376 }
377
378 SimPWMData[port->channel].zeroLatch = true;
379 SimPWMData[port->channel].zeroLatch = false;
380}
381
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800382void HAL_SetPWMAlwaysHighMode(HAL_DigitalHandle pwmPortHandle,
383 int32_t* status) {
384 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
385 if (port == nullptr) {
386 *status = HAL_HANDLE_ERROR;
387 return;
388 }
389
390 SimPWMData[port->channel].pulseMicrosecond = 0xFFFF;
391 SimPWMData[port->channel].position = 0xFFFF;
392 SimPWMData[port->channel].speed = 0xFFFF;
393}
394
Brian Silverman8fce7482020-01-05 13:18:21 -0800395void HAL_SetPWMPeriodScale(HAL_DigitalHandle pwmPortHandle, int32_t squelchMask,
396 int32_t* status) {
397 auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
398 if (port == nullptr) {
399 *status = HAL_HANDLE_ERROR;
400 return;
401 }
402
403 SimPWMData[port->channel].periodScale = squelchMask;
404}
405
Austin Schuh812d0d12021-11-04 20:16:48 -0700406int32_t HAL_GetPWMLoopTiming(int32_t* status) {
407 return kExpectedLoopTiming;
408}
Brian Silverman8fce7482020-01-05 13:18:21 -0800409
Austin Schuh812d0d12021-11-04 20:16:48 -0700410uint64_t HAL_GetPWMCycleStartTime(int32_t* status) {
411 return 0;
412}
Brian Silverman8fce7482020-01-05 13:18:21 -0800413} // extern "C"