Mindstorms 3rd Party ROBOTC Drivers RobotC
[Home] [Download] [Submit a bug/suggestion] [ROBOTC Forums] [Blog] [Support this project]

hitechnic-irlink.h

Go to the documentation of this file.
00001 /*!@addtogroup HiTechnic
00002  * @{
00003  * @defgroup htirl IR Link Sensor
00004  * HiTechnic IR Link Sensor driver
00005  * @{
00006  */
00007 
00008 /*
00009  * $Id: hitechnic-irlink.h 133 2013-03-10 15:15:38Z xander $
00010  */
00011 
00012 #ifndef _HTIRL_H_
00013 #define _HTIRL_H_
00014 /** \file hitechnic-irlink.h
00015  * \brief HiTechnic IR Link Sensor driver
00016  *
00017  * hitechnic-irlink.h provides an API for the HiTechnic IR Link Sensor.
00018  *
00019  * Changelog:
00020  * - 1.0: Initial release
00021  * - 1.1: Minor changes
00022  * - 1.2: Rewrite to make use of the new common.h API
00023  * - 1.3: Clarified port numbering
00024  * - 1.4: Removed inline functions
00025  * - 1.5: Added PFsinglePinOutputMode() functionality to control motors without a timeout\n
00026  *        Added PFmotor() as a wrapper for PFsinglePinOutputMode()\n
00027  *        eCPMMotorCommand has been replaced with more generic ePWMMotorCommand\n
00028  *        transmitIR() now works according to the specs\n
00029  *
00030  * Credits:
00031  * - Big thanks to HiTechnic for providing me with the hardware necessary to write and test this.
00032  *
00033  * License: You may use this code as you wish, provided you give credit where its due.
00034  *
00035  * THIS CODE WILL ONLY WORK WITH ROBOTC VERSION 3.59 AND HIGHER. 
00036 
00037  * \author Xander Soldaat (xander_at_botbench.com)
00038  * \date 25 May 2010
00039  * \version 1.4
00040  * \example hitechnic-irlink-test1.c
00041  */
00042 
00043 #pragma systemFile
00044 
00045 //#define _DEBUG_DRIVER_    /*!< Compile the driver with debugging code */
00046 
00047 #ifdef _DEBUG_DRIVER_
00048 #warn "-----------------------------------------"
00049 #warn "HTIRL DRIVER COMPILED WITH DEBUGGING CODE"
00050 #warn "-----------------------------------------"
00051 #endif
00052 
00053 #ifndef _COMMON_H_
00054 #include "common.h"
00055 #endif
00056 
00057 #define BUF_HEADSIZE    3   /*!< I2C buff size, address and register */
00058 #define BUF_DATASIZE    11  /*!< max size of encoded buffer */
00059 #define BUF_TAILSIZE    3   /*!< IR data length, IR Link mode and start transmission */
00060 #define START_HEAD      0   /*!< index of start of header */
00061 #define START_DATA      3   /*!< index of start of data payload */
00062 #define START_TAIL      15  /*!< index of start of tail */
00063 
00064 #define PFSPORT(X) X / 8
00065 #define PFCHAN(X) (X % 8) / 2
00066 #define PFMOT(X) X % 2
00067 
00068 byte toggle[4] = {0, 0, 0, 0};
00069 
00070 /*!< Motor connections */
00071 typedef enum {
00072   pfmotor_S1_C1_A = 0,  /*!< Motor A, Channel 1, IR Link connected to S1 */
00073   pfmotor_S1_C1_B,      /*!< Motor B, Channel 1, IR Link connected to S1 */
00074   pfmotor_S1_C2_A,      /*!< Motor A, Channel 2, IR Link connected to S1 */
00075   pfmotor_S1_C2_B,      /*!< Motor B, Channel 2, IR Link connected to S1 */
00076   pfmotor_S1_C3_A,      /*!< Motor A, Channel 3, IR Link connected to S1 */
00077   pfmotor_S1_C3_B,      /*!< Motor B, Channel 3, IR Link connected to S1 */
00078   pfmotor_S1_C4_A,      /*!< Motor A, Channel 4, IR Link connected to S1 */
00079   pfmotor_S1_C4_B,      /*!< Motor B, Channel 4, IR Link connected to S1 */
00080   pfmotor_S2_C1_A,      /*!< Motor A, Channel 1, IR Link connected to S2 */
00081   pfmotor_S2_C1_B,      /*!< Motor B, Channel 1, IR Link connected to S2 */
00082   pfmotor_S2_C2_A,      /*!< Motor A, Channel 2, IR Link connected to S2 */
00083   pfmotor_S2_C2_B,      /*!< Motor B, Channel 2, IR Link connected to S2 */
00084   pfmotor_S2_C3_A,      /*!< Motor A, Channel 3, IR Link connected to S2 */
00085   pfmotor_S2_C3_B,      /*!< Motor B, Channel 3, IR Link connected to S2 */
00086   pfmotor_S2_C4_A,      /*!< Motor A, Channel 4, IR Link connected to S2 */
00087   pfmotor_S2_C4_B,      /*!< Motor B, Channel 4, IR Link connected to S2 */
00088   pfmotor_S3_C1_A,      /*!< Motor A, Channel 1, IR Link connected to S3 */
00089   pfmotor_S3_C1_B,      /*!< Motor B, Channel 1, IR Link connected to S3 */
00090   pfmotor_S3_C2_A,      /*!< Motor A, Channel 2, IR Link connected to S3 */
00091   pfmotor_S3_C2_B,      /*!< Motor B, Channel 2, IR Link connected to S3 */
00092   pfmotor_S3_C3_A,      /*!< Motor A, Channel 3, IR Link connected to S3 */
00093   pfmotor_S3_C3_B,      /*!< Motor B, Channel 3, IR Link connected to S3 */
00094   pfmotor_S3_C4_A,      /*!< Motor A, Channel 4, IR Link connected to S3 */
00095   pfmotor_S3_C4_B,      /*!< Motor B, Channel 4, IR Link connected to S3 */
00096   pfmotor_S4_C1_A,      /*!< Motor A, Channel 1, IR Link connected to S4 */
00097   pfmotor_S4_C1_B,      /*!< Motor B, Channel 1, IR Link connected to S4 */
00098   pfmotor_S4_C2_A,      /*!< Motor A, Channel 2, IR Link connected to S4 */
00099   pfmotor_S4_C2_B,      /*!< Motor B, Channel 2, IR Link connected to S4 */
00100   pfmotor_S4_C3_A,      /*!< Motor A, Channel 3, IR Link connected to S4 */
00101   pfmotor_S4_C3_B,      /*!< Motor B, Channel 3, IR Link connected to S4 */
00102   pfmotor_S4_C4_A,      /*!< Motor A, Channel 4, IR Link connected to S4 */
00103   pfmotor_S4_C4_B,      /*!< Motor B, Channel 4, IR Link connected to S4 */
00104 } tPFmotor;
00105 
00106 
00107 /*!< PWM Mode commands */
00108 typedef enum {
00109   MOTOR_FLOAT = 0,      /*!< Float the motor */
00110   MOTOR_FWD_PWM_1 = 1,  /*!< Forward speed 1 */
00111   MOTOR_FWD_PWM_2 = 2,  /*!< Forward speed 2 */
00112   MOTOR_FWD_PWM_3 = 3,  /*!< Forward speed 3 */
00113   MOTOR_FWD_PWM_4 = 4,  /*!< Forward speed 4 */
00114   MOTOR_FWD_PWM_5 = 5,  /*!< Forward speed 5 */
00115   MOTOR_FWD_PWM_6 = 6,  /*!< Forward speed 6 */
00116   MOTOR_FWD_PWM_7 = 7,  /*!< Forward speed 7 */
00117   MOTOR_BRAKE = 8,      /*!< Brake the motor */
00118   MOTOR_REV_PWM_7 = 9,  /*!< Reverse speed 7 */
00119   MOTOR_REV_PWM_6 = 10, /*!< Reverse speed 6 */
00120   MOTOR_REV_PWM_5 = 11, /*!< Reverse speed 5 */
00121   MOTOR_REV_PWM_4 = 12, /*!< Reverse speed 4 */
00122   MOTOR_REV_PWM_3 = 13, /*!< Reverse speed 3 */
00123   MOTOR_REV_PWM_2 = 14, /*!< Reverse speed 2 */
00124   MOTOR_REV_PWM_1 = 15  /*!< Reverse speed 1 */
00125 } ePWMMotorCommand;
00126 
00127 /*!< Combo Direct Mode commands */
00128 typedef enum {
00129   CDM_MOTOR_FLOAT = 0,      /*!< Float the motor */
00130   CDM_MOTOR_FWD = 1,        /*!< Forward */
00131   CDM_MOTOR_BAK = 2,        /*!< Reverse */
00132   CDM_MOTOR_BRAKE = 3       /*!< Brake the motor */
00133 } eCDMMotorCommand;
00134 
00135 // Function prototypes
00136 // inline void addI2CHead(tByteArray &data);
00137 // inline void addI2CTail(tByteArray &data);
00138 void PFcomboDirectMode(tSensors link, int channel, eCDMMotorCommand _motorB, eCDMMotorCommand _motorA);
00139 void PFcomboPwmMode(tSensors link, int channel, ePWMMotorCommand _motorB, ePWMMotorCommand _motorA);
00140 void encodeBuffer(tByteArray &iBuffer, tByteArray &oBuffer);
00141 void transmitIR(tSensors link, tByteArray &oBuffer, int channel);
00142 
00143 #ifdef _DEBUG_DRIVER_
00144 void decToBin(int number, int length, string &output);
00145 void debugIR(tByteArray &data);
00146 
00147 
00148 /**
00149  * Returns a binary representation in a string of an int with specified length
00150  *
00151  * Note: this function is only available when driver is compiled with _DEBUG_DRIVER_ defined.
00152  * @param number the number to be converted to a binary representation
00153  * @param length number of bits to convert
00154  * @param output the number converted to binary representation
00155  */
00156 void decToBin(int number, int length, string &output) {
00157   memset(output, 0, sizeof(output));
00158   output = "";
00159 
00160   for (int i = 0; i < length; i++) {
00161     output += (number & (1<< (length - 1))) >> (length - 1);
00162     number = number << 1;
00163   }
00164 }
00165 
00166 
00167 /**
00168  * Print out the buffer in question to the screen using the following format:
00169  *
00170  * @<index@> @<binary reprentation@> @<hex representation@>
00171  *
00172  * 0 11001100 0xCC
00173  *
00174  * It pauses for 10 seconds between each screenful, accompanied by a beep.
00175  *
00176  * Note: this function is only available when driver is compiled with _DEBUG_DRIVER_ defined.
00177  * @param data the data to be displayed as binary/hex numbers
00178  */
00179 void debugIR(tByteArray &data) {
00180   string _output;
00181   for (int i = 0; i < MAX_ARR_SIZE; i++) {
00182     if ((i != 0) && (i % 8 == 0)) {
00183       wait1Msec(10000);
00184       PlaySound(soundBlip);
00185       eraseDisplay();
00186     }
00187     decToBin(data[i], 8, _output);
00188     StringFormat(_output, "%2d %s", i, _output);
00189     nxtDisplayTextLine(i % 8, "%s 0x%02x", _output, ubyteToInt(data[i]));
00190   }
00191   wait1Msec(10000);
00192 }
00193 #endif // _DEBUG_DRIVER_
00194 
00195 
00196 /**
00197  * Control two motors using the ComboDirectMode.  This mode does not allow for fine grained
00198  * speed control.
00199  * @param link the sensor port number
00200  * @param channel the channel of the receiver we wish to communicate with, numbered 0-3
00201  * @param _motorB the command to be sent to Motor B
00202  * @param _motorA the command to be sent to Motor A
00203  */
00204 void PFcomboDirectMode(tSensors link, int channel, eCDMMotorCommand _motorB, eCDMMotorCommand _motorA) {
00205   tByteArray _iBuffer;
00206   tByteArray _oBuffer;
00207 
00208   // Clear the input buffer before we start filling it
00209   memset(_iBuffer, 0, sizeof(tByteArray));
00210   memset(_oBuffer, 0, sizeof(tByteArray));
00211 
00212   // This is the unencoded command for the IR receiver
00213   _iBuffer[0] = (channel << 4) + 1;
00214   _iBuffer[1] = ((ubyte)_motorB << 6) + ((ubyte)_motorA << 4);
00215   _iBuffer[1] += 0xF ^ (_iBuffer[0] >> 4) ^ (_iBuffer[0] & 0xF) ^ (_iBuffer[1] >> 4);
00216 
00217   // Setup the header of the I2C packet
00218   _oBuffer[0] = 16;    // Total msg length
00219   _oBuffer[1] = 0x02;  // I2C device address
00220   _oBuffer[2] = 0x42;  // Internal register
00221 
00222   // Generate the data payload
00223   encodeBuffer(_iBuffer, (tByteArray)_oBuffer);                       // Encode PF command
00224 
00225   // Setup the tail end of the packet
00226   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE] = 11;         // Total IR command length
00227   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE + 1] = 0x02;   // IRLink mode 0x02 is PF motor
00228   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE + 2] = 0x01;   // Start transmitting
00229 
00230   transmitIR(link, _oBuffer, channel);
00231 }
00232 
00233 
00234 /*
00235   =============================================================================
00236   Combo Direct Mode
00237         | Nib 1 | Nib 2 | Nib 3 | Nib 4 |
00238   start  a 1 C C B B B B A A A A L L L L stop
00239 
00240  */
00241 /**
00242  * Control two motors using the ComboPWMMode.  This mode allows for fine grained
00243  * speed control.
00244  * @param link the sensor port number
00245  * @param channel the channel of the receiver we wish to communicate with, numbered 0-3
00246  * @param _motorB the command to be sent to Motor B
00247  * @param _motorA the command to be sent to Motor A
00248  */
00249 void PFcomboPwmMode(tSensors link, int channel, ePWMMotorCommand _motorB, ePWMMotorCommand _motorA) {
00250   tByteArray _iBuffer;
00251   tByteArray _oBuffer;
00252 
00253   // Clear the input buffer before we start filling it
00254   memset(_iBuffer, 0, sizeof(tByteArray));
00255   memset(_oBuffer, 0, sizeof(tByteArray));
00256 
00257   // This is the unencoded command for the IR receiver
00258   _iBuffer[0] = (1 << 6) + (channel << 4) + _motorA;
00259   _iBuffer[1] = ((ubyte)_motorB << 4);
00260   //_iBuffer[1] = (_motorB << 4) + (0xF ^ ((1 << 2) + channel) ^ _motorA ^ _motorB);
00261   _iBuffer[1] += 0xF ^ (_iBuffer[0] >> 4) ^ (_iBuffer[0] & 0xF) ^ (_iBuffer[1] >> 4);
00262 
00263   // Setup the header of the I2C packet
00264   _oBuffer[0] = 16;    // Total msg length
00265   _oBuffer[1] = 0x02;  // I2C device address
00266   _oBuffer[2] = 0x42;  // Internal register
00267   // Generate the data payload
00268   encodeBuffer(_iBuffer, _oBuffer);                       // Encode PF command
00269 
00270   // Setup the tail end of the packet
00271   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE] = 11;         // Total IR command length
00272   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE + 1] = 0x02;   // IRLink mode 0x02 is PF motor
00273   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE + 2] = 0x01;   // Start transmitting
00274   //_oBuffer[3] = 0x80;
00275 
00276   transmitIR(link, _oBuffer, channel);
00277 }
00278 
00279 
00280 /*
00281   =============================================================================
00282   Single Pin Output Mode
00283         | Nib 1 | Nib 2 | Nib 3 | Nib 4 |
00284   start  T 0 C C a 1 M 0 D D D D L L L L stop
00285 
00286  */
00287 /**
00288  * Control one motor with no timeout. This mode allows for fine grained
00289  * speed control.
00290  * @param link the sensor port number
00291  * @param channel the channel of the receiver we wish to communicate with, numbered 0-3
00292  * @param _motor the motor to be controlled, 0 or 1, for A or B
00293  * @param _motorCmd the command to send to the motor, 0-15
00294  */
00295 void PFsinglePinOutputMode(tSensors link, ubyte channel, ubyte _motor, ePWMMotorCommand _motorCmd) {
00296   tByteArray _iBuffer;
00297   tByteArray _oBuffer;
00298 
00299   toggle[link] ^= 1;
00300 
00301   // Clear the input buffer before we start filling it
00302   memset(_iBuffer, 0, sizeof(tByteArray));
00303   memset(_oBuffer, 0, sizeof(tByteArray));
00304 
00305   // This is the unencoded command for the IR receiver
00306   _iBuffer[0] = (toggle[link] <<7 ) + (channel << 4) + (1 << 2) + _motor;
00307   _iBuffer[1] = ((ubyte)_motorCmd << 4);
00308   _iBuffer[1] += 0xF ^ (_iBuffer[0] >> 4) ^ (_iBuffer[0] & 0xF) ^ (_iBuffer[1] >> 4);
00309 
00310   // Setup the header of the I2C packet
00311   _oBuffer[0] = 16;    // Total msg length
00312   _oBuffer[1] = 0x02;  // I2C device address
00313   _oBuffer[2] = 0x42;  // Internal register
00314   // Generate the data payload
00315   encodeBuffer(_iBuffer, _oBuffer);                       // Encode PF command
00316 
00317   // Setup the tail end of the packet
00318   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE] = 11;         // Total IR command length
00319   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE + 1] = 0x02;   // IRLink mode 0x02 is PF motor
00320   _oBuffer[BUF_HEADSIZE + BUF_DATASIZE + 2] = 0x01;   // Start transmitting
00321 
00322   transmitIR(link, _oBuffer, channel);
00323 }
00324 
00325 
00326 /**
00327  * Control one motor with no timeout. This mode allows for fine grained
00328  * speed control.
00329  * @param pfmotor the motor to which to send the command
00330  * @param _motorCmd the command to send to the motor, 0-15
00331  */
00332 void PFMotor(tPFmotor pfmotor, ePWMMotorCommand _motorCmd) {
00333   PFsinglePinOutputMode((tSensors)PFSPORT(pfmotor), (ubyte)PFCHAN(pfmotor), (ubyte)PFMOT(pfmotor), _motorCmd);
00334 }
00335 
00336 
00337 /**
00338  * Encode the input buffer into a special format for the IRLink.
00339  *
00340  * Note: this is an internal function and should not be called directly.
00341  * @param iBuffer the data that is be encoded
00342  * @param oBuffer output buffer for encoded data
00343  */
00344 void encodeBuffer(tByteArray &iBuffer, tByteArray &oBuffer) {
00345   int _oByteIdx = 0;
00346   int _oBitIdx = 0;
00347   int _iIndex = 0;              // _iBUffer bit index
00348   int _oIndex = 0;              // _oBuffer bit index
00349   int _len = 0;
00350 
00351   //debugIR(iBuffer);
00352   // Calculate the size of the output bit index
00353   _oIndex = (8 * (MAX_ARR_SIZE - BUF_HEADSIZE)) - 1;
00354 
00355   // Start bit is a special case and is encoded as 0x80
00356   oBuffer[START_DATA] = 0x80; // Start bit
00357   _oIndex -= 8;                   // move the index along 8 bits.
00358 
00359   // Bits in the input buffer are encoded as follows:
00360   // 1 is encoded as 10000
00361   // 0 is encoded as 100
00362   // The encoded bits are tacked onto the end of the output
00363   // buffer, byte boundaries are ignored.
00364   for (_iIndex = 0; _iIndex < (2 * 8); _iIndex++) {
00365     _len = (iBuffer[_iIndex / 8] & 0x80) ? 5 : 3;
00366     _oByteIdx = (MAX_ARR_SIZE - 1) - (_oIndex / 8);
00367     _oBitIdx = _oIndex % 8;
00368     oBuffer[_oByteIdx] += (1 << _oBitIdx);
00369     _oIndex -= _len;
00370     iBuffer[_iIndex / 8] <<= 1;
00371   }
00372 
00373   // Finally, add the stop byte to the end of our command
00374   _oByteIdx = (MAX_ARR_SIZE - 1) - (_oIndex / 8);
00375   _oBitIdx = _oIndex % 8;
00376   oBuffer[_oByteIdx] += (1 << _oBitIdx);
00377 
00378 }
00379 
00380 
00381 /**
00382  * Send the command to the IRLink Sensor for transmission.
00383  *
00384  * Note: this is an internal function and should not be called directly.
00385  * If the driver is compiled with _DEBUG_DRIVER_, this function will call
00386  * debugIR() prior to transmitting the data for debugging purposes.
00387  * @param link the sensor port number
00388  * @param oBuffer the data that is be transmitted
00389  * @param channel the channel number of the receiver
00390  */
00391 void transmitIR(tSensors link, tByteArray &oBuffer, int channel) {
00392   long starttime = 0;
00393 #ifdef _DEBUG_DRIVER_
00394   debugIR(oBuffer);
00395 #endif // _DEBUG_DRIVER_
00396 
00397   // Channel is assumed to be 1-4 in the specs.
00398   // channel++;
00399 
00400   // Message should be sent 5 times according to the PF specs
00401   // Specific timing has to be used to prevent interence with other
00402   // transmitters.
00403 
00404   // First transmission
00405   wait1Msec((4 - channel) * 16);
00406   starttime = nPgmTime;
00407   if (!writeI2C(link, oBuffer)) return;
00408 
00409   // Second transmission
00410   wait1Msec(5 * 16 - (nPgmTime - starttime));
00411   starttime = nPgmTime;
00412   if (!writeI2C(link, oBuffer)) return;
00413 
00414   // Third transmission
00415   wait1Msec(5 * 16 - (nPgmTime - starttime));
00416   starttime = nPgmTime;
00417   if (!writeI2C(link, oBuffer)) return;
00418 
00419   // Fourth transmission
00420   wait1Msec((6 + (2*channel) * 16) - (nPgmTime - starttime));
00421   starttime = nPgmTime;
00422   if (!writeI2C(link, oBuffer)) return;
00423 
00424   // Fifth transmission
00425   wait1Msec((6 + (2*channel) * 16) - (nPgmTime - starttime));
00426   if (!writeI2C(link, oBuffer)) return;
00427 }
00428 
00429 #endif // _HTIRL_H_
00430 
00431 /*
00432  * $Id: hitechnic-irlink.h 133 2013-03-10 15:15:38Z xander $
00433  */
00434 /* @} */
00435 /* @} */