|
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 /* @} */