blob: e3196f0981f32b3c64f9dc182b13a2658bc2e507 [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2009. 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 "CANJaguar.h"
8#define tNIRIO_i32 int
9#include "ChipObject/NiFpga.h"
10#include "CAN/JaguarCANDriver.h"
11#include "CAN/can_proto.h"
12#include "NetworkCommunication/UsageReporting.h"
13#include "WPIErrors.h"
14#include <stdio.h>
15#include "LiveWindow/LiveWindow.h"
16
17#define swap16(x) ( (((x)>>8) &0x00FF) \
18 | (((x)<<8) &0xFF00) )
19#define swap32(x) ( (((x)>>24)&0x000000FF) \
20 | (((x)>>8) &0x0000FF00) \
21 | (((x)<<8) &0x00FF0000) \
22 | (((x)<<24)&0xFF000000) )
23
24#define kFullMessageIDMask (CAN_MSGID_API_M | CAN_MSGID_MFR_M | CAN_MSGID_DTYPE_M)
25
26const INT32 CANJaguar::kControllerRate;
27const double CANJaguar::kApproxBusVoltage;
28
29/**
30 * Common initialization code called by all constructors.
31 */
32void CANJaguar::InitCANJaguar()
33{
jerrym37afdca2013-03-03 01:17:57 +000034 m_table = NULL;
jerrymf1579332013-02-07 01:56:28 +000035 m_transactionSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
36 if (m_deviceNumber < 1 || m_deviceNumber > 63)
37 {
38 char buf[256];
39 snprintf(buf, 256, "device number \"%d\" must be between 1 and 63", m_deviceNumber);
40 wpi_setWPIErrorWithContext(ParameterOutOfRange, buf);
41 return;
42 }
43 UINT32 fwVer = GetFirmwareVersion();
44 if (StatusIsFatal())
45 return;
46 // 3330 was the first shipping RDK firmware version for the Jaguar
jerrym42dedc02013-02-25 01:59:14 +000047 if (fwVer >= 3330 || fwVer < 101)
jerrymf1579332013-02-07 01:56:28 +000048 {
49 char buf[256];
50 if (fwVer < 3330)
51 {
jerrym42dedc02013-02-25 01:59:14 +000052 snprintf(buf, 256, "Jag #%d firmware (%d) is too old (must be at least version 101 of the FIRST approved firmware)", m_deviceNumber, fwVer);
jerrymf1579332013-02-07 01:56:28 +000053 }
54 else
55 {
jerrym42dedc02013-02-25 01:59:14 +000056 snprintf(buf, 256, "Jag #%d firmware (%d) is not FIRST approved (must be at least version 101 of the FIRST approved firmware)", m_deviceNumber, fwVer);
jerrymf1579332013-02-07 01:56:28 +000057 }
58 wpi_setWPIErrorWithContext(JaguarVersionError, buf);
59 return;
60 }
61 switch (m_controlMode)
62 {
63 case kPercentVbus:
64 case kVoltage:
65 // No additional configuration required... start enabled.
66 EnableControl();
67 break;
68 default:
69 break;
70 }
71 m_safetyHelper = new MotorSafetyHelper(this);
72
73 nUsageReporting::report(nUsageReporting::kResourceType_CANJaguar, m_deviceNumber, m_controlMode);
74 LiveWindow::GetInstance()->AddActuator("CANJaguar", m_deviceNumber, 0, this);
75}
76
77/**
78 * Constructor
79 *
80 * @param deviceNumber The the address of the Jaguar on the CAN bus.
81 */
82CANJaguar::CANJaguar(UINT8 deviceNumber, ControlMode controlMode)
83 : m_deviceNumber (deviceNumber)
84 , m_controlMode (controlMode)
85 , m_transactionSemaphore (NULL)
86 , m_maxOutputVoltage (kApproxBusVoltage)
87 , m_safetyHelper (NULL)
88{
89 InitCANJaguar();
90}
91
92CANJaguar::~CANJaguar()
93{
94 delete m_safetyHelper;
95 m_safetyHelper = NULL;
96 semDelete(m_transactionSemaphore);
97 m_transactionSemaphore = NULL;
98}
99
100/**
101 * Set the output set-point value.
102 *
103 * The scale and the units depend on the mode the Jaguar is in.
104 * In PercentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).
105 * In Voltage Mode, the outputValue is in Volts.
106 * In Current Mode, the outputValue is in Amps.
107 * In Speed Mode, the outputValue is in Rotations/Minute.
108 * In Position Mode, the outputValue is in Rotations.
109 *
110 * @param outputValue The set-point to sent to the motor controller.
111 * @param syncGroup The update group to add this Set() to, pending UpdateSyncGroup(). If 0, update immediately.
112 */
113void CANJaguar::Set(float outputValue, UINT8 syncGroup)
114{
115 UINT32 messageID;
116 UINT8 dataBuffer[8];
117 UINT8 dataSize;
118
119 if (m_safetyHelper && !m_safetyHelper->IsAlive())
120 {
121 EnableControl();
122 }
123
124 switch(m_controlMode)
125 {
126 case kPercentVbus:
127 {
128 messageID = LM_API_VOLT_T_SET;
129 if (outputValue > 1.0) outputValue = 1.0;
130 if (outputValue < -1.0) outputValue = -1.0;
131 dataSize = packPercentage(dataBuffer, outputValue);
132 }
133 break;
134 case kSpeed:
135 {
136 messageID = LM_API_SPD_T_SET;
137 dataSize = packFXP16_16(dataBuffer, outputValue);
138 }
139 break;
140 case kPosition:
141 {
142 messageID = LM_API_POS_T_SET;
143 dataSize = packFXP16_16(dataBuffer, outputValue);
144 }
145 break;
146 case kCurrent:
147 {
148 messageID = LM_API_ICTRL_T_SET;
149 dataSize = packFXP8_8(dataBuffer, outputValue);
150 }
151 break;
152 case kVoltage:
153 {
154 messageID = LM_API_VCOMP_T_SET;
155 dataSize = packFXP8_8(dataBuffer, outputValue);
156 }
157 break;
158 default:
159 return;
160 }
161 if (syncGroup != 0)
162 {
163 dataBuffer[dataSize] = syncGroup;
164 dataSize++;
165 }
166 setTransaction(messageID, dataBuffer, dataSize);
167 if (m_safetyHelper) m_safetyHelper->Feed();
168}
169
170/**
171 * Get the recently set outputValue setpoint.
172 *
173 * The scale and the units depend on the mode the Jaguar is in.
174 * In PercentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).
175 * In Voltage Mode, the outputValue is in Volts.
176 * In Current Mode, the outputValue is in Amps.
177 * In Speed Mode, the outputValue is in Rotations/Minute.
178 * In Position Mode, the outputValue is in Rotations.
179 *
180 * @return The most recently set outputValue setpoint.
181 */
182float CANJaguar::Get()
183{
184 UINT8 dataBuffer[8];
185 UINT8 dataSize;
186
187 switch(m_controlMode)
188 {
189 case kPercentVbus:
190 getTransaction(LM_API_VOLT_SET, dataBuffer, &dataSize);
191 if (dataSize == sizeof(INT16))
192 {
193 return unpackPercentage(dataBuffer);
194 }
195 break;
196 case kSpeed:
197 getTransaction(LM_API_SPD_SET, dataBuffer, &dataSize);
198 if (dataSize == sizeof(INT32))
199 {
200 return unpackFXP16_16(dataBuffer);
201 }
202 break;
203 case kPosition:
204 getTransaction(LM_API_POS_SET, dataBuffer, &dataSize);
205 if (dataSize == sizeof(INT32))
206 {
207 return unpackFXP16_16(dataBuffer);
208 }
209 break;
210 case kCurrent:
211 getTransaction(LM_API_ICTRL_SET, dataBuffer, &dataSize);
212 if (dataSize == sizeof(INT16))
213 {
214 return unpackFXP8_8(dataBuffer);
215 }
216 break;
217 case kVoltage:
218 getTransaction(LM_API_VCOMP_SET, dataBuffer, &dataSize);
219 if (dataSize == sizeof(INT16))
220 {
221 return unpackFXP8_8(dataBuffer);
222 }
223 break;
224 }
225 return 0.0;
226}
227
228/**
229 * Common interface for disabling a motor.
230 *
231 * @deprecated Call DisableControl instead.
232 */
233void CANJaguar::Disable()
234{
235 DisableControl();
236}
237
238/**
239 * Write out the PID value as seen in the PIDOutput base object.
240 *
241 * @deprecated Call Set instead.
242 *
243 * @param output Write out the PercentVbus value as was computed by the PIDController
244 */
245void CANJaguar::PIDWrite(float output)
246{
247 if (m_controlMode == kPercentVbus)
248 {
249 Set(output);
250 }
251 else
252 {
253 wpi_setWPIErrorWithContext(IncompatibleMode, "PID only supported in PercentVbus mode");
254 }
255}
256
257UINT8 CANJaguar::packPercentage(UINT8 *buffer, double value)
258{
259 INT16 intValue = (INT16)(value * 32767.0);
260 *((INT16*)buffer) = swap16(intValue);
261 return sizeof(INT16);
262}
263
264UINT8 CANJaguar::packFXP8_8(UINT8 *buffer, double value)
265{
266 INT16 intValue = (INT16)(value * 256.0);
267 *((INT16*)buffer) = swap16(intValue);
268 return sizeof(INT16);
269}
270
271UINT8 CANJaguar::packFXP16_16(UINT8 *buffer, double value)
272{
273 INT32 intValue = (INT32)(value * 65536.0);
274 *((INT32*)buffer) = swap32(intValue);
275 return sizeof(INT32);
276}
277
278UINT8 CANJaguar::packINT16(UINT8 *buffer, INT16 value)
279{
280 *((INT16*)buffer) = swap16(value);
281 return sizeof(INT16);
282}
283
284UINT8 CANJaguar::packINT32(UINT8 *buffer, INT32 value)
285{
286 *((INT32*)buffer) = swap32(value);
287 return sizeof(INT32);
288}
289
290double CANJaguar::unpackPercentage(UINT8 *buffer)
291{
292 INT16 value = *((INT16*)buffer);
293 value = swap16(value);
294 return value / 32767.0;
295}
296
297double CANJaguar::unpackFXP8_8(UINT8 *buffer)
298{
299 INT16 value = *((INT16*)buffer);
300 value = swap16(value);
301 return value / 256.0;
302}
303
304double CANJaguar::unpackFXP16_16(UINT8 *buffer)
305{
306 INT32 value = *((INT32*)buffer);
307 value = swap32(value);
308 return value / 65536.0;
309}
310
311INT16 CANJaguar::unpackINT16(UINT8 *buffer)
312{
313 INT16 value = *((INT16*)buffer);
314 return swap16(value);
315}
316
317INT32 CANJaguar::unpackINT32(UINT8 *buffer)
318{
319 INT32 value = *((INT32*)buffer);
320 return swap32(value);
321}
322
323/**
324 * Send a message on the CAN bus through the CAN driver in FRC_NetworkCommunication
325 *
326 * Trusted messages require a 2-byte token at the beginning of the data payload.
327 * If the message being sent is trusted, make space for the token.
328 *
329 * @param messageID The messageID to be used on the CAN bus
330 * @param data The up to 8 bytes of data to be sent with the message
331 * @param dataSize Specify how much of the data in "data" to send
332 * @return Status of send call
333 */
334INT32 CANJaguar::sendMessage(UINT32 messageID, const UINT8 *data, UINT8 dataSize)
335{
336 static const UINT32 kTrustedMessages[] = {
337 LM_API_VOLT_T_EN, LM_API_VOLT_T_SET, LM_API_SPD_T_EN, LM_API_SPD_T_SET,
338 LM_API_VCOMP_T_EN, LM_API_VCOMP_T_SET, LM_API_POS_T_EN, LM_API_POS_T_SET,
339 LM_API_ICTRL_T_EN, LM_API_ICTRL_T_SET};
340 INT32 status=0;
341
342 for (UINT8 i=0; i<(sizeof(kTrustedMessages)/sizeof(kTrustedMessages[0])); i++)
343 {
344 if ((kFullMessageIDMask & messageID) == kTrustedMessages[i])
345 {
346 UINT8 dataBuffer[8];
347 dataBuffer[0] = 0;
348 dataBuffer[1] = 0;
349 // Make sure the data will still fit after adjusting for the token.
350 if (dataSize > 6)
351 {
352 // TODO: I would rather this not have to set the global error
353 wpi_setGlobalWPIErrorWithContext(ParameterOutOfRange, "dataSize > 6");
354 return 0;
355 }
356 for (UINT8 j=0; j < dataSize; j++)
357 {
358 dataBuffer[j + 2] = data[j];
359 }
360 FRC_NetworkCommunication_JaguarCANDriver_sendMessage(messageID, dataBuffer, dataSize + 2, &status);
361 return status;
362 }
363 }
364 FRC_NetworkCommunication_JaguarCANDriver_sendMessage(messageID, data, dataSize, &status);
365 return status;
366}
367
368/**
369 * Receive a message from the CAN bus through the CAN driver in FRC_NetworkCommunication
370 *
371 * @param messageID The messageID to read from the CAN bus
372 * @param data The up to 8 bytes of data that was received with the message
373 * @param dataSize Indicates how much data was received
374 * @param timeout Specify how long to wait for a message (in seconds)
375 * @return Status of receive call
376 */
377INT32 CANJaguar::receiveMessage(UINT32 *messageID, UINT8 *data, UINT8 *dataSize, float timeout)
378{
379 INT32 status = 0;
380 FRC_NetworkCommunication_JaguarCANDriver_receiveMessage(messageID, data, dataSize,
381 (UINT32)(timeout * 1000), &status);
382 return status;
383}
384
385/**
386 * Execute a transaction with a Jaguar that sets some property.
387 *
388 * Jaguar always acks when it receives a message. If we don't wait for an ack,
389 * the message object in the Jaguar could get overwritten before it is handled.
390 *
391 * @param messageID The messageID to be used on the CAN bus (device number is added internally)
392 * @param data The up to 8 bytes of data to be sent with the message
393 * @param dataSize Specify how much of the data in "data" to send
394 */
395void CANJaguar::setTransaction(UINT32 messageID, const UINT8 *data, UINT8 dataSize)
396{
397 UINT32 ackMessageID = LM_API_ACK | m_deviceNumber;
398 INT32 localStatus = 0;
399
400 // If there was an error on this object and it wasn't a timeout, refuse to talk to the device
401 // Call ClearError() on the object to try again
402 if (StatusIsFatal() && GetError().GetCode() != -44087)
403 return;
404
405 // Make sure we don't have more than one transaction with the same Jaguar outstanding.
406 semTake(m_transactionSemaphore, WAIT_FOREVER);
407
408 // Throw away any stale acks.
409 receiveMessage(&ackMessageID, NULL, 0, 0.0f);
410 // Send the message with the data.
411 localStatus = sendMessage(messageID | m_deviceNumber, data, dataSize);
412 wpi_setErrorWithContext(localStatus, "sendMessage");
413 // Wait for an ack.
414 localStatus = receiveMessage(&ackMessageID, NULL, 0);
415 wpi_setErrorWithContext(localStatus, "receiveMessage");
416
417 // Transaction complete.
418 semGive(m_transactionSemaphore);
419}
420
421/**
422 * Execute a transaction with a Jaguar that gets some property.
423 *
424 * Jaguar always generates a message with the same message ID when replying.
425 *
426 * @param messageID The messageID to read from the CAN bus (device number is added internally)
427 * @param data The up to 8 bytes of data that was received with the message
428 * @param dataSize Indicates how much data was received
429 */
430void CANJaguar::getTransaction(UINT32 messageID, UINT8 *data, UINT8 *dataSize)
431{
432 UINT32 targetedMessageID = messageID | m_deviceNumber;
433 INT32 localStatus = 0;
434
435 // If there was an error on this object and it wasn't a timeout, refuse to talk to the device
436 // Call ClearError() on the object to try again
437 if (StatusIsFatal() && GetError().GetCode() != -44087)
438 {
439 if (dataSize != NULL)
440 *dataSize = 0;
441 return;
442 }
443
444 // Make sure we don't have more than one transaction with the same Jaguar outstanding.
445 semTake(m_transactionSemaphore, WAIT_FOREVER);
446
447 // Throw away any stale responses.
448 receiveMessage(&targetedMessageID, NULL, 0, 0.0f);
449 // Send the message requesting data.
450 localStatus = sendMessage(targetedMessageID, NULL, 0);
451 wpi_setErrorWithContext(localStatus, "sendMessage");
452 // Caller may have set bit31 for remote frame transmission so clear invalid bits[31-29]
453 targetedMessageID &= 0x1FFFFFFF;
454 // Wait for the data.
455 localStatus = receiveMessage(&targetedMessageID, data, dataSize);
456 wpi_setErrorWithContext(localStatus, "receiveMessage");
457
458 // Transaction complete.
459 semGive(m_transactionSemaphore);
460}
461
462/**
463 * Set the reference source device for speed controller mode.
464 *
465 * Choose encoder as the source of speed feedback when in speed control mode.
466 *
467 * @param reference Specify a SpeedReference.
468 */
469void CANJaguar::SetSpeedReference(SpeedReference reference)
470{
471 UINT8 dataBuffer[8];
472
473 dataBuffer[0] = reference;
474 setTransaction(LM_API_SPD_REF, dataBuffer, sizeof(UINT8));
475}
476
477/**
478 * Get the reference source device for speed controller mode.
479 *
480 * @return A SpeedReference indicating the currently selected reference device for speed controller mode.
481 */
482CANJaguar::SpeedReference CANJaguar::GetSpeedReference()
483{
484 UINT8 dataBuffer[8];
485 UINT8 dataSize;
486
487 getTransaction(LM_API_SPD_REF, dataBuffer, &dataSize);
488 if (dataSize == sizeof(UINT8))
489 {
490 return (SpeedReference)*dataBuffer;
491 }
492 return kSpeedRef_None;
493}
494
495/**
496 * Set the reference source device for position controller mode.
497 *
498 * Choose between using and encoder and using a potentiometer
499 * as the source of position feedback when in position control mode.
500 *
501 * @param reference Specify a PositionReference.
502 */
503void CANJaguar::SetPositionReference(PositionReference reference)
504{
505 UINT8 dataBuffer[8];
506
507 dataBuffer[0] = reference;
508 setTransaction(LM_API_POS_REF, dataBuffer, sizeof(UINT8));
509}
510
511/**
512 * Get the reference source device for position controller mode.
513 *
514 * @return A PositionReference indicating the currently selected reference device for position controller mode.
515 */
516CANJaguar::PositionReference CANJaguar::GetPositionReference()
517{
518 UINT8 dataBuffer[8];
519 UINT8 dataSize;
520
521 getTransaction(LM_API_POS_REF, dataBuffer, &dataSize);
522 if (dataSize == sizeof(UINT8))
523 {
524 return (PositionReference)*dataBuffer;
525 }
526 return kPosRef_None;
527}
528
529/**
530 * Set the P, I, and D constants for the closed loop modes.
531 *
532 * @param p The proportional gain of the Jaguar's PID controller.
533 * @param i The integral gain of the Jaguar's PID controller.
534 * @param d The differential gain of the Jaguar's PID controller.
535 */
536void CANJaguar::SetPID(double p, double i, double d)
537{
538 UINT8 dataBuffer[8];
539 UINT8 dataSize;
540
541 switch(m_controlMode)
542 {
543 case kPercentVbus:
544 case kVoltage:
545 wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
546 break;
547 case kSpeed:
548 dataSize = packFXP16_16(dataBuffer, p);
549 setTransaction(LM_API_SPD_PC, dataBuffer, dataSize);
550 dataSize = packFXP16_16(dataBuffer, i);
551 setTransaction(LM_API_SPD_IC, dataBuffer, dataSize);
552 dataSize = packFXP16_16(dataBuffer, d);
553 setTransaction(LM_API_SPD_DC, dataBuffer, dataSize);
554 break;
555 case kPosition:
556 dataSize = packFXP16_16(dataBuffer, p);
557 setTransaction(LM_API_POS_PC, dataBuffer, dataSize);
558 dataSize = packFXP16_16(dataBuffer, i);
559 setTransaction(LM_API_POS_IC, dataBuffer, dataSize);
560 dataSize = packFXP16_16(dataBuffer, d);
561 setTransaction(LM_API_POS_DC, dataBuffer, dataSize);
562 break;
563 case kCurrent:
564 dataSize = packFXP16_16(dataBuffer, p);
565 setTransaction(LM_API_ICTRL_PC, dataBuffer, dataSize);
566 dataSize = packFXP16_16(dataBuffer, i);
567 setTransaction(LM_API_ICTRL_IC, dataBuffer, dataSize);
568 dataSize = packFXP16_16(dataBuffer, d);
569 setTransaction(LM_API_ICTRL_DC, dataBuffer, dataSize);
570 break;
571 }
572}
573
574/**
575 * Get the Proportional gain of the controller.
576 *
577 * @return The proportional gain.
578 */
579double CANJaguar::GetP()
580{
581 UINT8 dataBuffer[8];
582 UINT8 dataSize;
583
584 switch(m_controlMode)
585 {
586 case kPercentVbus:
587 case kVoltage:
588 wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
589 break;
590 case kSpeed:
591 getTransaction(LM_API_SPD_PC, dataBuffer, &dataSize);
592 if (dataSize == sizeof(INT32))
593 {
594 return unpackFXP16_16(dataBuffer);
595 }
596 break;
597 case kPosition:
598 getTransaction(LM_API_POS_PC, dataBuffer, &dataSize);
599 if (dataSize == sizeof(INT32))
600 {
601 return unpackFXP16_16(dataBuffer);
602 }
603 break;
604 case kCurrent:
605 getTransaction(LM_API_ICTRL_PC, dataBuffer, &dataSize);
606 if (dataSize == sizeof(INT32))
607 {
608 return unpackFXP16_16(dataBuffer);
609 }
610 break;
611 }
612 return 0.0;
613}
614
615/**
616 * Get the Intregral gain of the controller.
617 *
618 * @return The integral gain.
619 */
620double CANJaguar::GetI()
621{
622 UINT8 dataBuffer[8];
623 UINT8 dataSize;
624
625 switch(m_controlMode)
626 {
627 case kPercentVbus:
628 case kVoltage:
629 wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
630 break;
631 case kSpeed:
632 getTransaction(LM_API_SPD_IC, dataBuffer, &dataSize);
633 if (dataSize == sizeof(INT32))
634 {
635 return unpackFXP16_16(dataBuffer);
636 }
637 break;
638 case kPosition:
639 getTransaction(LM_API_POS_IC, dataBuffer, &dataSize);
640 if (dataSize == sizeof(INT32))
641 {
642 return unpackFXP16_16(dataBuffer);
643 }
644 break;
645 case kCurrent:
646 getTransaction(LM_API_ICTRL_IC, dataBuffer, &dataSize);
647 if (dataSize == sizeof(INT32))
648 {
649 return unpackFXP16_16(dataBuffer);
650 }
651 break;
652 }
653 return 0.0;
654}
655
656/**
657 * Get the Differential gain of the controller.
658 *
659 * @return The differential gain.
660 */
661double CANJaguar::GetD()
662{
663 UINT8 dataBuffer[8];
664 UINT8 dataSize;
665
666 switch(m_controlMode)
667 {
668 case kPercentVbus:
669 case kVoltage:
670 wpi_setWPIErrorWithContext(IncompatibleMode, "PID constants only apply in Speed, Position, and Current mode");
671 break;
672 case kSpeed:
673 getTransaction(LM_API_SPD_DC, dataBuffer, &dataSize);
674 if (dataSize == sizeof(INT32))
675 {
676 return unpackFXP16_16(dataBuffer);
677 }
678 break;
679 case kPosition:
680 getTransaction(LM_API_POS_DC, dataBuffer, &dataSize);
681 if (dataSize == sizeof(INT32))
682 {
683 return unpackFXP16_16(dataBuffer);
684 }
685 break;
686 case kCurrent:
687 getTransaction(LM_API_ICTRL_DC, dataBuffer, &dataSize);
688 if (dataSize == sizeof(INT32))
689 {
690 return unpackFXP16_16(dataBuffer);
691 }
692 break;
693 }
694 return 0.0;
695}
696
697/**
698 * Enable the closed loop controller.
699 *
700 * Start actually controlling the output based on the feedback.
701 * If starting a position controller with an encoder reference,
702 * use the encoderInitialPosition parameter to initialize the
703 * encoder state.
704 *
705 * @param encoderInitialPosition Encoder position to set if position with encoder reference. Ignored otherwise.
706 */
707void CANJaguar::EnableControl(double encoderInitialPosition)
708{
709 UINT8 dataBuffer[8];
710 UINT8 dataSize = 0;
711
712 switch(m_controlMode)
713 {
714 case kPercentVbus:
715 setTransaction(LM_API_VOLT_T_EN, dataBuffer, dataSize);
716 break;
717 case kSpeed:
718 setTransaction(LM_API_SPD_T_EN, dataBuffer, dataSize);
719 break;
720 case kPosition:
721 dataSize = packFXP16_16(dataBuffer, encoderInitialPosition);
722 setTransaction(LM_API_POS_T_EN, dataBuffer, dataSize);
723 break;
724 case kCurrent:
725 setTransaction(LM_API_ICTRL_T_EN, dataBuffer, dataSize);
726 break;
727 case kVoltage:
728 setTransaction(LM_API_VCOMP_T_EN, dataBuffer, dataSize);
729 break;
730 }
731}
732
733/**
734 * Disable the closed loop controller.
735 *
736 * Stop driving the output based on the feedback.
737 */
738void CANJaguar::DisableControl()
739{
740 UINT8 dataBuffer[8];
741 UINT8 dataSize = 0;
742
743 switch(m_controlMode)
744 {
745 case kPercentVbus:
746 setTransaction(LM_API_VOLT_DIS, dataBuffer, dataSize);
747 break;
748 case kSpeed:
749 setTransaction(LM_API_SPD_DIS, dataBuffer, dataSize);
750 break;
751 case kPosition:
752 setTransaction(LM_API_POS_DIS, dataBuffer, dataSize);
753 break;
754 case kCurrent:
755 setTransaction(LM_API_ICTRL_DIS, dataBuffer, dataSize);
756 break;
757 case kVoltage:
758 setTransaction(LM_API_VCOMP_DIS, dataBuffer, dataSize);
759 break;
760 }
761}
762
763/**
764 * Change the control mode of this Jaguar object.
765 *
766 * After changing modes, configure any PID constants or other settings needed
767 * and then EnableControl() to actually change the mode on the Jaguar.
768 *
769 * @param controlMode The new mode.
770 */
771void CANJaguar::ChangeControlMode(ControlMode controlMode)
772{
773 // Disable the previous mode
774 DisableControl();
775
776 // Update the local mode
777 m_controlMode = controlMode;
778
779 nUsageReporting::report(nUsageReporting::kResourceType_CANJaguar, m_deviceNumber, m_controlMode);
780}
781
782/**
783 * Get the active control mode from the Jaguar.
784 *
785 * Ask the Jag what mode it is in.
786 *
787 * @return ControlMode that the Jag is in.
788 */
789CANJaguar::ControlMode CANJaguar::GetControlMode()
790{
791 UINT8 dataBuffer[8];
792 UINT8 dataSize;
793
794 getTransaction(LM_API_STATUS_CMODE, dataBuffer, &dataSize);
795 if (dataSize == sizeof(INT8))
796 {
797 return (ControlMode)dataBuffer[0];
798 }
799 return kPercentVbus;
800}
801
802/**
803 * Get the voltage at the battery input terminals of the Jaguar.
804 *
805 * @return The bus voltage in Volts.
806 */
807float CANJaguar::GetBusVoltage()
808{
809 UINT8 dataBuffer[8];
810 UINT8 dataSize;
811
812 getTransaction(LM_API_STATUS_VOLTBUS, dataBuffer, &dataSize);
813 if (dataSize == sizeof(INT16))
814 {
815 return unpackFXP8_8(dataBuffer);
816 }
817 return 0.0;
818}
819
820/**
821 * Get the voltage being output from the motor terminals of the Jaguar.
822 *
823 * @return The output voltage in Volts.
824 */
825float CANJaguar::GetOutputVoltage()
826{
827 UINT8 dataBuffer[8];
828 UINT8 dataSize;
829
830 // Read the volt out which is in Volts units.
831 getTransaction(LM_API_STATUS_VOUT, dataBuffer, &dataSize);
832 if (dataSize == sizeof(INT16))
833 {
834 return unpackFXP8_8(dataBuffer);
835 }
836 return 0.0;
837}
838
839/**
840 * Get the current through the motor terminals of the Jaguar.
841 *
842 * @return The output current in Amps.
843 */
844float CANJaguar::GetOutputCurrent()
845{
846 UINT8 dataBuffer[8];
847 UINT8 dataSize;
848
849 getTransaction(LM_API_STATUS_CURRENT, dataBuffer, &dataSize);
850 if (dataSize == sizeof(INT16))
851 {
852 return unpackFXP8_8(dataBuffer);
853 }
854 return 0.0;
855}
856
857/**
858 * Get the internal temperature of the Jaguar.
859 *
860 * @return The temperature of the Jaguar in degrees Celsius.
861 */
862float CANJaguar::GetTemperature()
863{
864 UINT8 dataBuffer[8];
865 UINT8 dataSize;
866
867 getTransaction(LM_API_STATUS_TEMP, dataBuffer, &dataSize);
868 if (dataSize == sizeof(INT16))
869 {
870 return unpackFXP8_8(dataBuffer);
871 }
872 return 0.0;
873}
874
875/**
876 * Get the position of the encoder or potentiometer.
877 *
878 * @return The position of the motor in rotations based on the configured feedback.
879 */
880double CANJaguar::GetPosition()
881{
882 UINT8 dataBuffer[8];
883 UINT8 dataSize;
884
885 getTransaction(LM_API_STATUS_POS, dataBuffer, &dataSize);
886 if (dataSize == sizeof(INT32))
887 {
888 return unpackFXP16_16(dataBuffer);
889 }
890 return 0.0;
891}
892
893/**
894 * Get the speed of the encoder.
895 *
896 * @return The speed of the motor in RPM based on the configured feedback.
897 */
898double CANJaguar::GetSpeed()
899{
900 UINT8 dataBuffer[8];
901 UINT8 dataSize;
902
903 getTransaction(LM_API_STATUS_SPD, dataBuffer, &dataSize);
904 if (dataSize == sizeof(INT32))
905 {
906 return unpackFXP16_16(dataBuffer);
907 }
908 return 0.0;
909}
910
911/**
912 * Get the status of the forward limit switch.
913 *
914 * @return The motor is allowed to turn in the forward direction when true.
915 */
916bool CANJaguar::GetForwardLimitOK()
917{
918 UINT8 dataBuffer[8];
919 UINT8 dataSize;
920
921 getTransaction(LM_API_STATUS_LIMIT, dataBuffer, &dataSize);
922 if (dataSize == sizeof(UINT8))
923 {
924 return (*dataBuffer & kForwardLimit) != 0;
925 }
926 return 0;
927}
928
929/**
930 * Get the status of the reverse limit switch.
931 *
932 * @return The motor is allowed to turn in the reverse direction when true.
933 */
934bool CANJaguar::GetReverseLimitOK()
935{
936 UINT8 dataBuffer[8];
937 UINT8 dataSize;
938
939 getTransaction(LM_API_STATUS_LIMIT, dataBuffer, &dataSize);
940 if (dataSize == sizeof(UINT8))
941 {
942 return (*dataBuffer & kReverseLimit) != 0;
943 }
944 return 0;
945}
946
947/**
948 * Get the status of any faults the Jaguar has detected.
949 *
950 * @return A bit-mask of faults defined by the "Faults" enum.
951 */
952UINT16 CANJaguar::GetFaults()
953{
954 UINT8 dataBuffer[8];
955 UINT8 dataSize;
956
957 getTransaction(LM_API_STATUS_FAULT, dataBuffer, &dataSize);
958 if (dataSize == sizeof(UINT16))
959 {
960 return unpackINT16(dataBuffer);
961 }
962 return 0;
963}
964
965/**
966 * Check if the Jaguar's power has been cycled since this was last called.
967 *
968 * This should return true the first time called after a Jaguar power up,
969 * and false after that.
970 *
971 * @return The Jaguar was power cycled since the last call to this function.
972 */
973bool CANJaguar::GetPowerCycled()
974{
975 UINT8 dataBuffer[8];
976 UINT8 dataSize;
977
978 getTransaction(LM_API_STATUS_POWER, dataBuffer, &dataSize);
979 if (dataSize == sizeof(UINT8))
980 {
981 bool powerCycled = (*dataBuffer != 0);
982
983 // Clear the power cycled bit now that we've accessed it
984 if (powerCycled)
985 {
986 dataBuffer[0] = 1;
987 setTransaction(LM_API_STATUS_POWER, dataBuffer, sizeof(UINT8));
988 }
989
990 return powerCycled;
991 }
992 return 0;
993}
994
995/**
996 * Set the maximum voltage change rate.
997 *
998 * When in PercentVbus or Voltage output mode, the rate at which the voltage changes can
999 * be limited to reduce current spikes. Set this to 0.0 to disable rate limiting.
1000 *
1001 * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s.
1002 */
1003void CANJaguar::SetVoltageRampRate(double rampRate)
1004{
1005 UINT8 dataBuffer[8];
1006 UINT8 dataSize;
1007
1008 switch(m_controlMode)
1009 {
1010 case kPercentVbus:
1011 dataSize = packPercentage(dataBuffer, rampRate / (m_maxOutputVoltage * kControllerRate));
1012 setTransaction(LM_API_VOLT_SET_RAMP, dataBuffer, dataSize);
1013 break;
1014 case kVoltage:
1015 dataSize = packFXP8_8(dataBuffer, rampRate / kControllerRate);
1016 setTransaction(LM_API_VCOMP_IN_RAMP, dataBuffer, dataSize);
1017 break;
1018 default:
1019 return;
1020 }
1021}
1022
1023/**
1024 * Get the version of the firmware running on the Jaguar.
1025 *
1026 * @return The firmware version. 0 if the device did not respond.
1027 */
1028UINT32 CANJaguar::GetFirmwareVersion()
1029{
1030 UINT8 dataBuffer[8];
1031 UINT8 dataSize;
1032
1033 // Set the MSB to tell the 2CAN that this is a remote message.
1034 getTransaction(0x80000000 | CAN_MSGID_API_FIRMVER, dataBuffer, &dataSize);
1035 if (dataSize == sizeof(UINT32))
1036 {
1037 return unpackINT32(dataBuffer);
1038 }
1039 return 0;
1040}
1041
1042/**
1043 * Get the version of the Jaguar hardware.
1044 *
1045 * @return The hardware version. 1: Jaguar, 2: Black Jaguar
1046 */
1047UINT8 CANJaguar::GetHardwareVersion()
1048{
1049 UINT8 dataBuffer[8];
1050 UINT8 dataSize;
1051
1052 getTransaction(LM_API_HWVER, dataBuffer, &dataSize);
1053 if (dataSize == sizeof(UINT8)+sizeof(UINT8))
1054 {
1055 if (*dataBuffer == m_deviceNumber)
1056 {
1057 return *(dataBuffer+1);
1058 }
1059 }
1060 // Assume Gray Jag if there is no response
1061 return LM_HWVER_JAG_1_0;
1062}
1063
1064/**
1065 * Configure what the controller does to the H-Bridge when neutral (not driving the output).
1066 *
1067 * This allows you to override the jumper configuration for brake or coast.
1068 *
1069 * @param mode Select to use the jumper setting or to override it to coast or brake.
1070 */
1071void CANJaguar::ConfigNeutralMode(NeutralMode mode)
1072{
1073 UINT8 dataBuffer[8];
1074
1075 dataBuffer[0] = mode;
1076 setTransaction(LM_API_CFG_BRAKE_COAST, dataBuffer, sizeof(UINT8));
1077}
1078
1079/**
1080 * Configure how many codes per revolution are generated by your encoder.
1081 *
1082 * @param codesPerRev The number of counts per revolution in 1X mode.
1083 */
1084void CANJaguar::ConfigEncoderCodesPerRev(UINT16 codesPerRev)
1085{
1086 UINT8 dataBuffer[8];
1087 UINT8 dataSize;
1088
1089 dataSize = packINT16(dataBuffer, codesPerRev);
1090 setTransaction(LM_API_CFG_ENC_LINES, dataBuffer, dataSize);
1091}
1092
1093/**
1094 * Configure the number of turns on the potentiometer.
1095 *
1096 * There is no special support for continuous turn potentiometers.
1097 * Only integer numbers of turns are supported.
1098 *
1099 * @param turns The number of turns of the potentiometer
1100 */
1101void CANJaguar::ConfigPotentiometerTurns(UINT16 turns)
1102{
1103 UINT8 dataBuffer[8];
1104 UINT8 dataSize;
1105
1106 dataSize = packINT16(dataBuffer, turns);
1107 setTransaction(LM_API_CFG_POT_TURNS, dataBuffer, dataSize);
1108}
1109
1110/**
1111 * Configure Soft Position Limits when in Position Controller mode.
1112 *
1113 * When controlling position, you can add additional limits on top of the limit switch inputs
1114 * that are based on the position feedback. If the position limit is reached or the
1115 * switch is opened, that direction will be disabled.
1116 *
1117 * @param forwardLimitPosition The position that if exceeded will disable the forward direction.
1118 * @param reverseLimitPosition The position that if exceeded will disable the reverse direction.
1119 */
1120void CANJaguar::ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition)
1121{
1122 UINT8 dataBuffer[8];
1123 UINT8 dataSize;
1124
1125 dataSize = packFXP16_16(dataBuffer, forwardLimitPosition);
1126 dataBuffer[dataSize++] = forwardLimitPosition > reverseLimitPosition;
1127 setTransaction(LM_API_CFG_LIMIT_FWD, dataBuffer, dataSize);
1128
1129 dataSize = packFXP16_16(dataBuffer, reverseLimitPosition);
1130 dataBuffer[dataSize++] = forwardLimitPosition <= reverseLimitPosition;
1131 setTransaction(LM_API_CFG_LIMIT_REV, dataBuffer, dataSize);
1132
1133 dataBuffer[0] = kLimitMode_SoftPositionLimits;
1134 setTransaction(LM_API_CFG_LIMIT_MODE, dataBuffer, sizeof(UINT8));
1135}
1136
1137/**
1138 * Disable Soft Position Limits if previously enabled.
1139 *
1140 * Soft Position Limits are disabled by default.
1141 */
1142void CANJaguar::DisableSoftPositionLimits()
1143{
1144 UINT8 dataBuffer[8];
1145
1146 dataBuffer[0] = kLimitMode_SwitchInputsOnly;
1147 setTransaction(LM_API_CFG_LIMIT_MODE, dataBuffer, sizeof(UINT8));
1148}
1149
1150/**
1151 * Configure the maximum voltage that the Jaguar will ever output.
1152 *
1153 * This can be used to limit the maximum output voltage in all modes so that
1154 * motors which cannot withstand full bus voltage can be used safely.
1155 *
1156 * @param voltage The maximum voltage output by the Jaguar.
1157 */
1158void CANJaguar::ConfigMaxOutputVoltage(double voltage)
1159{
1160 UINT8 dataBuffer[8];
1161 UINT8 dataSize;
1162
1163 m_maxOutputVoltage = voltage;
1164 dataSize = packFXP8_8(dataBuffer, voltage);
1165 setTransaction(LM_API_CFG_MAX_VOUT, dataBuffer, dataSize);
1166}
1167
1168/**
1169 * Configure how long the Jaguar waits in the case of a fault before resuming operation.
1170 *
1171 * Faults include over temerature, over current, and bus under voltage.
1172 * The default is 3.0 seconds, but can be reduced to as low as 0.5 seconds.
1173 *
1174 * @param faultTime The time to wait before resuming operation, in seconds.
1175 */
1176void CANJaguar::ConfigFaultTime(float faultTime)
1177{
1178 UINT8 dataBuffer[8];
1179 UINT8 dataSize;
1180
1181 // Message takes ms
1182 dataSize = packINT16(dataBuffer, (INT16)(faultTime * 1000.0));
1183 setTransaction(LM_API_CFG_FAULT_TIME, dataBuffer, dataSize);
1184}
1185
1186/**
1187 * Update all the motors that have pending sets in the syncGroup.
1188 *
1189 * @param syncGroup A bitmask of groups to generate synchronous output.
1190 */
1191void CANJaguar::UpdateSyncGroup(UINT8 syncGroup)
1192{
1193 sendMessage(CAN_MSGID_API_SYNC, &syncGroup, sizeof(syncGroup));
1194}
1195
1196
1197void CANJaguar::SetExpiration(float timeout)
1198{
1199 if (m_safetyHelper) m_safetyHelper->SetExpiration(timeout);
1200}
1201
1202float CANJaguar::GetExpiration()
1203{
1204 if (!m_safetyHelper) return 0.0;
1205 return m_safetyHelper->GetExpiration();
1206}
1207
1208bool CANJaguar::IsAlive()
1209{
1210 if (!m_safetyHelper) return false;
1211 return m_safetyHelper->IsAlive();
1212}
1213
1214bool CANJaguar::IsSafetyEnabled()
1215{
1216 if (!m_safetyHelper) return false;
1217 return m_safetyHelper->IsSafetyEnabled();
1218}
1219
1220void CANJaguar::SetSafetyEnabled(bool enabled)
1221{
1222 if (m_safetyHelper) m_safetyHelper->SetSafetyEnabled(enabled);
1223}
1224
1225void CANJaguar::GetDescription(char *desc)
1226{
1227 sprintf(desc, "CANJaguar ID %d", m_deviceNumber);
1228}
1229
1230/**
1231 * Common interface for stopping the motor
1232 * Part of the MotorSafety interface
1233 *
1234 * @deprecated Call DisableControl instead.
1235 */
1236void CANJaguar::StopMotor()
1237{
1238 DisableControl();
1239}
1240
1241void CANJaguar::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) {
1242 Set(value.f);
1243}
1244
1245void CANJaguar::UpdateTable() {
1246 if (m_table != NULL) {
1247 m_table->PutNumber("Value", Get());
1248 }
1249}
1250
1251void CANJaguar::StartLiveWindowMode() {
jerrym37afdca2013-03-03 01:17:57 +00001252 if (m_table != NULL) {
1253 m_table->AddTableListener("Value", this, true);
1254 }
jerrymf1579332013-02-07 01:56:28 +00001255}
1256
1257void CANJaguar::StopLiveWindowMode() {
jerrym37afdca2013-03-03 01:17:57 +00001258 if (m_table != NULL) {
1259 m_table->RemoveTableListener(this);
1260 }
jerrymf1579332013-02-07 01:56:28 +00001261}
1262
1263std::string CANJaguar::GetSmartDashboardType() {
1264 return "Speed Controller";
1265}
1266
1267void CANJaguar::InitTable(ITable *subTable) {
1268 m_table = subTable;
1269 UpdateTable();
1270}
1271
1272ITable * CANJaguar::GetTable() {
1273 return m_table;
1274}
1275
1276