1 /* 2 * Author: Jon Trulson <jtrulson (at) ics.com> 3 * Copyright (c) 2015 Intel Corporation. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 #include <unistd.h> 26 #include <math.h> 27 #include <iostream> 28 #include <string> 29 30 #include "adafruitms1438.h" 31 32 using namespace upm; 33 using namespace std; 34 35 36 AdafruitMS1438::AdafruitMS1438(int bus, uint8_t address) : 37 m_pca9685(new PCA9685(bus, address)) 38 { 39 setupPinMaps(); 40 41 // set a default period of 50Hz 42 setPWMPeriod(50); 43 44 // disable all PWM's (4 of them). They are shared with each other 45 // (stepper/DC), so just disable the DC motors here 46 disableMotor(MOTOR_M1); 47 disableMotor(MOTOR_M2); 48 disableMotor(MOTOR_M3); 49 disableMotor(MOTOR_M4); 50 51 // Set all 'on time' registers to 0 52 m_pca9685->ledOnTime(PCA9685_ALL_LED, 0); 53 54 // set the default stepper config at 200 steps per rev 55 stepConfig(STEPMOTOR_M12, 200); 56 stepConfig(STEPMOTOR_M34, 200); 57 } 58 59 AdafruitMS1438::~AdafruitMS1438() 60 { 61 delete m_pca9685; 62 } 63 64 void AdafruitMS1438::initClock(STEPMOTORS_T motor) 65 { 66 gettimeofday(&m_stepConfig[motor].startTime, NULL); 67 } 68 69 uint32_t AdafruitMS1438::getMillis(STEPMOTORS_T motor) 70 { 71 struct timeval elapsed, now; 72 uint32_t elapse; 73 74 // get current time 75 gettimeofday(&now, NULL); 76 77 struct timeval startTime = m_stepConfig[motor].startTime; 78 79 // compute the delta since m_startTime 80 if( (elapsed.tv_usec = now.tv_usec - startTime.tv_usec) < 0 ) 81 { 82 elapsed.tv_usec += 1000000; 83 elapsed.tv_sec = now.tv_sec - startTime.tv_sec - 1; 84 } 85 else 86 { 87 elapsed.tv_sec = now.tv_sec - startTime.tv_sec; 88 } 89 90 elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000)); 91 92 // never return 0 93 if (elapse == 0) 94 elapse = 1; 95 96 return elapse; 97 } 98 99 // setup the pin mappings of the pca9685 outputs to the proper motor controls 100 void AdafruitMS1438::setupPinMaps() 101 { 102 // first the dc motors 103 m_dcMotors[0] = (DC_PINMAP_T){ 8, 10, 9 }; 104 m_dcMotors[1] = (DC_PINMAP_T){ 13, 11, 12 }; 105 m_dcMotors[2] = (DC_PINMAP_T){ 2, 4, 3 }; 106 m_dcMotors[3] = (DC_PINMAP_T){ 7, 5, 6 }; 107 108 // now the 2 steppers 109 m_stepMotors[0] = (STEPPER_PINMAP_T){ 8, 10, 9, 110 13, 11, 12 }; 111 m_stepMotors[1] = (STEPPER_PINMAP_T){ 2, 4, 3, 112 7, 5, 6 }; 113 } 114 115 void AdafruitMS1438::setPWMPeriod(float hz) 116 { 117 // must be in sleep mode to set the prescale register 118 m_pca9685->setModeSleep(true); 119 m_pca9685->setPrescaleFromHz(hz); 120 m_pca9685->setModeSleep(false); 121 } 122 123 void AdafruitMS1438::enableMotor(DCMOTORS_T motor) 124 { 125 m_pca9685->ledFullOff(m_dcMotors[motor].pwm, false); 126 } 127 128 void AdafruitMS1438::disableMotor(DCMOTORS_T motor) 129 { 130 m_pca9685->ledFullOff(m_dcMotors[motor].pwm, true); 131 } 132 133 void AdafruitMS1438::enableStepper(STEPMOTORS_T motor) 134 { 135 m_pca9685->ledFullOff(m_stepMotors[motor].pwmA, false); 136 m_pca9685->ledFullOff(m_stepMotors[motor].pwmB, false); 137 } 138 139 void AdafruitMS1438::disableStepper(STEPMOTORS_T motor) 140 { 141 m_pca9685->ledFullOff(m_stepMotors[motor].pwmA, true); 142 m_pca9685->ledFullOff(m_stepMotors[motor].pwmB, true); 143 } 144 145 void AdafruitMS1438::setMotorSpeed(DCMOTORS_T motor, int speed) 146 { 147 if (speed < 0) 148 speed = 0; 149 150 if (speed > 100) 151 speed = 100; 152 153 float percent = float(speed) / 100.0; 154 155 // make sure that the FullOn bit is turned off, or the speed setting 156 // (PWM duty cycle) won't have any effect. 157 m_pca9685->ledFullOn(m_dcMotors[motor].pwm, false); 158 159 // set the PWM duty cycle 160 m_pca9685->ledOffTime(m_dcMotors[motor].pwm, int(4095.0 * percent)); 161 } 162 163 void AdafruitMS1438::setStepperSpeed(STEPMOTORS_T motor, int speed) 164 { 165 m_stepConfig[motor].stepDelay = 60 * 1000 / 166 m_stepConfig[motor].stepsPerRev / speed; 167 } 168 169 void AdafruitMS1438::setMotorDirection(DCMOTORS_T motor, DIRECTION_T dir) 170 { 171 if (dir & 0x01) 172 { 173 m_pca9685->ledFullOn(m_dcMotors[motor].in1, true); 174 m_pca9685->ledFullOff(m_dcMotors[motor].in1, false); 175 } 176 else 177 { 178 m_pca9685->ledFullOff(m_dcMotors[motor].in1, true); 179 m_pca9685->ledFullOn(m_dcMotors[motor].in1, false); 180 } 181 182 if (dir & 0x02) 183 { 184 m_pca9685->ledFullOn(m_dcMotors[motor].in2, true); 185 m_pca9685->ledFullOff(m_dcMotors[motor].in2, false); 186 } 187 else 188 { 189 m_pca9685->ledFullOff(m_dcMotors[motor].in2, true); 190 m_pca9685->ledFullOn(m_dcMotors[motor].in2, false); 191 } 192 } 193 194 void AdafruitMS1438::setStepperDirection(STEPMOTORS_T motor, DIRECTION_T dir) 195 { 196 switch (dir) 197 { 198 case DIR_CW: 199 m_stepConfig[motor].stepDirection = 1; 200 break; 201 case DIR_CCW: 202 m_stepConfig[motor].stepDirection = -1; 203 break; 204 default: // default to 1 if DIR_NONE specified 205 m_stepConfig[motor].stepDirection = 1; 206 break; 207 } 208 } 209 210 void AdafruitMS1438::stepConfig(STEPMOTORS_T motor, unsigned int stepsPerRev) 211 { 212 m_stepConfig[motor].stepsPerRev = stepsPerRev; 213 m_stepConfig[motor].currentStep = 0; 214 m_stepConfig[motor].stepDelay = 0; 215 m_stepConfig[motor].stepDirection = 1; // forward 216 217 // now, setup the control pins - we want both FULL ON and FULL OFF. 218 // Since FULL OFF has precedence, we can then control the steps by 219 // just turning on/off the FULL OFF bit for the relevant outputs 220 221 m_pca9685->ledFullOff(m_stepMotors[motor].pwmA, true); 222 m_pca9685->ledFullOn(m_stepMotors[motor].pwmA, true); 223 224 m_pca9685->ledFullOff(m_stepMotors[motor].pwmB, true); 225 m_pca9685->ledFullOn(m_stepMotors[motor].pwmB, true); 226 227 m_pca9685->ledFullOff(m_stepMotors[motor].in1A, true); 228 m_pca9685->ledFullOn(m_stepMotors[motor].in1A, true); 229 230 m_pca9685->ledFullOff(m_stepMotors[motor].in2A, true); 231 m_pca9685->ledFullOn(m_stepMotors[motor].in2A, true); 232 233 m_pca9685->ledFullOff(m_stepMotors[motor].in1B, true); 234 m_pca9685->ledFullOn(m_stepMotors[motor].in1B, true); 235 236 m_pca9685->ledFullOff(m_stepMotors[motor].in2B, true); 237 m_pca9685->ledFullOn(m_stepMotors[motor].in2B, true); 238 } 239 240 void AdafruitMS1438::stepperStep(STEPMOTORS_T motor) 241 { 242 int step = m_stepConfig[motor].currentStep % 4; 243 244 // Step I0 I1 I2 I3 245 // 1 1 0 1 0 246 // 2 0 1 1 0 247 // 3 0 1 0 1 248 // 4 1 0 0 1 249 250 // we invert the logic since we are essentially toggling an OFF bit, 251 // not an ON bit. 252 switch (step) 253 { 254 case 0: // 1010 255 m_pca9685->ledFullOff(m_stepMotors[motor].in1A, false); 256 m_pca9685->ledFullOff(m_stepMotors[motor].in2A, true); 257 m_pca9685->ledFullOff(m_stepMotors[motor].in1B, false); 258 m_pca9685->ledFullOff(m_stepMotors[motor].in2B, true); 259 break; 260 case 1: // 0110 261 m_pca9685->ledFullOff(m_stepMotors[motor].in1A, true); 262 m_pca9685->ledFullOff(m_stepMotors[motor].in2A, false); 263 m_pca9685->ledFullOff(m_stepMotors[motor].in1B, false); 264 m_pca9685->ledFullOff(m_stepMotors[motor].in2B, true); 265 break; 266 case 2: //0101 267 m_pca9685->ledFullOff(m_stepMotors[motor].in1A, true); 268 m_pca9685->ledFullOff(m_stepMotors[motor].in2A, false); 269 m_pca9685->ledFullOff(m_stepMotors[motor].in1B, true); 270 m_pca9685->ledFullOff(m_stepMotors[motor].in2B, false); 271 break; 272 case 3: //1001 273 m_pca9685->ledFullOff(m_stepMotors[motor].in1A, false); 274 m_pca9685->ledFullOff(m_stepMotors[motor].in2A, true); 275 m_pca9685->ledFullOff(m_stepMotors[motor].in1B, true); 276 m_pca9685->ledFullOff(m_stepMotors[motor].in2B, false); 277 break; 278 } 279 } 280 281 void AdafruitMS1438::stepperSteps(STEPMOTORS_T motor, unsigned int steps) 282 { 283 while (steps > 0) 284 { 285 if (getMillis(motor) >= m_stepConfig[motor].stepDelay) 286 { 287 // reset the clock 288 initClock(motor); 289 290 m_stepConfig[motor].currentStep += m_stepConfig[motor].stepDirection; 291 292 if (m_stepConfig[motor].stepDirection == 1) 293 { 294 if (m_stepConfig[motor].currentStep >= 295 m_stepConfig[motor].stepsPerRev) 296 m_stepConfig[motor].currentStep = 0; 297 } 298 else 299 { 300 if (m_stepConfig[motor].currentStep <= 0) 301 m_stepConfig[motor].currentStep = 302 m_stepConfig[motor].stepsPerRev; 303 } 304 305 steps--; 306 stepperStep(motor); 307 } 308 } 309 } 310 311