Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 1 | #include "motors/motor.h" |
| 2 | |
| 3 | #include <limits.h> |
| 4 | |
| 5 | #include <array> |
| 6 | |
| 7 | #include "motors/peripheral/configuration.h" |
| 8 | |
| 9 | #include <stdio.h> |
| 10 | #include <inttypes.h> |
| 11 | #include "motors/core/time.h" |
| 12 | #include "motors/usb/usb_serial.h" |
| 13 | #include "motors/peripheral/can.h" |
| 14 | |
| 15 | namespace frc971 { |
| 16 | namespace salsa { |
| 17 | namespace { |
| 18 | |
| 19 | constexpr int kDeadtimeCompensation = 9; |
| 20 | |
| 21 | uint32_t CalculateOnTime(uint32_t width, bool flip) { |
| 22 | if (width > 0) { |
| 23 | width += kDeadtimeCompensation; |
| 24 | if (flip) { |
| 25 | width += 1; |
| 26 | } |
| 27 | } |
| 28 | return 1500 - width / 2; |
| 29 | } |
| 30 | |
| 31 | uint32_t CalculateOffTime(uint32_t width, bool flip) { |
| 32 | if (width > 0) { |
| 33 | width += kDeadtimeCompensation; |
| 34 | if (!flip) { |
| 35 | width += 1; |
| 36 | } |
| 37 | } |
| 38 | return 1500 + width / 2; |
| 39 | } |
| 40 | |
| 41 | } // namespace |
| 42 | |
| 43 | Motor::Motor(BigFTM *pwm_ftm, LittleFTM *encoder_ftm, MotorControls *controls) |
| 44 | : pwm_ftm_(pwm_ftm), encoder_ftm_(encoder_ftm), controls_(controls) {} |
| 45 | |
| 46 | static_assert((BUS_CLOCK_FREQUENCY % SWITCHING_FREQUENCY) == 0, |
| 47 | "Switching frequency needs to divide the bus clock frequency"); |
| 48 | |
| 49 | static_assert(BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY < UINT16_MAX, |
| 50 | "Need to prescale"); |
| 51 | |
| 52 | void Motor::Init() { |
| 53 | PORTE_PCR24 = PORT_PCR_MUX(1); |
| 54 | PORTB_PCR0 = PORT_PCR_MUX(6); |
| 55 | PORTB_PCR1 = PORT_PCR_MUX(6); |
| 56 | |
| 57 | PORTC_PCR1 = PORT_PCR_MUX(4); |
| 58 | PORTC_PCR2 = PORT_PCR_MUX(4); |
| 59 | PORTC_PCR3 = PORT_PCR_MUX(4); |
| 60 | PORTC_PCR4 = PORT_PCR_MUX(4); |
| 61 | PORTD_PCR4 = PORT_PCR_MUX(4); |
| 62 | PORTD_PCR5 = PORT_PCR_MUX(4); |
| 63 | |
| 64 | // PWMSYNC doesn't matter because we set SYNCMODE down below. |
| 65 | pwm_ftm_->MODE = FTM_MODE_WPDIS; |
| 66 | pwm_ftm_->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN; |
| 67 | encoder_ftm_->MODE = FTM_MODE_WPDIS; |
| 68 | encoder_ftm_->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN; |
| 69 | |
| 70 | pwm_ftm_->SC = FTM_SC_CLKS(0) /* Disable counting for now */; |
| 71 | encoder_ftm_->SC = |
| 72 | FTM_SC_CLKS(1) /* Use the system clock (not sure it matters) */ | |
| 73 | FTM_SC_PS(0) /* Don't prescale the clock (not sure it matters) */; |
| 74 | |
| 75 | pwm_ftm_->CNTIN = encoder_ftm_->CNTIN = 0; |
| 76 | pwm_ftm_->CNT = encoder_ftm_->CNT = 0; |
| 77 | |
| 78 | pwm_ftm_->MOD = (BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY) - 1; |
| 79 | encoder_ftm_->MOD = controls_->counts_per_revolution() - 1; |
| 80 | |
| 81 | // Put them all into combine active-high mode, and all the low ones staying on |
| 82 | // all the time by default. |
| 83 | // TODO: update comment |
| 84 | pwm_ftm_->C0SC = FTM_CSC_ELSA; |
| 85 | pwm_ftm_->C0V = 0; |
| 86 | pwm_ftm_->C1SC = FTM_CSC_ELSA; |
| 87 | pwm_ftm_->C1V = 0; |
| 88 | pwm_ftm_->C2SC = FTM_CSC_ELSA; |
| 89 | pwm_ftm_->C2V = 0; |
| 90 | pwm_ftm_->C3SC = FTM_CSC_ELSA; |
| 91 | pwm_ftm_->C3V = 0; |
| 92 | pwm_ftm_->C4SC = FTM_CSC_ELSA; |
| 93 | pwm_ftm_->C4V = 0; |
| 94 | pwm_ftm_->C5SC = FTM_CSC_ELSA; |
| 95 | pwm_ftm_->C5V = 0; |
| 96 | |
| 97 | // I think you have to set this to something other than 0 for the quadrature |
| 98 | // encoder mode to actually work? This is "input capture on rising edge only", |
| 99 | // which should be fine. |
| 100 | encoder_ftm_->C0SC = FTM_CSC_ELSA; |
| 101 | encoder_ftm_->C1SC = FTM_CSC_ELSA; |
| 102 | |
| 103 | // Initialize all the channels to 0. |
| 104 | // TODO(Brian): Is this really what we want? |
| 105 | pwm_ftm_->OUTINIT = 0; |
| 106 | |
| 107 | pwm_ftm_->COMBINE = FTM_COMBINE_SYNCEN3 /* Synchronize updates usefully */ | |
| 108 | FTM_COMBINE_DTEN3 /* Enable deadtime */ | |
| 109 | FTM_COMBINE_COMP3 /* Make them complementary */ | |
| 110 | FTM_COMBINE_COMBINE3 /* Combine the channels */ | |
| 111 | FTM_COMBINE_SYNCEN2 /* Synchronize updates usefully */ | |
| 112 | FTM_COMBINE_DTEN2 /* Enable deadtime */ | |
| 113 | FTM_COMBINE_COMP2 /* Make them complementary */ | |
| 114 | FTM_COMBINE_COMBINE2 /* Combine the channels */ | |
| 115 | FTM_COMBINE_SYNCEN1 /* Synchronize updates usefully */ | |
| 116 | FTM_COMBINE_DTEN1 /* Enable deadtime */ | |
| 117 | FTM_COMBINE_COMP1 /* Make them complementary */ | |
| 118 | FTM_COMBINE_COMBINE1 /* Combine the channels */ | |
| 119 | FTM_COMBINE_SYNCEN0 /* Synchronize updates usefully */ | |
| 120 | FTM_COMBINE_DTEN0 /* Enable deadtime */ | |
| 121 | FTM_COMBINE_COMP0 /* Make them complementary */ | |
| 122 | FTM_COMBINE_COMBINE0 /* Combine the channels */; |
| 123 | |
| 124 | // Set a deadtime of 500ns. |
| 125 | // TODO: update comment |
| 126 | pwm_ftm_->DEADTIME = |
| 127 | FTM_DEADTIME_DTPS(0) /* Prescaler of 1 */ | FTM_DEADTIME_DTVAL(9); |
| 128 | |
| 129 | // All of the channels are active high. |
| 130 | pwm_ftm_->POL = 0; |
| 131 | |
| 132 | encoder_ftm_->FILTER = FTM_FILTER_CH0FVAL(0) /* No filter */ | |
| 133 | FTM_FILTER_CH1FVAL(0) /* No filter */; |
| 134 | |
| 135 | // Could set PHAFLTREN and PHBFLTREN here to enable the filters. |
| 136 | encoder_ftm_->QDCTRL = FTM_QDCTRL_QUADEN; |
| 137 | |
| 138 | pwm_ftm_->SYNCONF = |
| 139 | FTM_SYNCONF_HWWRBUF /* Hardware trigger flushes switching points */ | |
| 140 | FTM_SYNCONF_SWWRBUF /* Software trigger flushes switching points */ | |
| 141 | FTM_SYNCONF_SWRSTCNT /* Software trigger resets the count */ | |
| 142 | FTM_SYNCONF_SYNCMODE /* Use the new synchronization mode */; |
| 143 | encoder_ftm_->SYNCONF = |
| 144 | FTM_SYNCONF_SWWRBUF /* Software trigger flushes MOD */ | |
| 145 | FTM_SYNCONF_SWRSTCNT /* Software trigger resets the count */ | |
| 146 | FTM_SYNCONF_SYNCMODE /* Use the new synchronization mode */; |
| 147 | |
| 148 | // Don't want any intermediate loading points. |
| 149 | pwm_ftm_->PWMLOAD = 0; |
| 150 | |
| 151 | // This has to happen after messing with SYNCONF, and should happen after |
| 152 | // messing with various other things so the values can get flushed out of the |
| 153 | // buffers. |
| 154 | pwm_ftm_->SYNC = |
| 155 | FTM_SYNC_SWSYNC /* Flush everything out right now */ | |
| 156 | FTM_SYNC_CNTMAX /* Load new values at the end of the cycle */; |
| 157 | encoder_ftm_->SYNC = FTM_SYNC_SWSYNC /* Flush everything out right now */; |
| 158 | |
| 159 | // Wait for the software synchronization to finish. |
| 160 | while (pwm_ftm_->SYNC & FTM_SYNC_SWSYNC) { |
| 161 | } |
| 162 | while (encoder_ftm_->SYNC & FTM_SYNC_SWSYNC) { |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | void Motor::Zero() { |
| 167 | #if 0 |
| 168 | while (true) { |
| 169 | if (GPIO_BITBAND(GPIOE_PDIR, 24)) { |
| 170 | encoder_ftm_->CNT = 0; |
| 171 | break; |
| 172 | } |
| 173 | } |
| 174 | #else |
| 175 | uint32_t scratch; |
| 176 | __disable_irq(); |
| 177 | // Stuff all of this in an inline assembly statement so we can make sure the |
| 178 | // compiler doesn't decide sticking constant loads etc in the middle of |
| 179 | // the loop is a good idea, because that increases the latency of recognizing |
| 180 | // the index pulse edge which makes velocity affect the zeroing accuracy. |
| 181 | __asm__ __volatile__( |
| 182 | // A label to restart the loop. |
| 183 | "0:\n" |
| 184 | // Load the current PDIR value for the pin we care about. |
| 185 | "ldr %[scratch], [%[pdir_word]]\n" |
| 186 | // Terminate the loop if it's non-0. |
| 187 | "cbnz %[scratch], 1f\n" |
| 188 | // Go back around again. |
| 189 | "b 0b\n" |
| 190 | // A label to finish the loop. |
| 191 | "1:\n" |
| 192 | // Reset the count once we're down here. It doesn't actually matter what |
| 193 | // value we store because writing anything resets it to CNTIN (ie 0). |
| 194 | "str %[scratch], [%[cnt]]\n" |
| 195 | : [scratch] "=&l"(scratch) |
| 196 | : [pdir_word] "l"(&GPIO_BITBAND(GPIOE_PDIR, 24)), |
| 197 | [cnt] "l"(&encoder_ftm_->CNT)); |
| 198 | __enable_irq(); |
| 199 | #endif |
| 200 | } |
| 201 | |
| 202 | void Motor::Start() { |
| 203 | pwm_ftm_->SC = FTM_SC_TOIE /* Interrupt on overflow */ | |
| 204 | FTM_SC_CLKS(1) /* Use the system clock */ | |
| 205 | FTM_SC_PS(0) /* Don't prescale the clock */; |
| 206 | pwm_ftm_->MODE &= ~FTM_MODE_WPDIS; |
| 207 | encoder_ftm_->MODE &= ~FTM_MODE_WPDIS; |
| 208 | |
| 209 | NVIC_ENABLE_IRQ(IRQ_FTM0); |
| 210 | } |
| 211 | |
| 212 | void Motor::HandleInterrupt() { |
| 213 | const uint32_t starting_sc = pwm_ftm_->SC; |
| 214 | pwm_ftm_->SC = starting_sc & ~FTM_SC_TOF; |
| 215 | |
| 216 | const uint32_t start_count = pwm_ftm_->CNT; |
| 217 | __asm__("":::"memory"); |
| 218 | const MediumAdcReadings adc_readings = AdcReadMedium(); |
| 219 | |
| 220 | // Turn the light on if we're starting too late (this generally means a |
| 221 | // previous iteration took too long). |
| 222 | if (start_count > 100) { |
| 223 | GPIOC_PSOR = 1 << 5; |
| 224 | } |
| 225 | |
| 226 | ReadingsToBalance to_balance{{0, 0, 0}, {0, 0, 0}}; |
| 227 | { |
| 228 | for (int reading = 0; reading < 2; ++reading) { |
| 229 | for (int phase = 0; phase < 3; ++phase) { |
| 230 | to_balance.Add(phase, adc_readings.motor_currents[phase][reading]); |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | const BalancedReadings balanced = BalanceReadings(to_balance); |
| 235 | |
| 236 | static float current_command = 0; |
| 237 | static uint32_t last_command_receive_time = 0; |
| 238 | { |
| 239 | unsigned char command_data[8]; |
| 240 | int command_length; |
| 241 | can_receive_command(command_data, &command_length); |
| 242 | if (command_length == 4) { |
| 243 | last_command_receive_time = micros(); |
| 244 | uint32_t result = command_data[0] << 24 | command_data[1] << 16 | |
| 245 | command_data[2] << 8 | command_data[3]; |
| 246 | current_command = static_cast<float>(result) / 1000.0f; |
| 247 | } |
| 248 | } |
| 249 | if (!time_after(time_add(last_command_receive_time, 100000), micros())) { |
| 250 | current_command = 0; |
| 251 | } |
| 252 | |
| 253 | static bool high_gear = false; |
| 254 | if (controls_->estimated_velocity() < -2015) { |
| 255 | high_gear = true; |
| 256 | } |
| 257 | if (current_command < 1) { |
| 258 | high_gear = false; |
| 259 | } |
| 260 | float current_now = current_command; |
| 261 | if (!high_gear) { |
| 262 | current_now = current_now * -120.0f / 120.0f; |
| 263 | } else { |
| 264 | current_now = current_now * 115.0f / 120.0f; |
| 265 | } |
| 266 | |
| 267 | #if 0 |
| 268 | static int status_send_counter = 0; |
| 269 | if (++status_send_counter == 1000) { |
| 270 | // can_send(uint32_t can_id, const unsigned char *data, unsigned int length) |
| 271 | unsigned char send_data[8]; |
| 272 | can_send(9 << 8, send_data, 0); |
| 273 | status_send_counter = 0; |
| 274 | printf("sent\n"); |
| 275 | } |
| 276 | #endif |
| 277 | |
| 278 | #define DO_CONTROLS 1 |
| 279 | #if DO_CONTROLS |
| 280 | static constexpr int kEncoderOffset = 810; |
| 281 | const uint32_t adjusted_count = (encoder_ftm_->CNT + kEncoderOffset) % 1024; |
| 282 | const ::std::array<uint32_t, 3> switching_points = |
| 283 | controls_->DoIteration(balanced.readings, adjusted_count, current_now); |
| 284 | constexpr uint32_t kMax = 2945; |
| 285 | static bool done = false; |
| 286 | bool done_now = false; |
| 287 | if (switching_points[0] > kMax || switching_points[1] > kMax || |
| 288 | switching_points[2] > kMax) { |
| 289 | done_now = true; |
| 290 | } |
| 291 | #define USE_ABSOLUTE_CUTOFF 1 |
| 292 | #if USE_ABSOLUTE_CUTOFF |
| 293 | static unsigned int current_done_count = 0; |
| 294 | bool current_done = false; |
| 295 | for (int phase = 0; phase < 3; ++phase) { |
| 296 | const float scaled_reading = |
| 297 | MotorControls::scale_current_reading(balanced.readings[0]); |
| 298 | static constexpr float kMaxBalancedCurrent = 190.0f; |
| 299 | if (scaled_reading > kMaxBalancedCurrent || |
| 300 | scaled_reading < -kMaxBalancedCurrent) { |
| 301 | current_done = true; |
| 302 | } |
| 303 | } |
| 304 | if (current_done) { |
| 305 | if (current_done_count > 5) { |
| 306 | done_now = true; |
| 307 | } |
| 308 | ++current_done_count; |
| 309 | } else { |
| 310 | current_done_count = 0; |
| 311 | } |
| 312 | #endif |
| 313 | if (done_now && !done) { |
| 314 | printf("done now\n"); |
| 315 | printf("switching_points %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", |
| 316 | switching_points[0], switching_points[1], switching_points[2]); |
| 317 | printf("raw %" PRIu16 " %" PRIu16 " %" PRIu16 " %" PRIu16 " %" PRIu16 |
| 318 | " %" PRIu16 "\n", |
| 319 | adc_readings.motor_currents[0][0], adc_readings.motor_currents[0][1], |
| 320 | adc_readings.motor_currents[1][0], adc_readings.motor_currents[1][1], |
| 321 | adc_readings.motor_currents[2][0], |
| 322 | adc_readings.motor_currents[2][1]); |
| 323 | printf("balanced %" PRIu16 " %" PRIu16 " %" PRIu16 "\n", |
| 324 | static_cast<uint16_t>(balanced.readings[0]), |
| 325 | static_cast<uint16_t>(balanced.readings[1]), |
| 326 | static_cast<uint16_t>(balanced.readings[2])); |
| 327 | done = true; |
| 328 | } |
| 329 | if (!done) { |
| 330 | pwm_ftm_->C0V = CalculateOnTime(switching_points[0], flip_time_offset_); |
| 331 | pwm_ftm_->C1V = CalculateOffTime(switching_points[0], flip_time_offset_); |
| 332 | pwm_ftm_->C2V = CalculateOnTime(switching_points[1], flip_time_offset_); |
| 333 | pwm_ftm_->C3V = CalculateOffTime(switching_points[1], flip_time_offset_); |
| 334 | pwm_ftm_->C4V = CalculateOnTime(switching_points[2], flip_time_offset_); |
| 335 | pwm_ftm_->C5V = CalculateOffTime(switching_points[2], flip_time_offset_); |
| 336 | flip_time_offset_ = !flip_time_offset_; |
| 337 | } else { |
| 338 | pwm_ftm_->C0V = 0; |
| 339 | pwm_ftm_->C1V = 0; |
| 340 | pwm_ftm_->C2V = 0; |
| 341 | pwm_ftm_->C3V = 0; |
| 342 | pwm_ftm_->C4V = 0; |
| 343 | pwm_ftm_->C5V = 0; |
| 344 | } |
| 345 | #define DO_FIXED_PULSE 0 |
| 346 | #elif DO_FIXED_PULSE |
| 347 | // An on-width of 60 with 30V in means about 50A through the motor and about |
| 348 | // 30W total power dumped by the motor. |
| 349 | #if 0 |
| 350 | static int i = 0; |
| 351 | ++i; |
| 352 | if (i == 2) { |
| 353 | pwm_ftm_->C3V = 111; |
| 354 | i = 0; |
| 355 | } else { |
| 356 | pwm_ftm_->C3V = 0; |
| 357 | } |
| 358 | #endif |
| 359 | pwm_ftm_->C0V = 0; |
| 360 | pwm_ftm_->C1V = 0; |
| 361 | pwm_ftm_->C2V = 0; |
| 362 | //pwm_ftm_->C3V = 100; |
| 363 | pwm_ftm_->C4V = 0; |
| 364 | pwm_ftm_->C5V = 0; |
| 365 | #endif |
| 366 | |
| 367 | #define PRINT_READINGS 0 |
| 368 | #if PRINT_READINGS |
| 369 | static int i = 0; |
| 370 | if (i == 100) { |
| 371 | i = 0; |
| 372 | printf("i=%" PRIu16 "\n", adc_readings.input_voltage); |
| 373 | } else if (i == 20) { |
| 374 | printf("0=%" PRIu16 " r=%" PRIu16 "\n", |
| 375 | adc_readings.motor_currents[0][0], adc_readings.motor_current_ref); |
| 376 | } else if (i == 40) { |
| 377 | printf("1=%" PRIu16 " r=%" PRIu16 "\n", |
| 378 | adc_readings.motor_currents[1][0], adc_readings.motor_current_ref); |
| 379 | } else if (i == 60) { |
| 380 | printf("2=%" PRIu16 " r=%" PRIu16 "\n", |
| 381 | adc_readings.motor_currents[2][0], adc_readings.motor_current_ref); |
| 382 | } else { |
| 383 | //printf("%" PRIu32 " to %" PRIu32 "\n", start_count, end_count); |
| 384 | } |
| 385 | ++i; |
| 386 | #define PRINT_ALL_READINGS 0 |
| 387 | #elif PRINT_ALL_READINGS |
| 388 | printf("ref=%" PRIu16 " 0.0=%" PRIu16 " 1.0=%" PRIu16 " 2.0=%" PRIu16 |
| 389 | " in=%" PRIu16 " 0.1=%" PRIu16 " 1.1=%" PRIu16 " 2.1=%" PRIu16 "\n", |
| 390 | adc_readings.motor_current_ref, adc_readings.motor_currents[0][0], |
| 391 | adc_readings.motor_currents[1][0], adc_readings.motor_currents[2][0], |
| 392 | adc_readings.input_voltage, adc_readings.motor_currents[0][1], |
| 393 | adc_readings.motor_currents[1][1], adc_readings.motor_currents[2][1]); |
| 394 | #define TAKE_SAMPLE 1 |
| 395 | #elif TAKE_SAMPLE |
| 396 | #if 0 |
| 397 | constexpr int kStartupWait = 50000; |
| 398 | #elif 0 |
| 399 | constexpr int kStartupWait = 0; |
| 400 | #elif 0 |
| 401 | constexpr int kStartupWait = 30000; |
| 402 | #elif 1 |
| 403 | constexpr int kStartupWait = 2 * 20000; |
| 404 | #endif |
| 405 | constexpr int kSubsampling = 1; |
| 406 | constexpr int kPoints = 5000; |
| 407 | constexpr int kSamplingEnd = kStartupWait + kPoints * kSubsampling; |
| 408 | (void)kSamplingEnd; |
| 409 | static int j = 0; |
| 410 | static int16_t data[kPoints][11]; |
| 411 | static int written = 0; |
| 412 | static_assert((kStartupWait % kSubsampling) == 0, "foo"); |
| 413 | static_assert((kPoints % kSubsampling) == 0, "foo"); |
| 414 | if (j < kStartupWait) { |
| 415 | // Wait to be started up. |
| 416 | ++j; |
| 417 | #define SAMPLE_UNTIL_DONE 0 |
| 418 | #if !SAMPLE_UNTIL_DONE |
| 419 | } else if (j < kSamplingEnd && (j % kSubsampling) == 0) { |
| 420 | #else |
| 421 | } else if (!done) { |
| 422 | #endif |
| 423 | { |
| 424 | const int index = ((j - kStartupWait) / kSubsampling) % kPoints; |
| 425 | auto point = data[index]; |
| 426 | #if 0 |
| 427 | point[0] = adc_readings.motor_currents[0][0]; |
| 428 | point[1] = adc_readings.motor_currents[1][0]; |
| 429 | point[2] = adc_readings.motor_currents[2][0]; |
| 430 | point[3] = adc_readings.motor_currents[0][1]; |
| 431 | point[4] = adc_readings.motor_currents[1][1]; |
| 432 | point[5] = adc_readings.motor_currents[2][1]; |
| 433 | #else |
| 434 | point[0] = balanced.readings[0] / 2; |
| 435 | point[1] = balanced.readings[1] / 2; |
| 436 | point[2] = balanced.readings[2] / 2; |
| 437 | #if 1 |
| 438 | point[3] = controls_->Debug(0); |
| 439 | point[4] = controls_->Debug(1); |
| 440 | point[5] = controls_->Debug(2); |
| 441 | point[6] = controls_->Debug(3); |
| 442 | point[7] = controls_->Debug(4); |
| 443 | point[8] = controls_->Debug(5); |
| 444 | point[9] = controls_->Debug(6); |
| 445 | point[10] = controls_->Debug(7); |
| 446 | #else |
| 447 | point[3] = adc_readings.motor_currents[0][0]; |
| 448 | point[4] = adc_readings.motor_currents[1][0]; |
| 449 | point[5] = adc_readings.motor_currents[2][0]; |
| 450 | point[6] = adc_readings.motor_currents[0][1]; |
| 451 | point[7] = adc_readings.motor_currents[1][1]; |
| 452 | point[8] = adc_readings.motor_currents[2][1]; |
| 453 | point[9] = temp1; |
| 454 | point[10] = temp2; |
| 455 | #endif |
| 456 | (void)temp1; |
| 457 | (void)temp2; |
| 458 | #if 0 |
| 459 | point[3] = pwm_ftm_->C1V - pwm_ftm_->C0V; |
| 460 | point[4] = pwm_ftm_->C3V - pwm_ftm_->C2V; |
| 461 | point[5] = pwm_ftm_->C5V - pwm_ftm_->C4V; |
| 462 | #endif |
| 463 | #endif |
| 464 | #if 0 |
| 465 | point[6] = adc_readings.motor_current_ref; |
| 466 | point[7] = adc_readings.input_voltage; |
| 467 | #else |
| 468 | #endif |
| 469 | } |
| 470 | |
| 471 | #define DO_STEP_RESPONSE 0 |
| 472 | #if DO_STEP_RESPONSE |
| 473 | // Step response |
| 474 | if (j > 25000) { |
| 475 | pwm_ftm_->C1V = 20; |
| 476 | } |
| 477 | #define DO_PULSE_SWEEP 0 |
| 478 | #elif DO_PULSE_SWEEP |
| 479 | // Sweep the pulse through the ADC sampling points. |
| 480 | static constexpr int kMax = 2500; |
| 481 | static constexpr int kExtraWait = 1500; |
| 482 | if (j > kStartupWait && j < kStartupWait + kExtraWait) { |
| 483 | pwm_ftm_->C4V = 0; |
| 484 | pwm_ftm_->C5V = 60; |
| 485 | } else if (j < kStartupWait + kMax + kExtraWait) { |
| 486 | uint32_t start = j - kStartupWait - kExtraWait; |
| 487 | pwm_ftm_->C4V = start; |
| 488 | pwm_ftm_->C5V = start + 60; |
| 489 | } else { |
| 490 | pwm_ftm_->C4V = 0; |
| 491 | pwm_ftm_->C5V = 0; |
| 492 | } |
| 493 | #endif |
| 494 | |
| 495 | ++j; |
| 496 | #if !SAMPLE_UNTIL_DONE |
| 497 | } else if (j < kSamplingEnd) { |
| 498 | ++j; |
| 499 | } else if (j == kSamplingEnd) { |
| 500 | #else |
| 501 | } else if (false) { |
| 502 | #endif |
| 503 | printf("finished\n"); |
| 504 | ++j; |
| 505 | #if !SAMPLE_UNTIL_DONE |
| 506 | } else { |
| 507 | #else |
| 508 | } else if (done) { |
| 509 | #endif |
| 510 | // Time to write the data out. |
| 511 | if (written < (int)sizeof(data)) { |
| 512 | int to_write = sizeof(data) - written; |
| 513 | #if 1 |
| 514 | if (to_write > 20) { |
| 515 | to_write = 20; |
| 516 | } |
| 517 | // TODO(Brian): Fix usb_serial_write so we can write more than 1 byte at a |
| 518 | // time. |
| 519 | if (to_write > 1) { |
| 520 | to_write = 1; |
| 521 | } |
| 522 | int result = |
| 523 | usb_serial_write(1, ((const char *)data) + written, to_write); |
| 524 | #else |
| 525 | if (to_write > 8) { |
| 526 | to_write = 8; |
| 527 | } |
| 528 | int result = |
| 529 | can_send(97, ((const unsigned char *)data) + written, to_write); |
| 530 | #endif |
| 531 | if (result >= 0) { |
| 532 | written += to_write; |
| 533 | } else { |
| 534 | //printf("error\n"); |
| 535 | } |
| 536 | if (written == (int)sizeof(data)) { |
| 537 | printf("done writing %d\n", written); |
| 538 | } |
| 539 | } |
| 540 | } |
| 541 | #endif |
| 542 | (void)adc_readings; |
| 543 | (void)start_count; |
| 544 | (void)balanced; |
| 545 | |
| 546 | // Tell the hardware to use the new switching points. |
| 547 | pwm_ftm_->PWMLOAD = FTM_PWMLOAD_LDOK; |
| 548 | |
| 549 | // If another cycle has already started, turn the light on right now. |
| 550 | if (pwm_ftm_->SC & FTM_SC_TOF) { |
| 551 | GPIOC_PSOR = 1 << 5; |
| 552 | } |
| 553 | } |
| 554 | |
| 555 | } // namespace salsa |
| 556 | } // namespace frc971 |