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

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