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