blob: 06f9f083ee3fa91f81e2b8e475d0b7b7ae36c71a [file] [log] [blame]
Brian Silverman8d3816a2017-07-03 18:52:15 -07001#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
15namespace frc971 {
16namespace salsa {
17namespace {
18
19constexpr int kDeadtimeCompensation = 9;
20
21uint32_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
31uint32_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
43Motor::Motor(BigFTM *pwm_ftm, LittleFTM *encoder_ftm, MotorControls *controls)
44 : pwm_ftm_(pwm_ftm), encoder_ftm_(encoder_ftm), controls_(controls) {}
45
46static_assert((BUS_CLOCK_FREQUENCY % SWITCHING_FREQUENCY) == 0,
47 "Switching frequency needs to divide the bus clock frequency");
48
49static_assert(BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY < UINT16_MAX,
50 "Need to prescale");
51
52void 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
166void 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
202void 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
212void 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