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