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