blob: b9e6683c40054586dd61320488d6cebd65a66e67 [file] [log] [blame]
Brian Silverman6260c092018-01-14 15:21:36 -08001#include "motors/core/kinetis.h"
2
Brian Silverman6260c092018-01-14 15:21:36 -08003#include <inttypes.h>
Brian Silvermandabdf902017-10-21 15:34:40 -04004#include <stdio.h>
Brian Silverman6260c092018-01-14 15:21:36 -08005
6#include <atomic>
7#include <cmath>
8
Brian Silvermandabdf902017-10-21 15:34:40 -04009#include "frc971/control_loops/drivetrain/integral_haptic_trigger.h"
10#include "frc971/control_loops/drivetrain/integral_haptic_wheel.h"
Brian Silverman6260c092018-01-14 15:21:36 -080011#include "motors/core/time.h"
12#include "motors/motor.h"
13#include "motors/peripheral/adc.h"
14#include "motors/peripheral/can.h"
15#include "motors/pistol_grip/motor_controls.h"
Brian Silverman4787a6e2018-10-06 16:00:54 -070016#include "motors/print/print.h"
Brian Silverman6260c092018-01-14 15:21:36 -080017#include "motors/util.h"
Brian Silverman6260c092018-01-14 15:21:36 -080018
19#define MOTOR0_PWM_FTM FTM3
20#define MOTOR0_ENCODER_FTM FTM2
21#define MOTOR1_PWM_FTM FTM0
22#define MOTOR1_ENCODER_FTM FTM1
23
Austin Schuh745e4ed2019-04-07 15:50:16 -070024extern const float kWheelCoggingTorque0[4096];
25extern const float kWheelCoggingTorque1[4096];
26extern const float kTriggerCoggingTorque0[4096];
27extern const float kTriggerCoggingTorque1[4096];
Brian Silverman6260c092018-01-14 15:21:36 -080028
29namespace frc971 {
Brian Silvermana96c1a42018-05-12 12:11:31 -070030namespace motors {
Brian Silverman6260c092018-01-14 15:21:36 -080031namespace {
32
Austin Schuh745e4ed2019-04-07 15:50:16 -070033::std::atomic<const float *> trigger_cogging_torque{nullptr};
34::std::atomic<const float *> wheel_cogging_torque{nullptr};
35
36float TriggerCoggingTorque(uint32_t index) {
37 return trigger_cogging_torque.load(::std::memory_order_relaxed)[index];
38}
39
40float WheelCoggingTorque(uint32_t index) {
41 return wheel_cogging_torque.load(::std::memory_order_relaxed)[index];
42}
43
Brian Silverman6260c092018-01-14 15:21:36 -080044using ::frc971::control_loops::drivetrain::MakeIntegralHapticTriggerPlant;
45using ::frc971::control_loops::drivetrain::MakeIntegralHapticTriggerObserver;
46using ::frc971::control_loops::drivetrain::MakeIntegralHapticWheelPlant;
47using ::frc971::control_loops::drivetrain::MakeIntegralHapticWheelObserver;
48
Austin Schuh80b99932019-04-07 14:04:41 -070049// Returns an identifier for the processor we're running on.
50// This isn't guaranteed to be unique, but it should be close enough.
51uint8_t ProcessorIdentifier() {
52 // This XORs together all the bytes of the unique identifier provided by the
53 // hardware.
54 uint8_t r = 0;
55 for (uint8_t uid : {SIM_UIDH, SIM_UIDMH, SIM_UIDML, SIM_UIDL}) {
56 r = r ^ ((uid >> 0) & 0xFF);
57 r = r ^ ((uid >> 8) & 0xFF);
58 r = r ^ ((uid >> 16) & 0xFF);
59 r = r ^ ((uid >> 24) & 0xFF);
60 }
61 return r;
62}
63
64uint8_t ProcessorIndex() {
65 switch (ProcessorIdentifier()) {
66 case static_cast<uint8_t>(0xaa):
67 return 1;
68 default:
69 return 0;
70 }
71}
72
Brian Silverman9ed2cf12018-05-12 13:06:38 -070073struct SmallAdcReadings {
74 uint16_t currents[3];
75};
76
77struct SmallInitReadings {
78 uint16_t motor0_abs;
79 uint16_t motor1_abs;
80 uint16_t wheel_abs;
81};
82
83void AdcInitSmall() {
84 AdcInitCommon();
85
86 // M0_CH0F ADC1_SE17
87 PORTA_PCR17 = PORT_PCR_MUX(0);
88
89 // M0_CH1F ADC1_SE14
90 PORTB_PCR10 = PORT_PCR_MUX(0);
91
92 // M0_CH2F ADC1_SE15
93 PORTB_PCR11 = PORT_PCR_MUX(0);
94
95 // M0_ABS ADC0_SE5b
96 PORTD_PCR1 = PORT_PCR_MUX(0);
97
98 // M1_CH0F ADC0_SE13
99 PORTB_PCR3 = PORT_PCR_MUX(0);
100
101 // M1_CH1F ADC0_SE12
102 PORTB_PCR2 = PORT_PCR_MUX(0);
103
104 // M1_CH2F ADC0_SE14
105 PORTC_PCR0 = PORT_PCR_MUX(0);
106
107 // M1_ABS ADC0_SE17
108 PORTE_PCR24 = PORT_PCR_MUX(0);
109
110 // WHEEL_ABS ADC0_SE18
111 PORTE_PCR25 = PORT_PCR_MUX(0);
112
113 // VIN ADC1_SE5B
114 PORTC_PCR9 = PORT_PCR_MUX(0);
115}
116
117SmallAdcReadings AdcReadSmall0(const DisableInterrupts &) {
118 SmallAdcReadings r;
119
120 ADC1_SC1A = 17;
121 while (!(ADC1_SC1A & ADC_SC1_COCO)) {
122 }
123 ADC1_SC1A = 14;
124 r.currents[0] = ADC1_RA;
125 while (!(ADC1_SC1A & ADC_SC1_COCO)) {
126 }
127 ADC1_SC1A = 15;
128 r.currents[1] = ADC1_RA;
129 while (!(ADC1_SC1A & ADC_SC1_COCO)) {
130 }
131 r.currents[2] = ADC1_RA;
132
133 return r;
134}
135
136SmallAdcReadings AdcReadSmall1(const DisableInterrupts &) {
137 SmallAdcReadings r;
138
139 ADC0_SC1A = 13;
140 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
141 }
142 ADC0_SC1A = 12;
143 r.currents[0] = ADC0_RA;
144 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
145 }
146 ADC0_SC1A = 14;
147 r.currents[1] = ADC0_RA;
148 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
149 }
150 r.currents[2] = ADC0_RA;
151
152 return r;
153}
154
155SmallInitReadings AdcReadSmallInit(const DisableInterrupts &) {
156 SmallInitReadings r;
157
158 ADC0_SC1A = 5;
159 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
160 }
161 ADC0_SC1A = 17;
162 r.motor0_abs = ADC0_RA;
163 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
164 }
165 ADC0_SC1A = 18;
166 r.motor1_abs = ADC0_RA;
167 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
168 }
169 r.wheel_abs = ADC0_RA;
170
171 return r;
172}
173
Brian Silverman6260c092018-01-14 15:21:36 -0800174constexpr float kHapticWheelCurrentLimit = static_cast<float>(
175 ::frc971::control_loops::drivetrain::kHapticWheelCurrentLimit);
176constexpr float kHapticTriggerCurrentLimit = static_cast<float>(
177 ::frc971::control_loops::drivetrain::kHapticTriggerCurrentLimit);
178
179::std::atomic<Motor *> global_motor0{nullptr}, global_motor1{nullptr};
Brian Silverman6260c092018-01-14 15:21:36 -0800180
181// Angle last time the current loop ran.
182::std::atomic<float> global_wheel_angle{0.0f};
183::std::atomic<float> global_trigger_angle{0.0f};
184
185// Wheel observer/plant.
186::std::atomic<StateFeedbackObserver<3, 1, 1, float> *> global_wheel_observer{
187 nullptr};
188::std::atomic<StateFeedbackPlant<3, 1, 1, float> *> global_wheel_plant{nullptr};
189// Throttle observer/plant.
190::std::atomic<StateFeedbackObserver<3, 1, 1, float> *> global_trigger_observer{
191 nullptr};
192::std::atomic<StateFeedbackPlant<3, 1, 1, float> *> global_trigger_plant{
193 nullptr};
194
195// Torques for the current loop to apply.
196::std::atomic<float> global_wheel_current{0.0f};
197::std::atomic<float> global_trigger_torque{0.0f};
198
199constexpr int kSwitchingDivisor = 2;
200
201float analog_ratio(uint16_t reading) {
202 static constexpr uint16_t kMin = 260, kMax = 3812;
203 return static_cast<float>(::std::max(::std::min(reading, kMax), kMin) -
204 kMin) /
205 static_cast<float>(kMax - kMin);
206}
207
208constexpr float InterpolateFloat(float x1, float x0, float y1, float y0, float x) {
209 return (x - x0) * (y1 - y0) / (x1 - x0) + y0;
210}
211
212float absolute_wheel(float wheel_position) {
Austin Schuh4a8d4922019-04-07 15:31:30 -0700213 const float kCenterOffset = (ProcessorIndex() == 1) ? -0.683f : -0.935f;
214
215 wheel_position += kCenterOffset;
216
217 if (wheel_position > 0.5f) {
218 wheel_position -= 1.0f;
219 } else if (wheel_position < -0.5f) {
Brian Silverman6260c092018-01-14 15:21:36 -0800220 wheel_position += 1.0f;
221 }
Brian Silverman6260c092018-01-14 15:21:36 -0800222 return wheel_position;
223}
224
225extern "C" {
226
227void *__stack_chk_guard = (void *)0x67111971;
228void __stack_chk_fail() {
229 while (true) {
230 GPIOC_PSOR = (1 << 5);
231 printf("Stack corruption detected\n");
232 delay(1000);
233 GPIOC_PCOR = (1 << 5);
234 delay(1000);
235 }
236}
237
Brian Silverman6260c092018-01-14 15:21:36 -0800238extern uint32_t __bss_ram_start__[], __bss_ram_end__[];
239extern uint32_t __data_ram_start__[], __data_ram_end__[];
240extern uint32_t __heap_start__[], __heap_end__[];
241extern uint32_t __stack_end__[];
242
243} // extern "C"
244
245constexpr float kWheelMaxExtension = 1.0f;
246constexpr float kWheelFrictionMax = 0.2f;
247float WheelCenteringCurrent(float scalar, float angle, float velocity) {
248 float friction_goal_current = -angle * 10.0f;
249 if (friction_goal_current > kWheelFrictionMax) {
250 friction_goal_current = kWheelFrictionMax;
251 } else if (friction_goal_current < -kWheelFrictionMax) {
252 friction_goal_current = -kWheelFrictionMax;
253 }
254
255 constexpr float kWheelSpringNonlinearity = 0.45f;
256
257 float goal_current = -((1.0f - kWheelSpringNonlinearity) * angle +
258 kWheelSpringNonlinearity * angle * angle * angle) *
259 6.0f -
260 velocity * 0.04f;
261 if (goal_current > 5.0f - scalar) {
262 goal_current = 5.0f - scalar;
263 } else if (goal_current < -5.0f + scalar) {
264 goal_current = -5.0f + scalar;
265 }
266
267 return goal_current * scalar + friction_goal_current;
268}
269
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700270float CoggingCurrent1(uint32_t encoder, int32_t absolute_encoder) {
271 constexpr float kP = 0.05f;
272 constexpr float kI = 0.00001f;
273 static int goal = -6700;
274
275 const int error = goal - static_cast<int>(absolute_encoder);
276 static float error_sum = 0.0f;
277 float goal_current = static_cast<float>(error) * kP + error_sum * kI;
278
279 goal_current = ::std::min(1.0f, ::std::max(-1.0f, goal_current));
280
281 static int i = 0;
282 if (error == 0) {
283 ++i;
284 } else {
285 i = 0;
286 }
287 if (i >= 100) {
288 printf("reading1: %d %d a:%d e:%d\n", goal,
289 static_cast<int>(goal_current * 10000.0f),
290 static_cast<int>(encoder),
291 static_cast<int>(error));
292 static int counting_up = 0;
293 if (absolute_encoder <= -6900) {
294 counting_up = 1;
295 } else if (absolute_encoder >= 6900) {
296 counting_up = 0;
297 }
298 if (counting_up) {
299 ++goal;
300 } else {
301 --goal;
302 }
303 i = 0;
304 }
305
306 error_sum += static_cast<float>(error);
307 if (error_sum > 1.0f / kI) {
308 error_sum = 1.0f / kI;
309 } else if (error_sum < -1.0f / kI) {
310 error_sum = -1.0f / kI;
311 }
312 return goal_current;
313}
314
Brian Silverman6260c092018-01-14 15:21:36 -0800315extern "C" void ftm0_isr() {
316 SmallAdcReadings readings;
317 {
318 DisableInterrupts disable_interrupts;
319 readings = AdcReadSmall1(disable_interrupts);
320 }
321 uint32_t encoder =
322 global_motor1.load(::std::memory_order_relaxed)->wrapped_encoder();
323 int32_t absolute_encoder = global_motor1.load(::std::memory_order_relaxed)
324 ->absolute_encoder(encoder);
325
326 const float angle = absolute_encoder / static_cast<float>((15320 - 1488) / 2);
Brian Silverman6260c092018-01-14 15:21:36 -0800327
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700328 (void)CoggingCurrent1;
Austin Schuh54c8c842019-04-07 13:54:23 -0700329 float goal_current = global_wheel_current.load(::std::memory_order_relaxed) +
Austin Schuh745e4ed2019-04-07 15:50:16 -0700330 WheelCoggingTorque(encoder);
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700331 //float goal_current = CoggingCurrent1(encoder, absolute_encoder);
Austin Schuh54c8c842019-04-07 13:54:23 -0700332 //float goal_current = kWheelCoggingTorque[encoder];
333 //float goal_current = 0.0f;
Brian Silverman6260c092018-01-14 15:21:36 -0800334
335 global_motor1.load(::std::memory_order_relaxed)->SetGoalCurrent(goal_current);
336 global_motor1.load(::std::memory_order_relaxed)
Austin Schuh54c8c842019-04-07 13:54:23 -0700337 ->CurrentInterrupt(BalanceSimpleReadings(readings.currents), encoder);
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700338 //global_motor1.load(::std::memory_order_relaxed)->CycleFixedPhaseInterupt();
Austin Schuh54c8c842019-04-07 13:54:23 -0700339
340 global_wheel_angle.store(angle);
Austin Schuh5b0e6b62019-04-07 14:23:37 -0700341
342 /*
343 SmallInitReadings position_readings;
344 {
345 DisableInterrupts disable_interrupts;
346 position_readings = AdcReadSmallInit(disable_interrupts);
347 }
348
349 static int i = 0;
350 if (i == 1000) {
351 i = 0;
352 float wheel_position =
353 absolute_wheel(analog_ratio(position_readings.wheel_abs));
354 printf(
355 "ecnt %" PRIu32 " arev:%d erev:%d abs:%d awp:%d uncalwheel:%d\n",
356 encoder,
357 static_cast<int>((1.0f - analog_ratio(position_readings.motor1_abs)) *
358 7000.0f),
359 static_cast<int>(encoder * 7.0f / 4096.0f * 1000.0f),
360 static_cast<int>(absolute_encoder),
361 static_cast<int>(wheel_position * 1000.0f),
362 static_cast<int>(analog_ratio(position_readings.wheel_abs) * 1000.0f));
363 } else if (i == 200) {
364 printf("out %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
365 global_motor1.load(::std::memory_order_relaxed)
366 ->output_registers()[0][2],
367 global_motor1.load(::std::memory_order_relaxed)
368 ->output_registers()[1][2],
369 global_motor1.load(::std::memory_order_relaxed)
370 ->output_registers()[2][2]);
371 }
372 ++i;
373 */
Brian Silverman6260c092018-01-14 15:21:36 -0800374}
375
Austin Schuh876b4f02018-03-10 19:16:59 -0800376constexpr float kTriggerMaxExtension = -0.70f;
Brian Silverman6260c092018-01-14 15:21:36 -0800377constexpr float kTriggerCenter = 0.0f;
Austin Schuh876b4f02018-03-10 19:16:59 -0800378constexpr float kCenteringStiffness = 0.15f;
Brian Silverman6260c092018-01-14 15:21:36 -0800379float TriggerCenteringCurrent(float trigger_angle) {
380 float goal_current = (kTriggerCenter - trigger_angle) * 3.0f;
Austin Schuh876b4f02018-03-10 19:16:59 -0800381 float knotch_goal_current = (kTriggerCenter - trigger_angle) * 8.0f;
382 if (knotch_goal_current < -kCenteringStiffness) {
383 knotch_goal_current = -kCenteringStiffness;
384 } else if (knotch_goal_current > kCenteringStiffness) {
385 knotch_goal_current = kCenteringStiffness;
386 }
387
388 goal_current += knotch_goal_current;
389
Brian Silverman6260c092018-01-14 15:21:36 -0800390 if (goal_current < -1.0f) {
391 goal_current = -1.0f;
392 } else if (goal_current > 1.0f) {
393 goal_current = 1.0f;
394 if (trigger_angle < kTriggerMaxExtension) {
395 goal_current -= (30.0f * (trigger_angle - kTriggerMaxExtension));
Austin Schuh876b4f02018-03-10 19:16:59 -0800396 if (goal_current > 4.0f) {
397 goal_current = 4.0f;
Brian Silverman6260c092018-01-14 15:21:36 -0800398 }
399 }
400 }
401 return goal_current;
402}
403
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700404float CoggingCurrent0(uint32_t encoder, int32_t absolute_encoder) {
405 constexpr float kP = 0.05f;
406 constexpr float kI = 0.00001f;
407 static int goal = 0;
408
409 const int error = goal - static_cast<int>(absolute_encoder);
410 static float error_sum = 0.0f;
411 float goal_current = static_cast<float>(error) * kP + error_sum * kI;
412
413 goal_current = ::std::min(1.0f, ::std::max(-1.0f, goal_current));
414
415 static int i = 0;
416 if (error == 0) {
417 ++i;
418 } else {
419 i = 0;
420 }
421
422 if (i >= 100) {
423 printf("reading0: %d %d a:%d e:%d\n", goal,
424 static_cast<int>(goal_current * 10000.0f),
425 static_cast<int>(encoder),
426 static_cast<int>(error));
427 static int counting_up = 0;
428 if (absolute_encoder <= -1390) {
429 counting_up = 1;
430 } else if (absolute_encoder >= 1390) {
431 counting_up = 0;
432 }
433 if (counting_up) {
434 ++goal;
435 } else {
436 --goal;
437 }
438 }
439
440 error_sum += static_cast<float>(error);
441 if (error_sum > 1.0f / kI) {
442 error_sum = 1.0f / kI;
443 } else if (error_sum < -1.0f / kI) {
444 error_sum = -1.0f / kI;
445 }
446 return goal_current;
447}
448
Brian Silverman6260c092018-01-14 15:21:36 -0800449extern "C" void ftm3_isr() {
450 SmallAdcReadings readings;
451 {
452 DisableInterrupts disable_interrupts;
453 readings = AdcReadSmall0(disable_interrupts);
454 }
Brian Silverman6260c092018-01-14 15:21:36 -0800455
Austin Schuh54c8c842019-04-07 13:54:23 -0700456 const uint32_t encoder =
457 global_motor0.load(::std::memory_order_relaxed)->wrapped_encoder();
458 const int32_t absolute_encoder =
459 global_motor0.load(::std::memory_order_relaxed)
460 ->absolute_encoder(encoder);
461
462 const float trigger_angle = absolute_encoder / 1370.f;
Brian Silverman6260c092018-01-14 15:21:36 -0800463
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700464 (void)CoggingCurrent0;
Brian Silverman6260c092018-01-14 15:21:36 -0800465 const float goal_current =
Austin Schuh54c8c842019-04-07 13:54:23 -0700466 global_trigger_torque.load(::std::memory_order_relaxed) +
Austin Schuh745e4ed2019-04-07 15:50:16 -0700467 TriggerCoggingTorque(encoder);
Austin Schuh54c8c842019-04-07 13:54:23 -0700468 //const float goal_current = kTriggerCoggingTorque[encoder];
469 //const float goal_current = 0.0f;
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700470 //const float goal_current = CoggingCurrent0(encoder, absolute_encoder);
Brian Silverman6260c092018-01-14 15:21:36 -0800471
472 global_motor0.load(::std::memory_order_relaxed)->SetGoalCurrent(goal_current);
473 global_motor0.load(::std::memory_order_relaxed)
Austin Schuh54c8c842019-04-07 13:54:23 -0700474 ->CurrentInterrupt(BalanceSimpleReadings(readings.currents), encoder);
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700475 //global_motor0.load(::std::memory_order_relaxed)->CycleFixedPhaseInterupt();
Brian Silverman6260c092018-01-14 15:21:36 -0800476
Brian Silverman6260c092018-01-14 15:21:36 -0800477 global_trigger_angle.store(trigger_angle);
Austin Schuh5b0e6b62019-04-07 14:23:37 -0700478
479 /*
480 SmallInitReadings position_readings;
481 {
482 DisableInterrupts disable_interrupts;
483 position_readings = AdcReadSmallInit(disable_interrupts);
484 }
485
486 static int i = 0;
487 if (i == 1000) {
488 i = 0;
489 printf("ecnt %" PRIu32 " arev:%d erev:%d abs:%d\n", encoder,
490 static_cast<int>((analog_ratio(position_readings.motor0_abs)) *
491 7000.0f),
492 static_cast<int>(encoder * 7.0f / 4096.0f * 1000.0f),
493 static_cast<int>(absolute_encoder));
494 } else if (i == 200) {
495 printf("out %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
496 global_motor0.load(::std::memory_order_relaxed)
497 ->output_registers()[0][2],
498 global_motor0.load(::std::memory_order_relaxed)
499 ->output_registers()[1][2],
500 global_motor0.load(::std::memory_order_relaxed)
501 ->output_registers()[2][2]);
502 }
503 ++i;
504 */
Brian Silverman6260c092018-01-14 15:21:36 -0800505}
506
Brian Silverman6260c092018-01-14 15:21:36 -0800507int ConvertFloat16(float val) {
508 int result = static_cast<int>(val * 32768.0f) + 32768;
509 if (result > 0xffff) {
510 result = 0xffff;
511 } else if (result < 0) {
512 result = 0;
513 }
514 return result;
515}
516int ConvertFloat14(float val) {
517 int result = static_cast<int>(val * 8192.0f) + 8192;
518 if (result > 0x3fff) {
519 result = 0x3fff;
520 } else if (result < 0) {
521 result = 0;
522 }
523 return result;
524}
525
526extern "C" void pit3_isr() {
527 PIT_TFLG3 = 1;
528 const float absolute_trigger_angle =
529 global_trigger_angle.load(::std::memory_order_relaxed);
530 const float absolute_wheel_angle =
531 global_wheel_angle.load(::std::memory_order_relaxed);
532
533 // Force a barrier here so we sample everything guaranteed at the beginning.
534 __asm__("" ::: "memory");
535 const float absolute_wheel_angle_radians =
536 absolute_wheel_angle * static_cast<float>(M_PI) * (338.16f / 360.0f);
537 const float absolute_trigger_angle_radians =
538 absolute_trigger_angle * static_cast<float>(M_PI) * (45.0f / 360.0f);
539
540 static uint32_t last_command_time = 0;
541 static float trigger_goal_position = 0.0f;
542 static float trigger_goal_velocity = 0.0f;
543 static float trigger_haptic_current = 0.0f;
544 static bool trigger_centering = true;
545 static bool trigger_haptics = false;
546 {
547 uint8_t data[8];
548 int length;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700549 can_receive(data, &length, 0);
Brian Silverman6260c092018-01-14 15:21:36 -0800550 if (length > 0) {
551 last_command_time = micros();
552 trigger_goal_position =
553 static_cast<float>(
554 static_cast<int32_t>(static_cast<uint32_t>(data[0]) |
555 (static_cast<uint32_t>(data[1]) << 8)) -
556 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700557 static_cast<float>(32768.0 * M_PI / 8.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800558 trigger_goal_velocity =
559 static_cast<float>(
560 static_cast<int32_t>(static_cast<uint32_t>(data[2]) |
561 (static_cast<uint32_t>(data[3]) << 8)) -
562 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700563 static_cast<float>(32768.0 * 4.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800564
565 trigger_haptic_current =
566 static_cast<float>(
567 static_cast<int32_t>(static_cast<uint32_t>(data[4]) |
568 (static_cast<uint32_t>(data[5]) << 8)) -
569 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700570 static_cast<float>(32768.0 * 2.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800571 if (trigger_haptic_current > kHapticTriggerCurrentLimit) {
572 trigger_haptic_current = kHapticTriggerCurrentLimit;
573 } else if (trigger_haptic_current < -kHapticTriggerCurrentLimit) {
574 trigger_haptic_current = -kHapticTriggerCurrentLimit;
575 }
576 trigger_centering = !!(data[7] & 0x01);
577 trigger_haptics = !!(data[7] & 0x02);
578 }
579 }
580
581 static float wheel_goal_position = 0.0f;
582 static float wheel_goal_velocity = 0.0f;
583 static float wheel_haptic_current = 0.0f;
584 static float wheel_kp = 0.0f;
585 static bool wheel_centering = true;
586 static float wheel_centering_scalar = 0.25f;
587 {
588 uint8_t data[8];
589 int length;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700590 can_receive(data, &length, 1);
Brian Silverman6260c092018-01-14 15:21:36 -0800591 if (length == 8) {
592 last_command_time = micros();
593 wheel_goal_position =
594 static_cast<float>(
595 static_cast<int32_t>(static_cast<uint32_t>(data[0]) |
596 (static_cast<uint32_t>(data[1]) << 8)) -
597 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700598 static_cast<float>(32768.0 * M_PI);
Brian Silverman6260c092018-01-14 15:21:36 -0800599 wheel_goal_velocity =
600 static_cast<float>(
601 static_cast<int32_t>(static_cast<uint32_t>(data[2]) |
602 (static_cast<uint32_t>(data[3]) << 8)) -
603 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700604 static_cast<float>(32768.0 * 10.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800605
606 wheel_haptic_current =
607 static_cast<float>(
608 static_cast<int32_t>(static_cast<uint32_t>(data[4]) |
609 (static_cast<uint32_t>(data[5]) << 8)) -
610 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700611 static_cast<float>(32768.0 * 2.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800612 if (wheel_haptic_current > kHapticWheelCurrentLimit) {
613 wheel_haptic_current = kHapticWheelCurrentLimit;
614 } else if (wheel_haptic_current < -kHapticWheelCurrentLimit) {
615 wheel_haptic_current = -kHapticWheelCurrentLimit;
616 }
617 wheel_kp = static_cast<float>(data[6]) * 30.0f / 255.0f;
618 wheel_centering = !!(data[7] & 0x01);
619 wheel_centering_scalar = ((data[7] >> 1) & 0x7f) / 127.0f;
620 }
621 }
622
623 static constexpr uint32_t kTimeout = 100000;
624 if (!time_after(time_add(last_command_time, kTimeout), micros())) {
625 last_command_time = time_subtract(micros(), kTimeout);
626 trigger_goal_position = 0.0f;
627 trigger_goal_velocity = 0.0f;
628 trigger_haptic_current = 0.0f;
629 trigger_centering = true;
630 trigger_haptics = false;
631
632 wheel_goal_position = 0.0f;
633 wheel_goal_velocity = 0.0f;
634 wheel_haptic_current = 0.0f;
635 wheel_centering = true;
636 wheel_centering_scalar = 0.25f;
Brian Silverman17ffa8c2018-03-09 18:27:29 -0800637 // Avoid wrapping back into the valid range.
638 last_command_time = time_subtract(micros(), kTimeout);
Brian Silverman6260c092018-01-14 15:21:36 -0800639 }
640
641 StateFeedbackPlant<3, 1, 1, float> *const trigger_plant =
642 global_trigger_plant.load(::std::memory_order_relaxed);
643 StateFeedbackObserver<3, 1, 1, float> *const trigger_observer =
644 global_trigger_observer.load(::std::memory_order_relaxed);
645 ::Eigen::Matrix<float, 1, 1> trigger_Y;
646 trigger_Y << absolute_trigger_angle_radians;
647 trigger_observer->Correct(*trigger_plant,
648 ::Eigen::Matrix<float, 1, 1>::Zero(), trigger_Y);
649
650 StateFeedbackPlant<3, 1, 1, float> *const wheel_plant =
651 global_wheel_plant.load(::std::memory_order_relaxed);
652 StateFeedbackObserver<3, 1, 1, float> *const wheel_observer =
653 global_wheel_observer.load(::std::memory_order_relaxed);
654 ::Eigen::Matrix<float, 1, 1> wheel_Y;
655 wheel_Y << absolute_wheel_angle_radians;
656 wheel_observer->Correct(*wheel_plant, ::Eigen::Matrix<float, 1, 1>::Zero(),
657 wheel_Y);
658
659 float kWheelD = (wheel_kp - 10.0f) * (0.25f - 0.20f) / 5.0f + 0.20f;
660 if (wheel_kp < 0.5f) {
661 kWheelD = wheel_kp * 0.05f / 0.5f;
662 } else if (wheel_kp < 1.0f) {
663 kWheelD = InterpolateFloat(1.0f, 0.5f, 0.06f, 0.05f, wheel_kp);
664 } else if (wheel_kp < 2.0f) {
665 kWheelD = InterpolateFloat(2.0f, 1.0f, 0.08f, 0.06f, wheel_kp);
666 } else if (wheel_kp < 3.0f) {
667 kWheelD = InterpolateFloat(3.0f, 2.0f, 0.10f, 0.08f, wheel_kp);
668 } else if (wheel_kp < 5.0f) {
669 kWheelD = InterpolateFloat(5.0f, 3.0f, 0.13f, 0.10f, wheel_kp);
670 } else if (wheel_kp < 10.0f) {
671 kWheelD = InterpolateFloat(10.0f, 5.0f, 0.20f, 0.13f, wheel_kp);
672 }
673
674 float wheel_goal_current = wheel_haptic_current;
675
676 wheel_goal_current +=
677 (wheel_goal_position - absolute_wheel_angle_radians) * wheel_kp +
678 (wheel_goal_velocity - wheel_observer->X_hat()(1, 0)) * kWheelD;
679
680 // Compute the torques to apply to each motor.
681 if (wheel_centering) {
682 wheel_goal_current +=
683 WheelCenteringCurrent(wheel_centering_scalar, absolute_wheel_angle,
684 wheel_observer->X_hat()(1, 0));
685 }
686
687 if (wheel_goal_current > kHapticWheelCurrentLimit) {
688 wheel_goal_current = kHapticWheelCurrentLimit;
689 } else if (wheel_goal_current < -kHapticWheelCurrentLimit) {
690 wheel_goal_current = -kHapticWheelCurrentLimit;
691 }
692 global_wheel_current.store(wheel_goal_current, ::std::memory_order_relaxed);
693
694 constexpr float kTriggerP =
695 static_cast<float>(::frc971::control_loops::drivetrain::kHapticTriggerP);
696 constexpr float kTriggerD =
697 static_cast<float>(::frc971::control_loops::drivetrain::kHapticTriggerD);
698 float trigger_goal_current = trigger_haptic_current;
699 if (trigger_haptics) {
700 trigger_goal_current +=
701 (trigger_goal_position - absolute_trigger_angle_radians) * kTriggerP +
702 (trigger_goal_velocity - trigger_observer->X_hat()(1, 0)) * kTriggerD;
703 }
704
705 if (trigger_centering) {
706 trigger_goal_current += TriggerCenteringCurrent(absolute_trigger_angle);
707 }
708
709 if (trigger_goal_current > kHapticTriggerCurrentLimit) {
710 trigger_goal_current = kHapticTriggerCurrentLimit;
711 } else if (trigger_goal_current < -kHapticTriggerCurrentLimit) {
712 trigger_goal_current = -kHapticTriggerCurrentLimit;
713 }
714 global_trigger_torque.store(trigger_goal_current,
715 ::std::memory_order_relaxed);
716
717 uint8_t buttons = 0;
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500718 if (!PERIPHERAL_BITBAND(GPIOA_PDIR, 14)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800719 buttons |= 0x1;
720 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500721 if (!PERIPHERAL_BITBAND(GPIOE_PDIR, 26)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800722 buttons |= 0x2;
723 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500724 if (!PERIPHERAL_BITBAND(GPIOC_PDIR, 7)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800725 buttons |= 0x4;
726 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500727 if (!PERIPHERAL_BITBAND(GPIOD_PDIR, 0)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800728 buttons |= 0x8;
729 }
730
731 float trigger_angle = absolute_trigger_angle;
732
733 // Adjust the trigger range for reporting back.
734 // TODO(austin): We'll likely need to make this symmetric for the controls to
735 // work out well.
736 if (trigger_angle > kTriggerCenter) {
737 trigger_angle = (trigger_angle - kTriggerCenter) / (1.0f - kTriggerCenter);
738 } else {
739 trigger_angle = (trigger_angle - kTriggerCenter) /
740 (kTriggerCenter - kTriggerMaxExtension);
741 }
742
743 // TODO(austin): Class + fns. This is a mess.
744 // TODO(austin): Move this to a separate file. It's too big.
745 int can_trigger = ConvertFloat16(absolute_trigger_angle);
746 int can_trigger_velocity =
747 ConvertFloat16(trigger_observer->X_hat()(1, 0) / 50.0f);
748 int can_trigger_torque =
749 ConvertFloat16(trigger_observer->X_hat()(2, 0) * 2.0f);
750 int can_trigger_current = ConvertFloat14(trigger_goal_current / 10.0f);
751
752 int can_wheel = ConvertFloat16(absolute_wheel_angle);
753 int can_wheel_velocity =
754 ConvertFloat16(wheel_observer->X_hat()(1, 0) / 50.0f);
755 int can_wheel_torque = ConvertFloat16(wheel_observer->X_hat()(2, 0) * 2.0f);
756 int can_wheel_current = ConvertFloat14(wheel_goal_current / 10.0f);
757
758 {
759 const uint8_t trigger_joystick_values[8] = {
760 static_cast<uint8_t>(can_trigger & 0xff),
761 static_cast<uint8_t>((can_trigger >> 8) & 0xff),
762 static_cast<uint8_t>(can_trigger_velocity & 0xff),
763 static_cast<uint8_t>((can_trigger_velocity >> 8) & 0xff),
764 static_cast<uint8_t>(can_trigger_torque & 0xff),
765 static_cast<uint8_t>((can_trigger_torque >> 8) & 0xff),
766 static_cast<uint8_t>(can_trigger_current & 0xff),
767 static_cast<uint8_t>(((buttons & 0x3) << 6) |
768 (can_trigger_current >> 8))};
769 const uint8_t wheel_joystick_values[8] = {
770 static_cast<uint8_t>(can_wheel & 0xff),
771 static_cast<uint8_t>((can_wheel >> 8) & 0xff),
772 static_cast<uint8_t>(can_wheel_velocity & 0xff),
773 static_cast<uint8_t>((can_wheel_velocity >> 8) & 0xff),
774 static_cast<uint8_t>(can_wheel_torque & 0xff),
775 static_cast<uint8_t>((can_wheel_torque >> 8) & 0xff),
776 static_cast<uint8_t>(can_wheel_current & 0xff),
777 static_cast<uint8_t>(((buttons & 0xc) << 4) |
778 (can_wheel_current >> 8))};
779
780 can_send(0, trigger_joystick_values, 8, 2);
781 can_send(1, wheel_joystick_values, 8, 3);
782 }
783
784 ::Eigen::Matrix<float, 1, 1> trigger_U;
785 trigger_U << trigger_goal_current;
786 ::Eigen::Matrix<float, 1, 1> wheel_U;
787 wheel_U << wheel_goal_current;
788 trigger_observer->Predict(trigger_plant, trigger_U,
789 ::std::chrono::milliseconds(1));
790 wheel_observer->Predict(wheel_plant, wheel_U, ::std::chrono::milliseconds(1));
791}
792
793void ConfigurePwmFtm(BigFTM *pwm_ftm) {
794 // Put them all into combine active-high mode, and all the low ones staying
795 // off all the time by default. We'll then use only the low ones.
796 pwm_ftm->C0SC = FTM_CSC_ELSB;
797 pwm_ftm->C0V = 0;
798 pwm_ftm->C1SC = FTM_CSC_ELSB;
799 pwm_ftm->C1V = 0;
800 pwm_ftm->C2SC = FTM_CSC_ELSB;
801 pwm_ftm->C2V = 0;
802 pwm_ftm->C3SC = FTM_CSC_ELSB;
803 pwm_ftm->C3V = 0;
804 pwm_ftm->C4SC = FTM_CSC_ELSB;
805 pwm_ftm->C4V = 0;
806 pwm_ftm->C5SC = FTM_CSC_ELSB;
807 pwm_ftm->C5V = 0;
808 pwm_ftm->C6SC = FTM_CSC_ELSB;
809 pwm_ftm->C6V = 0;
810 pwm_ftm->C7SC = FTM_CSC_ELSB;
811 pwm_ftm->C7V = 0;
812
813 pwm_ftm->COMBINE = FTM_COMBINE_SYNCEN3 /* Synchronize updates usefully */ |
814 FTM_COMBINE_COMP3 /* Make them complementary */ |
815 FTM_COMBINE_COMBINE3 /* Combine the channels */ |
816 FTM_COMBINE_SYNCEN2 /* Synchronize updates usefully */ |
817 FTM_COMBINE_COMP2 /* Make them complementary */ |
818 FTM_COMBINE_COMBINE2 /* Combine the channels */ |
819 FTM_COMBINE_SYNCEN1 /* Synchronize updates usefully */ |
820 FTM_COMBINE_COMP1 /* Make them complementary */ |
821 FTM_COMBINE_COMBINE1 /* Combine the channels */ |
822 FTM_COMBINE_SYNCEN0 /* Synchronize updates usefully */ |
823 FTM_COMBINE_COMP0 /* Make them complementary */ |
824 FTM_COMBINE_COMBINE0 /* Combine the channels */;
825}
826
827bool CountValid(uint32_t count) {
828 static constexpr int kMaxMovement = 1;
829 return count <= kMaxMovement || count >= (4096 - kMaxMovement);
830}
831
832bool ZeroMotors(uint16_t *motor0_offset, uint16_t *motor1_offset,
833 uint16_t *wheel_offset) {
834 static constexpr int kNumberSamples = 1024;
835 static_assert(UINT16_MAX * kNumberSamples <= UINT32_MAX, "Too many samples");
836 uint32_t motor0_sum = 0, motor1_sum = 0, wheel_sum = 0;
837
838 // First clear both encoders.
839 MOTOR0_ENCODER_FTM->CNT = MOTOR1_ENCODER_FTM->CNT = 0;
840 for (int i = 0; i < kNumberSamples; ++i) {
841 delay(1);
842
843 if (!CountValid(MOTOR0_ENCODER_FTM->CNT)) {
844 printf("Motor 0 moved too much\n");
845 return false;
846 }
847 if (!CountValid(MOTOR1_ENCODER_FTM->CNT)) {
848 printf("Motor 1 moved too much\n");
849 return false;
850 }
851
852 DisableInterrupts disable_interrupts;
853 const SmallInitReadings readings = AdcReadSmallInit(disable_interrupts);
854 motor0_sum += readings.motor0_abs;
855 motor1_sum += readings.motor1_abs;
856 wheel_sum += readings.wheel_abs;
857 }
858
859 *motor0_offset = (motor0_sum + kNumberSamples / 2) / kNumberSamples;
860 *motor1_offset = (motor1_sum + kNumberSamples / 2) / kNumberSamples;
861 *wheel_offset = (wheel_sum + kNumberSamples / 2) / kNumberSamples;
862
863 return true;
864}
865
866} // namespace
867
868extern "C" int main() {
869 // for background about this startup delay, please see these conversations
870 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
871 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
872 delay(400);
873
874 // Set all interrupts to the second-lowest priority to start with.
875 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
876
877 // Now set priorities for all the ones we care about. They only have meaning
878 // relative to each other, which means centralizing them here makes it a lot
879 // more manageable.
880 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
881 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0x3);
882 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0x3);
883 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
884
885 // Set the LED's pin to output mode.
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500886 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800887 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
888
889 // Set up the CAN pins.
890 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
891 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
892
Brian Silvermanff7b3872018-03-10 18:08:30 -0800893 // .1ms filter time.
894 PORTA_DFWR = PORTC_DFWR = PORTD_DFWR = PORTE_DFWR = 6000;
895
Brian Silverman6260c092018-01-14 15:21:36 -0800896 // BTN0
897 PORTC_PCR7 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800898 PORTC_DFER |= 1 << 7;
Brian Silverman6260c092018-01-14 15:21:36 -0800899 // BTN1
900 PORTE_PCR26 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800901 PORTE_DFER |= 1 << 26;
Brian Silverman6260c092018-01-14 15:21:36 -0800902 // BTN2
903 PORTA_PCR14 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800904 PORTA_DFER |= 1 << 14;
Brian Silverman6260c092018-01-14 15:21:36 -0800905 // BTN3
906 PORTD_PCR0 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800907 PORTD_DFER |= 1 << 0;
908 // BTN4
909 PORTD_PCR7 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
910 PORTD_DFER |= 1 << 7;
911 // BTN5 (only new revision)
912 PORTA_PCR15 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
913 PORTA_DFER |= 1 << 15;
Brian Silverman6260c092018-01-14 15:21:36 -0800914
915 PORTA_PCR5 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
916
Brian Silverman45564a82018-09-02 16:35:22 -0700917 DMA.CR = M_DMA_EMLM;
Brian Silverman6260c092018-01-14 15:21:36 -0800918
Brian Silverman4787a6e2018-10-06 16:00:54 -0700919 PrintingParameters printing_parameters;
920 printing_parameters.dedicated_usb = true;
921 const ::std::unique_ptr<PrintingImplementation> printing =
922 CreatePrinting(printing_parameters);
923 printing->Initialize();
Brian Silverman6260c092018-01-14 15:21:36 -0800924
925 AdcInitSmall();
926 MathInit();
927 delay(100);
928 can_init(2, 3);
929
930 GPIOD_PCOR = 1 << 3;
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500931 PERIPHERAL_BITBAND(GPIOD_PDDR, 3) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800932 PORTD_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(1);
933 GPIOD_PSOR = 1 << 3;
934
935 GPIOC_PCOR = 1 << 4;
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500936 PERIPHERAL_BITBAND(GPIOC_PDDR, 4) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800937 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
938 GPIOC_PSOR = 1 << 4;
939
940 LittleMotorControlsImplementation controls0, controls1;
941
942 delay(100);
943
944 // M0_EA = FTM1_QD_PHB
945 PORTB_PCR19 = PORT_PCR_MUX(6);
946 // M0_EB = FTM1_QD_PHA
947 PORTB_PCR18 = PORT_PCR_MUX(6);
948
949 // M1_EA = FTM1_QD_PHA
950 PORTB_PCR0 = PORT_PCR_MUX(6);
951 // M1_EB = FTM1_QD_PHB
952 PORTB_PCR1 = PORT_PCR_MUX(6);
953
954 // M0_CH0 = FTM3_CH4
955 PORTC_PCR8 = PORT_PCR_DSE | PORT_PCR_MUX(3);
956 // M0_CH1 = FTM3_CH2
957 PORTD_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
958 // M0_CH2 = FTM3_CH6
959 PORTC_PCR10 = PORT_PCR_DSE | PORT_PCR_MUX(3);
960
961 // M1_CH0 = FTM0_CH0
962 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
963 // M1_CH1 = FTM0_CH2
964 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
965 // M1_CH2 = FTM0_CH4
966 PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
967
968 Motor motor0(
969 MOTOR0_PWM_FTM, MOTOR0_ENCODER_FTM, &controls0,
970 {&MOTOR0_PWM_FTM->C4V, &MOTOR0_PWM_FTM->C2V, &MOTOR0_PWM_FTM->C6V});
Brian Silverman4787a6e2018-10-06 16:00:54 -0700971 motor0.set_printing_implementation(printing.get());
Brian Silverman6260c092018-01-14 15:21:36 -0800972 motor0.set_switching_divisor(kSwitchingDivisor);
973 Motor motor1(
974 MOTOR1_PWM_FTM, MOTOR1_ENCODER_FTM, &controls1,
975 {&MOTOR1_PWM_FTM->C0V, &MOTOR1_PWM_FTM->C2V, &MOTOR1_PWM_FTM->C4V});
Brian Silverman4787a6e2018-10-06 16:00:54 -0700976 motor1.set_printing_implementation(printing.get());
Brian Silverman6260c092018-01-14 15:21:36 -0800977 motor1.set_switching_divisor(kSwitchingDivisor);
978 ConfigurePwmFtm(MOTOR0_PWM_FTM);
979 ConfigurePwmFtm(MOTOR1_PWM_FTM);
980 motor0.Init();
981 motor1.Init();
982 global_motor0.store(&motor0, ::std::memory_order_relaxed);
983 global_motor1.store(&motor1, ::std::memory_order_relaxed);
984
985 SIM_SCGC6 |= SIM_SCGC6_PIT;
Brian Silvermanb0de2402018-03-24 03:48:28 -0400986 // Workaround for errata e7914.
987 (void)PIT_MCR;
Brian Silverman6260c092018-01-14 15:21:36 -0800988 PIT_MCR = 0;
Brian Silvermanb0de2402018-03-24 03:48:28 -0400989 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 1000) - 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800990 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
991
992 // Have them both wait for the GTB signal.
993 FTM0->CONF = FTM3->CONF =
994 FTM_CONF_GTBEEN | FTM_CONF_NUMTOF(kSwitchingDivisor - 1);
995 // Make FTM3's period half of what it should be so we can get it a half-cycle
996 // out of phase.
997 const uint32_t original_mod = FTM3->MOD;
998 FTM3->MOD = ((original_mod + 1) / 2) - 1;
999 FTM3->SYNC |= FTM_SYNC_SWSYNC;
1000
1001 // Output triggers to things like the PDBs on initialization.
1002 FTM0_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;
1003 FTM3_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;
1004 // Don't let any memory accesses sneak past here, because we actually
1005 // need everything to be starting up.
1006 __asm__("" ::: "memory");
1007
1008 // Give everything a chance to get going.
1009 delay(100);
1010
1011 printf("BSS: %p-%p\n", __bss_ram_start__, __bss_ram_end__);
1012 printf("data: %p-%p\n", __data_ram_start__, __data_ram_end__);
1013 printf("heap start: %p\n", __heap_start__);
1014 printf("stack start: %p\n", __stack_end__);
1015
Austin Schuh745e4ed2019-04-07 15:50:16 -07001016 trigger_cogging_torque.store(ProcessorIndex() == 0 ? kTriggerCoggingTorque0
1017 : kTriggerCoggingTorque1);
1018 wheel_cogging_torque.store(ProcessorIndex() == 0 ? kWheelCoggingTorque0
1019 : kWheelCoggingTorque1);
1020
Austin Schuh80b99932019-04-07 14:04:41 -07001021 printf("Zeroing motors for %d:%x\n", static_cast<int>(ProcessorIndex()),
1022 (unsigned int)ProcessorIdentifier());
Brian Silverman6260c092018-01-14 15:21:36 -08001023 uint16_t motor0_offset, motor1_offset, wheel_offset;
1024 while (!ZeroMotors(&motor0_offset, &motor1_offset, &wheel_offset)) {
1025 }
1026 printf("Done zeroing\n");
1027
1028 const float motor0_offset_scaled = -analog_ratio(motor0_offset);
1029 const float motor1_offset_scaled = analog_ratio(motor1_offset);
1030 // Good for the initial trigger.
1031 {
Austin Schuh4a8d4922019-04-07 15:31:30 -07001032 // Calibrate winding phase error here.
1033 const float kZeroOffset0 = ProcessorIndex() == 1 ? 0.275f : 0.27f;
Brian Silverman6260c092018-01-14 15:21:36 -08001034 const int motor0_starting_point = static_cast<int>(
1035 (motor0_offset_scaled + (kZeroOffset0 / 7.0f)) * 4096.0f);
1036 printf("Motor 0 starting at %d\n", motor0_starting_point);
1037 motor0.set_encoder_calibration_offset(motor0_starting_point);
1038 motor0.set_encoder_multiplier(-1);
1039
Austin Schuh4a8d4922019-04-07 15:31:30 -07001040 // Calibrate output coordinate neutral here.
1041 motor0.set_encoder_offset(
1042 motor0.encoder_offset() +
1043 (ProcessorIndex() == 1 ? (-3096 - 430 - 30 - 6) : (-2065 + 20)));
1044
1045 while (true) {
1046 const uint32_t encoder =
1047 global_motor0.load(::std::memory_order_relaxed)->wrapped_encoder();
1048 const int32_t absolute_encoder =
1049 global_motor0.load(::std::memory_order_relaxed)
1050 ->absolute_encoder(encoder);
1051
1052 if (absolute_encoder > 2047) {
1053 motor0.set_encoder_offset(motor0.encoder_offset() - 4096);
1054 } else if (absolute_encoder < -2047) {
1055 motor0.set_encoder_offset(motor0.encoder_offset() + 4096);
1056 } else {
1057 break;
1058 }
1059 }
Brian Silverman6260c092018-01-14 15:21:36 -08001060
1061 uint32_t new_encoder = motor0.wrapped_encoder();
1062 int32_t absolute_encoder = motor0.absolute_encoder(new_encoder);
1063 printf("Motor 0 encoder %d absolute %d\n", static_cast<int>(new_encoder),
1064 static_cast<int>(absolute_encoder));
1065 }
1066
1067 {
Austin Schuh4a8d4922019-04-07 15:31:30 -07001068 const float kZeroOffset1 = ProcessorIndex() == 1 ? 0.420f : 0.26f;
Brian Silverman6260c092018-01-14 15:21:36 -08001069 const int motor1_starting_point = static_cast<int>(
1070 (motor1_offset_scaled + (kZeroOffset1 / 7.0f)) * 4096.0f);
1071 printf("Motor 1 starting at %d\n", motor1_starting_point);
1072 motor1.set_encoder_calibration_offset(motor1_starting_point);
1073 motor1.set_encoder_multiplier(-1);
1074
1075 float wheel_position = absolute_wheel(analog_ratio(wheel_offset));
1076
1077 uint32_t encoder = motor1.wrapped_encoder();
1078
1079 printf("Wheel starting at %d, encoder %" PRId32 "\n",
1080 static_cast<int>(wheel_position * 1000.0f), encoder);
1081
1082 constexpr float kWheelGearRatio = (1.25f + 0.02f) / 0.35f;
Austin Schuh4a8d4922019-04-07 15:31:30 -07001083 const float kWrappedWheelAtZero =
1084 ProcessorIndex() == 1 ? (0.934630859375f) : 0.6586310546875f;
Brian Silverman6260c092018-01-14 15:21:36 -08001085
1086 const int encoder_wraps =
1087 static_cast<int>(lround(wheel_position * kWheelGearRatio -
1088 (encoder / 4096.f) + kWrappedWheelAtZero));
1089
1090 printf("Wraps: %d\n", encoder_wraps);
1091 motor1.set_encoder_offset(4096 * encoder_wraps + motor1.encoder_offset() -
1092 static_cast<int>(kWrappedWheelAtZero * 4096));
1093 printf("Wheel encoder now at %d\n",
1094 static_cast<int>(1000.f / 4096.f *
1095 motor1.absolute_encoder(motor1.wrapped_encoder())));
1096 }
1097
1098 // Turn an LED on for Austin.
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001099 PERIPHERAL_BITBAND(GPIOC_PDDR, 6) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -08001100 GPIOC_PCOR = 1 << 6;
1101 PORTC_PCR6 = PORT_PCR_DSE | PORT_PCR_MUX(1);
1102
1103 // M0_THW
1104 PORTC_PCR11 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1105 // M0_FAULT
1106 PORTD_PCR6 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1107 // M1_THW
1108 PORTC_PCR2 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1109 // M1_FAULT
1110 PORTD_PCR5 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1111
1112 motor0.Start();
1113 motor1.Start();
1114 {
1115 // We rely on various things happening faster than the timer period, so make
1116 // sure slow USB or whatever interrupts don't prevent that.
1117 DisableInterrupts disable_interrupts;
1118
1119 // First clear the overflow flag.
1120 FTM3->SC &= ~FTM_SC_TOF;
1121
1122 // Now poke the GTB to actually start both timers.
1123 FTM0->CONF = FTM_CONF_GTBEEN | FTM_CONF_GTBEOUT |
1124 FTM_CONF_NUMTOF(kSwitchingDivisor - 1);
1125
1126 // Wait for it to overflow twice. For some reason, just once doesn't work.
1127 while (!(FTM3->SC & FTM_SC_TOF)) {
1128 }
1129 FTM3->SC &= ~FTM_SC_TOF;
1130 while (!(FTM3->SC & FTM_SC_TOF)) {
1131 }
1132
1133 // Now put the MOD value back to what it was.
1134 FTM3->MOD = original_mod;
1135 FTM3->PWMLOAD = FTM_PWMLOAD_LDOK;
1136
1137 // And then clear the overflow flags before enabling interrupts so we
1138 // actually wait until the next overflow to start doing interrupts.
1139 FTM0->SC &= ~FTM_SC_TOF;
1140 FTM3->SC &= ~FTM_SC_TOF;
1141 NVIC_ENABLE_IRQ(IRQ_FTM0);
1142 NVIC_ENABLE_IRQ(IRQ_FTM3);
1143 }
1144 global_trigger_plant.store(
1145 new StateFeedbackPlant<3, 1, 1, float>(MakeIntegralHapticTriggerPlant()));
1146 global_trigger_observer.store(new StateFeedbackObserver<3, 1, 1, float>(
1147 MakeIntegralHapticTriggerObserver()));
1148 global_trigger_observer.load(::std::memory_order_relaxed)
1149 ->Reset(global_trigger_plant.load(::std::memory_order_relaxed));
1150
1151 global_wheel_plant.store(
1152 new StateFeedbackPlant<3, 1, 1, float>(MakeIntegralHapticWheelPlant()));
1153 global_wheel_observer.store(new StateFeedbackObserver<3, 1, 1, float>(
1154 MakeIntegralHapticWheelObserver()));
1155 global_wheel_observer.load(::std::memory_order_relaxed)
1156 ->Reset(global_wheel_plant.load(::std::memory_order_relaxed));
1157
1158 delay(1000);
1159
1160 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
1161
1162 // TODO(Brian): Use SLEEPONEXIT to reduce interrupt latency?
1163 while (true) {
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001164 if (!PERIPHERAL_BITBAND(GPIOC_PDIR, 11)) {
1165 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001166 printf("M0_THW\n");
1167 }
1168 GPIOC_PSOR = 1 << 5;
1169 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001170 if (!PERIPHERAL_BITBAND(GPIOD_PDIR, 6)) {
1171 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001172 printf("M0_FAULT\n");
1173 }
1174 GPIOC_PSOR = 1 << 5;
1175 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001176 if (!PERIPHERAL_BITBAND(GPIOC_PDIR, 2)) {
1177 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001178 printf("M1_THW\n");
1179 }
1180 GPIOC_PSOR = 1 << 5;
1181 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001182 if (!PERIPHERAL_BITBAND(GPIOD_PDIR, 5)) {
1183 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001184 printf("M1_FAULT\n");
1185 }
1186 GPIOC_PSOR = 1 << 5;
1187 }
1188 }
1189
1190 return 0;
1191}
1192
Brian Silvermana96c1a42018-05-12 12:11:31 -07001193} // namespace motors
Brian Silverman6260c092018-01-14 15:21:36 -08001194} // namespace frc971