blob: 32a028587c0a5d2f0a6314697e6f195c6f7122ec [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"
Brian Silverman6260c092018-01-14 15:21:36 -080013#include "motors/peripheral/can.h"
Austin Schuhb402fd42019-04-13 00:02:53 -070014#include "motors/pistol_grip/controller_adc.h"
Brian Silverman6260c092018-01-14 15:21:36 -080015#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
Austin Schuhc1b2ca12019-04-13 00:04:10 -070073// Cached version for speed.
74const uint8_t processor_index = ProcessorIndex();
Brian Silverman9ed2cf12018-05-12 13:06:38 -070075
Brian Silverman6260c092018-01-14 15:21:36 -080076constexpr float kHapticWheelCurrentLimit = static_cast<float>(
77 ::frc971::control_loops::drivetrain::kHapticWheelCurrentLimit);
78constexpr float kHapticTriggerCurrentLimit = static_cast<float>(
79 ::frc971::control_loops::drivetrain::kHapticTriggerCurrentLimit);
80
81::std::atomic<Motor *> global_motor0{nullptr}, global_motor1{nullptr};
Brian Silverman6260c092018-01-14 15:21:36 -080082
83// Angle last time the current loop ran.
84::std::atomic<float> global_wheel_angle{0.0f};
85::std::atomic<float> global_trigger_angle{0.0f};
86
87// Wheel observer/plant.
88::std::atomic<StateFeedbackObserver<3, 1, 1, float> *> global_wheel_observer{
89 nullptr};
90::std::atomic<StateFeedbackPlant<3, 1, 1, float> *> global_wheel_plant{nullptr};
91// Throttle observer/plant.
92::std::atomic<StateFeedbackObserver<3, 1, 1, float> *> global_trigger_observer{
93 nullptr};
94::std::atomic<StateFeedbackPlant<3, 1, 1, float> *> global_trigger_plant{
95 nullptr};
96
97// Torques for the current loop to apply.
98::std::atomic<float> global_wheel_current{0.0f};
Austin Schuhc1b2ca12019-04-13 00:04:10 -070099::std::atomic<float> global_trigger_current{0.0f};
Brian Silverman6260c092018-01-14 15:21:36 -0800100
101constexpr int kSwitchingDivisor = 2;
102
103float analog_ratio(uint16_t reading) {
104 static constexpr uint16_t kMin = 260, kMax = 3812;
105 return static_cast<float>(::std::max(::std::min(reading, kMax), kMin) -
106 kMin) /
107 static_cast<float>(kMax - kMin);
108}
109
110constexpr float InterpolateFloat(float x1, float x0, float y1, float y0, float x) {
111 return (x - x0) * (y1 - y0) / (x1 - x0) + y0;
112}
113
114float absolute_wheel(float wheel_position) {
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700115 const float kCenterOffset = (processor_index == 1) ? -0.683f : -0.935f;
Austin Schuh4a8d4922019-04-07 15:31:30 -0700116
117 wheel_position += kCenterOffset;
118
119 if (wheel_position > 0.5f) {
120 wheel_position -= 1.0f;
121 } else if (wheel_position < -0.5f) {
Brian Silverman6260c092018-01-14 15:21:36 -0800122 wheel_position += 1.0f;
123 }
Brian Silverman6260c092018-01-14 15:21:36 -0800124 return wheel_position;
125}
126
127extern "C" {
128
129void *__stack_chk_guard = (void *)0x67111971;
130void __stack_chk_fail() {
131 while (true) {
132 GPIOC_PSOR = (1 << 5);
133 printf("Stack corruption detected\n");
134 delay(1000);
135 GPIOC_PCOR = (1 << 5);
136 delay(1000);
137 }
138}
139
Brian Silverman6260c092018-01-14 15:21:36 -0800140extern uint32_t __bss_ram_start__[], __bss_ram_end__[];
141extern uint32_t __data_ram_start__[], __data_ram_end__[];
142extern uint32_t __heap_start__[], __heap_end__[];
143extern uint32_t __stack_end__[];
144
145} // extern "C"
146
147constexpr float kWheelMaxExtension = 1.0f;
148constexpr float kWheelFrictionMax = 0.2f;
149float WheelCenteringCurrent(float scalar, float angle, float velocity) {
150 float friction_goal_current = -angle * 10.0f;
151 if (friction_goal_current > kWheelFrictionMax) {
152 friction_goal_current = kWheelFrictionMax;
153 } else if (friction_goal_current < -kWheelFrictionMax) {
154 friction_goal_current = -kWheelFrictionMax;
155 }
156
157 constexpr float kWheelSpringNonlinearity = 0.45f;
158
159 float goal_current = -((1.0f - kWheelSpringNonlinearity) * angle +
160 kWheelSpringNonlinearity * angle * angle * angle) *
161 6.0f -
162 velocity * 0.04f;
163 if (goal_current > 5.0f - scalar) {
164 goal_current = 5.0f - scalar;
165 } else if (goal_current < -5.0f + scalar) {
166 goal_current = -5.0f + scalar;
167 }
168
169 return goal_current * scalar + friction_goal_current;
170}
171
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700172float CoggingCurrent1(uint32_t encoder, int32_t absolute_encoder) {
173 constexpr float kP = 0.05f;
174 constexpr float kI = 0.00001f;
175 static int goal = -6700;
176
177 const int error = goal - static_cast<int>(absolute_encoder);
178 static float error_sum = 0.0f;
179 float goal_current = static_cast<float>(error) * kP + error_sum * kI;
180
181 goal_current = ::std::min(1.0f, ::std::max(-1.0f, goal_current));
182
183 static int i = 0;
184 if (error == 0) {
185 ++i;
186 } else {
187 i = 0;
188 }
189 if (i >= 100) {
190 printf("reading1: %d %d a:%d e:%d\n", goal,
191 static_cast<int>(goal_current * 10000.0f),
192 static_cast<int>(encoder),
193 static_cast<int>(error));
194 static int counting_up = 0;
195 if (absolute_encoder <= -6900) {
196 counting_up = 1;
197 } else if (absolute_encoder >= 6900) {
198 counting_up = 0;
199 }
200 if (counting_up) {
201 ++goal;
202 } else {
203 --goal;
204 }
205 i = 0;
206 }
207
208 error_sum += static_cast<float>(error);
209 if (error_sum > 1.0f / kI) {
210 error_sum = 1.0f / kI;
211 } else if (error_sum < -1.0f / kI) {
212 error_sum = -1.0f / kI;
213 }
214 return goal_current;
215}
216
Brian Silverman6260c092018-01-14 15:21:36 -0800217extern "C" void ftm0_isr() {
218 SmallAdcReadings readings;
219 {
220 DisableInterrupts disable_interrupts;
221 readings = AdcReadSmall1(disable_interrupts);
222 }
223 uint32_t encoder =
224 global_motor1.load(::std::memory_order_relaxed)->wrapped_encoder();
225 int32_t absolute_encoder = global_motor1.load(::std::memory_order_relaxed)
226 ->absolute_encoder(encoder);
227
228 const float angle = absolute_encoder / static_cast<float>((15320 - 1488) / 2);
Brian Silverman6260c092018-01-14 15:21:36 -0800229
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700230 (void)CoggingCurrent1;
Austin Schuh54c8c842019-04-07 13:54:23 -0700231 float goal_current = global_wheel_current.load(::std::memory_order_relaxed) +
Austin Schuh745e4ed2019-04-07 15:50:16 -0700232 WheelCoggingTorque(encoder);
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700233 //float goal_current = CoggingCurrent1(encoder, absolute_encoder);
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700234 // float goal_current = kWheelCoggingTorque[encoder];
235 // float goal_current = 0.0f;
Brian Silverman6260c092018-01-14 15:21:36 -0800236
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700237 // Controller 0 is mechanical and doesn't need the motor controls.
238 if (processor_index == 0) {
239 global_motor1.load(::std::memory_order_relaxed)->CycleFixedPhaseInterupt(0);
240 } else {
241 global_motor1.load(::std::memory_order_relaxed)
242 ->SetGoalCurrent(goal_current);
243 global_motor1.load(::std::memory_order_relaxed)
244 ->CurrentInterrupt(BalanceSimpleReadings(readings.currents), encoder);
245 global_wheel_angle.store(angle);
246 }
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700247 //global_motor1.load(::std::memory_order_relaxed)->CycleFixedPhaseInterupt();
Austin Schuh54c8c842019-04-07 13:54:23 -0700248
Austin Schuh5b0e6b62019-04-07 14:23:37 -0700249
250 /*
251 SmallInitReadings position_readings;
252 {
253 DisableInterrupts disable_interrupts;
254 position_readings = AdcReadSmallInit(disable_interrupts);
255 }
256
257 static int i = 0;
258 if (i == 1000) {
259 i = 0;
260 float wheel_position =
261 absolute_wheel(analog_ratio(position_readings.wheel_abs));
262 printf(
263 "ecnt %" PRIu32 " arev:%d erev:%d abs:%d awp:%d uncalwheel:%d\n",
264 encoder,
265 static_cast<int>((1.0f - analog_ratio(position_readings.motor1_abs)) *
266 7000.0f),
267 static_cast<int>(encoder * 7.0f / 4096.0f * 1000.0f),
268 static_cast<int>(absolute_encoder),
269 static_cast<int>(wheel_position * 1000.0f),
270 static_cast<int>(analog_ratio(position_readings.wheel_abs) * 1000.0f));
271 } else if (i == 200) {
272 printf("out %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
273 global_motor1.load(::std::memory_order_relaxed)
274 ->output_registers()[0][2],
275 global_motor1.load(::std::memory_order_relaxed)
276 ->output_registers()[1][2],
277 global_motor1.load(::std::memory_order_relaxed)
278 ->output_registers()[2][2]);
279 }
280 ++i;
281 */
Brian Silverman6260c092018-01-14 15:21:36 -0800282}
283
Austin Schuh876b4f02018-03-10 19:16:59 -0800284constexpr float kTriggerMaxExtension = -0.70f;
Brian Silverman6260c092018-01-14 15:21:36 -0800285constexpr float kTriggerCenter = 0.0f;
Austin Schuh876b4f02018-03-10 19:16:59 -0800286constexpr float kCenteringStiffness = 0.15f;
Brian Silverman6260c092018-01-14 15:21:36 -0800287float TriggerCenteringCurrent(float trigger_angle) {
288 float goal_current = (kTriggerCenter - trigger_angle) * 3.0f;
Austin Schuh876b4f02018-03-10 19:16:59 -0800289 float knotch_goal_current = (kTriggerCenter - trigger_angle) * 8.0f;
290 if (knotch_goal_current < -kCenteringStiffness) {
291 knotch_goal_current = -kCenteringStiffness;
292 } else if (knotch_goal_current > kCenteringStiffness) {
293 knotch_goal_current = kCenteringStiffness;
294 }
295
296 goal_current += knotch_goal_current;
297
Brian Silverman6260c092018-01-14 15:21:36 -0800298 if (goal_current < -1.0f) {
299 goal_current = -1.0f;
300 } else if (goal_current > 1.0f) {
301 goal_current = 1.0f;
302 if (trigger_angle < kTriggerMaxExtension) {
303 goal_current -= (30.0f * (trigger_angle - kTriggerMaxExtension));
Austin Schuh876b4f02018-03-10 19:16:59 -0800304 if (goal_current > 4.0f) {
305 goal_current = 4.0f;
Brian Silverman6260c092018-01-14 15:21:36 -0800306 }
307 }
308 }
309 return goal_current;
310}
311
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700312float CoggingCurrent0(uint32_t encoder, int32_t absolute_encoder) {
313 constexpr float kP = 0.05f;
314 constexpr float kI = 0.00001f;
315 static int goal = 0;
316
317 const int error = goal - static_cast<int>(absolute_encoder);
318 static float error_sum = 0.0f;
319 float goal_current = static_cast<float>(error) * kP + error_sum * kI;
320
321 goal_current = ::std::min(1.0f, ::std::max(-1.0f, goal_current));
322
323 static int i = 0;
324 if (error == 0) {
325 ++i;
326 } else {
327 i = 0;
328 }
329
330 if (i >= 100) {
331 printf("reading0: %d %d a:%d e:%d\n", goal,
332 static_cast<int>(goal_current * 10000.0f),
333 static_cast<int>(encoder),
334 static_cast<int>(error));
335 static int counting_up = 0;
336 if (absolute_encoder <= -1390) {
337 counting_up = 1;
338 } else if (absolute_encoder >= 1390) {
339 counting_up = 0;
340 }
341 if (counting_up) {
342 ++goal;
343 } else {
344 --goal;
345 }
346 }
347
348 error_sum += static_cast<float>(error);
349 if (error_sum > 1.0f / kI) {
350 error_sum = 1.0f / kI;
351 } else if (error_sum < -1.0f / kI) {
352 error_sum = -1.0f / kI;
353 }
354 return goal_current;
355}
356
Brian Silverman6260c092018-01-14 15:21:36 -0800357extern "C" void ftm3_isr() {
358 SmallAdcReadings readings;
359 {
360 DisableInterrupts disable_interrupts;
361 readings = AdcReadSmall0(disable_interrupts);
362 }
Brian Silverman6260c092018-01-14 15:21:36 -0800363
Austin Schuh54c8c842019-04-07 13:54:23 -0700364 const uint32_t encoder =
365 global_motor0.load(::std::memory_order_relaxed)->wrapped_encoder();
366 const int32_t absolute_encoder =
367 global_motor0.load(::std::memory_order_relaxed)
368 ->absolute_encoder(encoder);
369
370 const float trigger_angle = absolute_encoder / 1370.f;
Brian Silverman6260c092018-01-14 15:21:36 -0800371
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700372 (void)CoggingCurrent0;
Brian Silverman6260c092018-01-14 15:21:36 -0800373 const float goal_current =
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700374 global_trigger_current.load(::std::memory_order_relaxed) +
Austin Schuh745e4ed2019-04-07 15:50:16 -0700375 TriggerCoggingTorque(encoder);
Austin Schuh54c8c842019-04-07 13:54:23 -0700376 //const float goal_current = kTriggerCoggingTorque[encoder];
377 //const float goal_current = 0.0f;
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700378 //const float goal_current = CoggingCurrent0(encoder, absolute_encoder);
Brian Silverman6260c092018-01-14 15:21:36 -0800379
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700380 if (processor_index == 0) {
381 global_motor0.load(::std::memory_order_relaxed)->CycleFixedPhaseInterupt(0);
382 } else {
383 global_motor0.load(::std::memory_order_relaxed)
384 ->SetGoalCurrent(goal_current);
385 global_motor0.load(::std::memory_order_relaxed)
386 ->CurrentInterrupt(BalanceSimpleReadings(readings.currents), encoder);
387 global_trigger_angle.store(trigger_angle);
388 }
Austin Schuhfbbf0f02019-04-07 14:27:16 -0700389 //global_motor0.load(::std::memory_order_relaxed)->CycleFixedPhaseInterupt();
Brian Silverman6260c092018-01-14 15:21:36 -0800390
Austin Schuh5b0e6b62019-04-07 14:23:37 -0700391
392 /*
393 SmallInitReadings position_readings;
394 {
395 DisableInterrupts disable_interrupts;
396 position_readings = AdcReadSmallInit(disable_interrupts);
397 }
398
399 static int i = 0;
400 if (i == 1000) {
401 i = 0;
402 printf("ecnt %" PRIu32 " arev:%d erev:%d abs:%d\n", encoder,
403 static_cast<int>((analog_ratio(position_readings.motor0_abs)) *
404 7000.0f),
405 static_cast<int>(encoder * 7.0f / 4096.0f * 1000.0f),
406 static_cast<int>(absolute_encoder));
407 } else if (i == 200) {
408 printf("out %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
409 global_motor0.load(::std::memory_order_relaxed)
410 ->output_registers()[0][2],
411 global_motor0.load(::std::memory_order_relaxed)
412 ->output_registers()[1][2],
413 global_motor0.load(::std::memory_order_relaxed)
414 ->output_registers()[2][2]);
415 }
416 ++i;
417 */
Brian Silverman6260c092018-01-14 15:21:36 -0800418}
419
Brian Silverman6260c092018-01-14 15:21:36 -0800420int ConvertFloat16(float val) {
421 int result = static_cast<int>(val * 32768.0f) + 32768;
422 if (result > 0xffff) {
423 result = 0xffff;
424 } else if (result < 0) {
425 result = 0;
426 }
427 return result;
428}
429int ConvertFloat14(float val) {
430 int result = static_cast<int>(val * 8192.0f) + 8192;
431 if (result > 0x3fff) {
432 result = 0x3fff;
433 } else if (result < 0) {
434 result = 0;
435 }
436 return result;
437}
438
439extern "C" void pit3_isr() {
440 PIT_TFLG3 = 1;
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700441
442 // If we are a mechanical pistol grip, do the encoder angle calculation here
443 // since the high frequency interrupt isn't running.
444 if (processor_index == 0) {
445 const uint32_t trigger_encoder =
446 global_motor0.load(::std::memory_order_relaxed)->wrapped_encoder();
447 const int32_t trigger_absolute_encoder =
448 global_motor0.load(::std::memory_order_relaxed)
449 ->absolute_encoder(trigger_encoder);
450
451 const float trigger_angle =
452 trigger_absolute_encoder /
453 (trigger_absolute_encoder < 0.0 ? 198.0f : 252.0f);
454 global_trigger_angle.store(
455 ::std::max(-1.0f, ::std::min(1.0f, trigger_angle)));
456
457 uint32_t wheel_encoder =
458 global_motor1.load(::std::memory_order_relaxed)->wrapped_encoder();
459 int32_t wheel_absolute_encoder =
460 global_motor1.load(::std::memory_order_relaxed)
461 ->absolute_encoder(wheel_encoder);
462
463 const float wheel_angle = wheel_absolute_encoder / 1787.0f;
464 global_wheel_angle.store(::std::max(-1.0f, ::std::min(1.0f, wheel_angle)));
465
466 /*
467 // Calibration constants
468 static int k = 0;
469 ++k;
470 if (k > 100) {
471 printf("trigger: %d -> %d wheel %d -> %d -> %d\n",
472 static_cast<int>(trigger_encoder),
473 static_cast<int>(global_trigger_angle * 1000.0f),
474 static_cast<int>(wheel_encoder),
475 static_cast<int>(wheel_absolute_encoder),
476 static_cast<int>(global_wheel_angle * 1000.0f));
477 k = 0;
478 }
479 */
480 }
481
Brian Silverman6260c092018-01-14 15:21:36 -0800482 const float absolute_trigger_angle =
483 global_trigger_angle.load(::std::memory_order_relaxed);
484 const float absolute_wheel_angle =
485 global_wheel_angle.load(::std::memory_order_relaxed);
486
487 // Force a barrier here so we sample everything guaranteed at the beginning.
488 __asm__("" ::: "memory");
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700489
Brian Silverman6260c092018-01-14 15:21:36 -0800490 const float absolute_wheel_angle_radians =
491 absolute_wheel_angle * static_cast<float>(M_PI) * (338.16f / 360.0f);
492 const float absolute_trigger_angle_radians =
493 absolute_trigger_angle * static_cast<float>(M_PI) * (45.0f / 360.0f);
494
495 static uint32_t last_command_time = 0;
496 static float trigger_goal_position = 0.0f;
497 static float trigger_goal_velocity = 0.0f;
498 static float trigger_haptic_current = 0.0f;
499 static bool trigger_centering = true;
500 static bool trigger_haptics = false;
501 {
502 uint8_t data[8];
503 int length;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700504 can_receive(data, &length, 0);
Brian Silverman6260c092018-01-14 15:21:36 -0800505 if (length > 0) {
506 last_command_time = micros();
507 trigger_goal_position =
508 static_cast<float>(
509 static_cast<int32_t>(static_cast<uint32_t>(data[0]) |
510 (static_cast<uint32_t>(data[1]) << 8)) -
511 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700512 static_cast<float>(32768.0 * M_PI / 8.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800513 trigger_goal_velocity =
514 static_cast<float>(
515 static_cast<int32_t>(static_cast<uint32_t>(data[2]) |
516 (static_cast<uint32_t>(data[3]) << 8)) -
517 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700518 static_cast<float>(32768.0 * 4.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800519
520 trigger_haptic_current =
521 static_cast<float>(
522 static_cast<int32_t>(static_cast<uint32_t>(data[4]) |
523 (static_cast<uint32_t>(data[5]) << 8)) -
524 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700525 static_cast<float>(32768.0 * 2.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800526 if (trigger_haptic_current > kHapticTriggerCurrentLimit) {
527 trigger_haptic_current = kHapticTriggerCurrentLimit;
528 } else if (trigger_haptic_current < -kHapticTriggerCurrentLimit) {
529 trigger_haptic_current = -kHapticTriggerCurrentLimit;
530 }
531 trigger_centering = !!(data[7] & 0x01);
532 trigger_haptics = !!(data[7] & 0x02);
533 }
534 }
535
536 static float wheel_goal_position = 0.0f;
537 static float wheel_goal_velocity = 0.0f;
538 static float wheel_haptic_current = 0.0f;
539 static float wheel_kp = 0.0f;
540 static bool wheel_centering = true;
541 static float wheel_centering_scalar = 0.25f;
542 {
543 uint8_t data[8];
544 int length;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700545 can_receive(data, &length, 1);
Brian Silverman6260c092018-01-14 15:21:36 -0800546 if (length == 8) {
547 last_command_time = micros();
548 wheel_goal_position =
549 static_cast<float>(
550 static_cast<int32_t>(static_cast<uint32_t>(data[0]) |
551 (static_cast<uint32_t>(data[1]) << 8)) -
552 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700553 static_cast<float>(32768.0 * M_PI);
Brian Silverman6260c092018-01-14 15:21:36 -0800554 wheel_goal_velocity =
555 static_cast<float>(
556 static_cast<int32_t>(static_cast<uint32_t>(data[2]) |
557 (static_cast<uint32_t>(data[3]) << 8)) -
558 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700559 static_cast<float>(32768.0 * 10.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800560
561 wheel_haptic_current =
562 static_cast<float>(
563 static_cast<int32_t>(static_cast<uint32_t>(data[4]) |
564 (static_cast<uint32_t>(data[5]) << 8)) -
565 32768) /
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700566 static_cast<float>(32768.0 * 2.0);
Brian Silverman6260c092018-01-14 15:21:36 -0800567 if (wheel_haptic_current > kHapticWheelCurrentLimit) {
568 wheel_haptic_current = kHapticWheelCurrentLimit;
569 } else if (wheel_haptic_current < -kHapticWheelCurrentLimit) {
570 wheel_haptic_current = -kHapticWheelCurrentLimit;
571 }
572 wheel_kp = static_cast<float>(data[6]) * 30.0f / 255.0f;
573 wheel_centering = !!(data[7] & 0x01);
574 wheel_centering_scalar = ((data[7] >> 1) & 0x7f) / 127.0f;
575 }
576 }
577
578 static constexpr uint32_t kTimeout = 100000;
579 if (!time_after(time_add(last_command_time, kTimeout), micros())) {
580 last_command_time = time_subtract(micros(), kTimeout);
581 trigger_goal_position = 0.0f;
582 trigger_goal_velocity = 0.0f;
583 trigger_haptic_current = 0.0f;
584 trigger_centering = true;
585 trigger_haptics = false;
586
587 wheel_goal_position = 0.0f;
588 wheel_goal_velocity = 0.0f;
589 wheel_haptic_current = 0.0f;
590 wheel_centering = true;
591 wheel_centering_scalar = 0.25f;
Brian Silverman17ffa8c2018-03-09 18:27:29 -0800592 // Avoid wrapping back into the valid range.
593 last_command_time = time_subtract(micros(), kTimeout);
Brian Silverman6260c092018-01-14 15:21:36 -0800594 }
595
596 StateFeedbackPlant<3, 1, 1, float> *const trigger_plant =
597 global_trigger_plant.load(::std::memory_order_relaxed);
598 StateFeedbackObserver<3, 1, 1, float> *const trigger_observer =
599 global_trigger_observer.load(::std::memory_order_relaxed);
600 ::Eigen::Matrix<float, 1, 1> trigger_Y;
601 trigger_Y << absolute_trigger_angle_radians;
602 trigger_observer->Correct(*trigger_plant,
603 ::Eigen::Matrix<float, 1, 1>::Zero(), trigger_Y);
604
605 StateFeedbackPlant<3, 1, 1, float> *const wheel_plant =
606 global_wheel_plant.load(::std::memory_order_relaxed);
607 StateFeedbackObserver<3, 1, 1, float> *const wheel_observer =
608 global_wheel_observer.load(::std::memory_order_relaxed);
609 ::Eigen::Matrix<float, 1, 1> wheel_Y;
610 wheel_Y << absolute_wheel_angle_radians;
611 wheel_observer->Correct(*wheel_plant, ::Eigen::Matrix<float, 1, 1>::Zero(),
612 wheel_Y);
613
614 float kWheelD = (wheel_kp - 10.0f) * (0.25f - 0.20f) / 5.0f + 0.20f;
615 if (wheel_kp < 0.5f) {
616 kWheelD = wheel_kp * 0.05f / 0.5f;
617 } else if (wheel_kp < 1.0f) {
618 kWheelD = InterpolateFloat(1.0f, 0.5f, 0.06f, 0.05f, wheel_kp);
619 } else if (wheel_kp < 2.0f) {
620 kWheelD = InterpolateFloat(2.0f, 1.0f, 0.08f, 0.06f, wheel_kp);
621 } else if (wheel_kp < 3.0f) {
622 kWheelD = InterpolateFloat(3.0f, 2.0f, 0.10f, 0.08f, wheel_kp);
623 } else if (wheel_kp < 5.0f) {
624 kWheelD = InterpolateFloat(5.0f, 3.0f, 0.13f, 0.10f, wheel_kp);
625 } else if (wheel_kp < 10.0f) {
626 kWheelD = InterpolateFloat(10.0f, 5.0f, 0.20f, 0.13f, wheel_kp);
627 }
628
629 float wheel_goal_current = wheel_haptic_current;
630
631 wheel_goal_current +=
632 (wheel_goal_position - absolute_wheel_angle_radians) * wheel_kp +
633 (wheel_goal_velocity - wheel_observer->X_hat()(1, 0)) * kWheelD;
634
635 // Compute the torques to apply to each motor.
636 if (wheel_centering) {
637 wheel_goal_current +=
638 WheelCenteringCurrent(wheel_centering_scalar, absolute_wheel_angle,
639 wheel_observer->X_hat()(1, 0));
640 }
641
642 if (wheel_goal_current > kHapticWheelCurrentLimit) {
643 wheel_goal_current = kHapticWheelCurrentLimit;
644 } else if (wheel_goal_current < -kHapticWheelCurrentLimit) {
645 wheel_goal_current = -kHapticWheelCurrentLimit;
646 }
Brian Silverman6260c092018-01-14 15:21:36 -0800647
648 constexpr float kTriggerP =
649 static_cast<float>(::frc971::control_loops::drivetrain::kHapticTriggerP);
650 constexpr float kTriggerD =
651 static_cast<float>(::frc971::control_loops::drivetrain::kHapticTriggerD);
652 float trigger_goal_current = trigger_haptic_current;
653 if (trigger_haptics) {
654 trigger_goal_current +=
655 (trigger_goal_position - absolute_trigger_angle_radians) * kTriggerP +
656 (trigger_goal_velocity - trigger_observer->X_hat()(1, 0)) * kTriggerD;
657 }
658
659 if (trigger_centering) {
660 trigger_goal_current += TriggerCenteringCurrent(absolute_trigger_angle);
661 }
662
663 if (trigger_goal_current > kHapticTriggerCurrentLimit) {
664 trigger_goal_current = kHapticTriggerCurrentLimit;
665 } else if (trigger_goal_current < -kHapticTriggerCurrentLimit) {
666 trigger_goal_current = -kHapticTriggerCurrentLimit;
667 }
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700668
669 if (processor_index == 0) {
670 wheel_goal_current = 0.0f;
671 trigger_goal_current = 0.0f;
672 }
673 global_wheel_current.store(wheel_goal_current, ::std::memory_order_relaxed);
674 global_trigger_current.store(trigger_goal_current,
675 ::std::memory_order_relaxed);
Brian Silverman6260c092018-01-14 15:21:36 -0800676
677 uint8_t buttons = 0;
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500678 if (!PERIPHERAL_BITBAND(GPIOA_PDIR, 14)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800679 buttons |= 0x1;
680 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500681 if (!PERIPHERAL_BITBAND(GPIOE_PDIR, 26)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800682 buttons |= 0x2;
683 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500684 if (!PERIPHERAL_BITBAND(GPIOC_PDIR, 7)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800685 buttons |= 0x4;
686 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500687 if (!PERIPHERAL_BITBAND(GPIOD_PDIR, 0)) {
Brian Silverman6260c092018-01-14 15:21:36 -0800688 buttons |= 0x8;
689 }
690
691 float trigger_angle = absolute_trigger_angle;
692
693 // Adjust the trigger range for reporting back.
694 // TODO(austin): We'll likely need to make this symmetric for the controls to
695 // work out well.
696 if (trigger_angle > kTriggerCenter) {
697 trigger_angle = (trigger_angle - kTriggerCenter) / (1.0f - kTriggerCenter);
698 } else {
699 trigger_angle = (trigger_angle - kTriggerCenter) /
700 (kTriggerCenter - kTriggerMaxExtension);
701 }
702
703 // TODO(austin): Class + fns. This is a mess.
704 // TODO(austin): Move this to a separate file. It's too big.
705 int can_trigger = ConvertFloat16(absolute_trigger_angle);
706 int can_trigger_velocity =
707 ConvertFloat16(trigger_observer->X_hat()(1, 0) / 50.0f);
708 int can_trigger_torque =
709 ConvertFloat16(trigger_observer->X_hat()(2, 0) * 2.0f);
710 int can_trigger_current = ConvertFloat14(trigger_goal_current / 10.0f);
711
712 int can_wheel = ConvertFloat16(absolute_wheel_angle);
713 int can_wheel_velocity =
714 ConvertFloat16(wheel_observer->X_hat()(1, 0) / 50.0f);
715 int can_wheel_torque = ConvertFloat16(wheel_observer->X_hat()(2, 0) * 2.0f);
716 int can_wheel_current = ConvertFloat14(wheel_goal_current / 10.0f);
717
718 {
719 const uint8_t trigger_joystick_values[8] = {
720 static_cast<uint8_t>(can_trigger & 0xff),
721 static_cast<uint8_t>((can_trigger >> 8) & 0xff),
722 static_cast<uint8_t>(can_trigger_velocity & 0xff),
723 static_cast<uint8_t>((can_trigger_velocity >> 8) & 0xff),
724 static_cast<uint8_t>(can_trigger_torque & 0xff),
725 static_cast<uint8_t>((can_trigger_torque >> 8) & 0xff),
726 static_cast<uint8_t>(can_trigger_current & 0xff),
727 static_cast<uint8_t>(((buttons & 0x3) << 6) |
728 (can_trigger_current >> 8))};
729 const uint8_t wheel_joystick_values[8] = {
730 static_cast<uint8_t>(can_wheel & 0xff),
731 static_cast<uint8_t>((can_wheel >> 8) & 0xff),
732 static_cast<uint8_t>(can_wheel_velocity & 0xff),
733 static_cast<uint8_t>((can_wheel_velocity >> 8) & 0xff),
734 static_cast<uint8_t>(can_wheel_torque & 0xff),
735 static_cast<uint8_t>((can_wheel_torque >> 8) & 0xff),
736 static_cast<uint8_t>(can_wheel_current & 0xff),
737 static_cast<uint8_t>(((buttons & 0xc) << 4) |
738 (can_wheel_current >> 8))};
739
740 can_send(0, trigger_joystick_values, 8, 2);
741 can_send(1, wheel_joystick_values, 8, 3);
742 }
743
744 ::Eigen::Matrix<float, 1, 1> trigger_U;
745 trigger_U << trigger_goal_current;
746 ::Eigen::Matrix<float, 1, 1> wheel_U;
747 wheel_U << wheel_goal_current;
748 trigger_observer->Predict(trigger_plant, trigger_U,
749 ::std::chrono::milliseconds(1));
750 wheel_observer->Predict(wheel_plant, wheel_U, ::std::chrono::milliseconds(1));
751}
752
753void ConfigurePwmFtm(BigFTM *pwm_ftm) {
754 // Put them all into combine active-high mode, and all the low ones staying
755 // off all the time by default. We'll then use only the low ones.
756 pwm_ftm->C0SC = FTM_CSC_ELSB;
757 pwm_ftm->C0V = 0;
758 pwm_ftm->C1SC = FTM_CSC_ELSB;
759 pwm_ftm->C1V = 0;
760 pwm_ftm->C2SC = FTM_CSC_ELSB;
761 pwm_ftm->C2V = 0;
762 pwm_ftm->C3SC = FTM_CSC_ELSB;
763 pwm_ftm->C3V = 0;
764 pwm_ftm->C4SC = FTM_CSC_ELSB;
765 pwm_ftm->C4V = 0;
766 pwm_ftm->C5SC = FTM_CSC_ELSB;
767 pwm_ftm->C5V = 0;
768 pwm_ftm->C6SC = FTM_CSC_ELSB;
769 pwm_ftm->C6V = 0;
770 pwm_ftm->C7SC = FTM_CSC_ELSB;
771 pwm_ftm->C7V = 0;
772
773 pwm_ftm->COMBINE = FTM_COMBINE_SYNCEN3 /* Synchronize updates usefully */ |
774 FTM_COMBINE_COMP3 /* Make them complementary */ |
775 FTM_COMBINE_COMBINE3 /* Combine the channels */ |
776 FTM_COMBINE_SYNCEN2 /* Synchronize updates usefully */ |
777 FTM_COMBINE_COMP2 /* Make them complementary */ |
778 FTM_COMBINE_COMBINE2 /* Combine the channels */ |
779 FTM_COMBINE_SYNCEN1 /* Synchronize updates usefully */ |
780 FTM_COMBINE_COMP1 /* Make them complementary */ |
781 FTM_COMBINE_COMBINE1 /* Combine the channels */ |
782 FTM_COMBINE_SYNCEN0 /* Synchronize updates usefully */ |
783 FTM_COMBINE_COMP0 /* Make them complementary */ |
784 FTM_COMBINE_COMBINE0 /* Combine the channels */;
785}
786
787bool CountValid(uint32_t count) {
788 static constexpr int kMaxMovement = 1;
789 return count <= kMaxMovement || count >= (4096 - kMaxMovement);
790}
791
792bool ZeroMotors(uint16_t *motor0_offset, uint16_t *motor1_offset,
793 uint16_t *wheel_offset) {
794 static constexpr int kNumberSamples = 1024;
795 static_assert(UINT16_MAX * kNumberSamples <= UINT32_MAX, "Too many samples");
796 uint32_t motor0_sum = 0, motor1_sum = 0, wheel_sum = 0;
797
798 // First clear both encoders.
799 MOTOR0_ENCODER_FTM->CNT = MOTOR1_ENCODER_FTM->CNT = 0;
800 for (int i = 0; i < kNumberSamples; ++i) {
801 delay(1);
802
803 if (!CountValid(MOTOR0_ENCODER_FTM->CNT)) {
804 printf("Motor 0 moved too much\n");
805 return false;
806 }
807 if (!CountValid(MOTOR1_ENCODER_FTM->CNT)) {
808 printf("Motor 1 moved too much\n");
809 return false;
810 }
811
812 DisableInterrupts disable_interrupts;
813 const SmallInitReadings readings = AdcReadSmallInit(disable_interrupts);
814 motor0_sum += readings.motor0_abs;
815 motor1_sum += readings.motor1_abs;
816 wheel_sum += readings.wheel_abs;
817 }
818
819 *motor0_offset = (motor0_sum + kNumberSamples / 2) / kNumberSamples;
820 *motor1_offset = (motor1_sum + kNumberSamples / 2) / kNumberSamples;
821 *wheel_offset = (wheel_sum + kNumberSamples / 2) / kNumberSamples;
822
823 return true;
824}
825
826} // namespace
827
828extern "C" int main() {
829 // for background about this startup delay, please see these conversations
830 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
831 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
832 delay(400);
833
834 // Set all interrupts to the second-lowest priority to start with.
835 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
836
837 // Now set priorities for all the ones we care about. They only have meaning
838 // relative to each other, which means centralizing them here makes it a lot
839 // more manageable.
840 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
841 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0x3);
842 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0x3);
843 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
844
845 // Set the LED's pin to output mode.
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500846 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800847 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
848
849 // Set up the CAN pins.
850 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
851 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
852
Brian Silvermanff7b3872018-03-10 18:08:30 -0800853 // .1ms filter time.
854 PORTA_DFWR = PORTC_DFWR = PORTD_DFWR = PORTE_DFWR = 6000;
855
Brian Silverman6260c092018-01-14 15:21:36 -0800856 // BTN0
857 PORTC_PCR7 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800858 PORTC_DFER |= 1 << 7;
Brian Silverman6260c092018-01-14 15:21:36 -0800859 // BTN1
860 PORTE_PCR26 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800861 PORTE_DFER |= 1 << 26;
Brian Silverman6260c092018-01-14 15:21:36 -0800862 // BTN2
863 PORTA_PCR14 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800864 PORTA_DFER |= 1 << 14;
Brian Silverman6260c092018-01-14 15:21:36 -0800865 // BTN3
866 PORTD_PCR0 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
Brian Silvermanff7b3872018-03-10 18:08:30 -0800867 PORTD_DFER |= 1 << 0;
868 // BTN4
869 PORTD_PCR7 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
870 PORTD_DFER |= 1 << 7;
871 // BTN5 (only new revision)
872 PORTA_PCR15 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
873 PORTA_DFER |= 1 << 15;
Brian Silverman6260c092018-01-14 15:21:36 -0800874
875 PORTA_PCR5 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
876
Brian Silverman45564a82018-09-02 16:35:22 -0700877 DMA.CR = M_DMA_EMLM;
Brian Silverman6260c092018-01-14 15:21:36 -0800878
Brian Silverman4787a6e2018-10-06 16:00:54 -0700879 PrintingParameters printing_parameters;
880 printing_parameters.dedicated_usb = true;
881 const ::std::unique_ptr<PrintingImplementation> printing =
882 CreatePrinting(printing_parameters);
883 printing->Initialize();
Brian Silverman6260c092018-01-14 15:21:36 -0800884
885 AdcInitSmall();
886 MathInit();
887 delay(100);
888 can_init(2, 3);
889
890 GPIOD_PCOR = 1 << 3;
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500891 PERIPHERAL_BITBAND(GPIOD_PDDR, 3) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800892 PORTD_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(1);
893 GPIOD_PSOR = 1 << 3;
894
895 GPIOC_PCOR = 1 << 4;
Brian Silverman33eb5fa2018-02-11 18:36:19 -0500896 PERIPHERAL_BITBAND(GPIOC_PDDR, 4) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800897 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
898 GPIOC_PSOR = 1 << 4;
899
900 LittleMotorControlsImplementation controls0, controls1;
901
902 delay(100);
903
904 // M0_EA = FTM1_QD_PHB
905 PORTB_PCR19 = PORT_PCR_MUX(6);
906 // M0_EB = FTM1_QD_PHA
907 PORTB_PCR18 = PORT_PCR_MUX(6);
908
909 // M1_EA = FTM1_QD_PHA
910 PORTB_PCR0 = PORT_PCR_MUX(6);
911 // M1_EB = FTM1_QD_PHB
912 PORTB_PCR1 = PORT_PCR_MUX(6);
913
914 // M0_CH0 = FTM3_CH4
915 PORTC_PCR8 = PORT_PCR_DSE | PORT_PCR_MUX(3);
916 // M0_CH1 = FTM3_CH2
917 PORTD_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
918 // M0_CH2 = FTM3_CH6
919 PORTC_PCR10 = PORT_PCR_DSE | PORT_PCR_MUX(3);
920
921 // M1_CH0 = FTM0_CH0
922 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
923 // M1_CH1 = FTM0_CH2
924 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
925 // M1_CH2 = FTM0_CH4
926 PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
927
928 Motor motor0(
929 MOTOR0_PWM_FTM, MOTOR0_ENCODER_FTM, &controls0,
930 {&MOTOR0_PWM_FTM->C4V, &MOTOR0_PWM_FTM->C2V, &MOTOR0_PWM_FTM->C6V});
Brian Silverman4787a6e2018-10-06 16:00:54 -0700931 motor0.set_printing_implementation(printing.get());
Brian Silverman6260c092018-01-14 15:21:36 -0800932 motor0.set_switching_divisor(kSwitchingDivisor);
933 Motor motor1(
934 MOTOR1_PWM_FTM, MOTOR1_ENCODER_FTM, &controls1,
935 {&MOTOR1_PWM_FTM->C0V, &MOTOR1_PWM_FTM->C2V, &MOTOR1_PWM_FTM->C4V});
Brian Silverman4787a6e2018-10-06 16:00:54 -0700936 motor1.set_printing_implementation(printing.get());
Brian Silverman6260c092018-01-14 15:21:36 -0800937 motor1.set_switching_divisor(kSwitchingDivisor);
938 ConfigurePwmFtm(MOTOR0_PWM_FTM);
939 ConfigurePwmFtm(MOTOR1_PWM_FTM);
940 motor0.Init();
941 motor1.Init();
942 global_motor0.store(&motor0, ::std::memory_order_relaxed);
943 global_motor1.store(&motor1, ::std::memory_order_relaxed);
944
945 SIM_SCGC6 |= SIM_SCGC6_PIT;
Brian Silvermanb0de2402018-03-24 03:48:28 -0400946 // Workaround for errata e7914.
947 (void)PIT_MCR;
Brian Silverman6260c092018-01-14 15:21:36 -0800948 PIT_MCR = 0;
Brian Silvermanb0de2402018-03-24 03:48:28 -0400949 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 1000) - 1;
Brian Silverman6260c092018-01-14 15:21:36 -0800950 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
951
952 // Have them both wait for the GTB signal.
953 FTM0->CONF = FTM3->CONF =
954 FTM_CONF_GTBEEN | FTM_CONF_NUMTOF(kSwitchingDivisor - 1);
955 // Make FTM3's period half of what it should be so we can get it a half-cycle
956 // out of phase.
957 const uint32_t original_mod = FTM3->MOD;
958 FTM3->MOD = ((original_mod + 1) / 2) - 1;
959 FTM3->SYNC |= FTM_SYNC_SWSYNC;
960
961 // Output triggers to things like the PDBs on initialization.
962 FTM0_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;
963 FTM3_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;
964 // Don't let any memory accesses sneak past here, because we actually
965 // need everything to be starting up.
966 __asm__("" ::: "memory");
967
968 // Give everything a chance to get going.
969 delay(100);
970
971 printf("BSS: %p-%p\n", __bss_ram_start__, __bss_ram_end__);
972 printf("data: %p-%p\n", __data_ram_start__, __data_ram_end__);
973 printf("heap start: %p\n", __heap_start__);
974 printf("stack start: %p\n", __stack_end__);
975
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700976 trigger_cogging_torque.store(processor_index == 0 ? kTriggerCoggingTorque0
Austin Schuh745e4ed2019-04-07 15:50:16 -0700977 : kTriggerCoggingTorque1);
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700978 wheel_cogging_torque.store(processor_index == 0 ? kWheelCoggingTorque0
Austin Schuh745e4ed2019-04-07 15:50:16 -0700979 : kWheelCoggingTorque1);
980
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700981 printf("Zeroing motors for %d:%x\n", static_cast<int>(processor_index),
Austin Schuh80b99932019-04-07 14:04:41 -0700982 (unsigned int)ProcessorIdentifier());
Brian Silverman6260c092018-01-14 15:21:36 -0800983 uint16_t motor0_offset, motor1_offset, wheel_offset;
984 while (!ZeroMotors(&motor0_offset, &motor1_offset, &wheel_offset)) {
985 }
986 printf("Done zeroing\n");
987
988 const float motor0_offset_scaled = -analog_ratio(motor0_offset);
989 const float motor1_offset_scaled = analog_ratio(motor1_offset);
990 // Good for the initial trigger.
991 {
Austin Schuh4a8d4922019-04-07 15:31:30 -0700992 // Calibrate winding phase error here.
Austin Schuhc1b2ca12019-04-13 00:04:10 -0700993 const float kZeroOffset0 = processor_index == 1 ? 0.275f : 0.0f;
Brian Silverman6260c092018-01-14 15:21:36 -0800994 const int motor0_starting_point = static_cast<int>(
995 (motor0_offset_scaled + (kZeroOffset0 / 7.0f)) * 4096.0f);
996 printf("Motor 0 starting at %d\n", motor0_starting_point);
997 motor0.set_encoder_calibration_offset(motor0_starting_point);
998 motor0.set_encoder_multiplier(-1);
999
Austin Schuh4a8d4922019-04-07 15:31:30 -07001000 // Calibrate output coordinate neutral here.
1001 motor0.set_encoder_offset(
1002 motor0.encoder_offset() +
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001003 (processor_index == 1 ? (-3096 - 430 - 30 - 6) : (-1978)));
Austin Schuh4a8d4922019-04-07 15:31:30 -07001004
1005 while (true) {
1006 const uint32_t encoder =
1007 global_motor0.load(::std::memory_order_relaxed)->wrapped_encoder();
1008 const int32_t absolute_encoder =
1009 global_motor0.load(::std::memory_order_relaxed)
1010 ->absolute_encoder(encoder);
1011
1012 if (absolute_encoder > 2047) {
1013 motor0.set_encoder_offset(motor0.encoder_offset() - 4096);
1014 } else if (absolute_encoder < -2047) {
1015 motor0.set_encoder_offset(motor0.encoder_offset() + 4096);
1016 } else {
1017 break;
1018 }
1019 }
Brian Silverman6260c092018-01-14 15:21:36 -08001020
1021 uint32_t new_encoder = motor0.wrapped_encoder();
1022 int32_t absolute_encoder = motor0.absolute_encoder(new_encoder);
1023 printf("Motor 0 encoder %d absolute %d\n", static_cast<int>(new_encoder),
1024 static_cast<int>(absolute_encoder));
1025 }
1026
1027 {
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001028 const float kZeroOffset1 = processor_index == 1 ? 0.420f : 0.0f;
1029
Brian Silverman6260c092018-01-14 15:21:36 -08001030 const int motor1_starting_point = static_cast<int>(
1031 (motor1_offset_scaled + (kZeroOffset1 / 7.0f)) * 4096.0f);
1032 printf("Motor 1 starting at %d\n", motor1_starting_point);
1033 motor1.set_encoder_calibration_offset(motor1_starting_point);
1034 motor1.set_encoder_multiplier(-1);
1035
1036 float wheel_position = absolute_wheel(analog_ratio(wheel_offset));
1037
1038 uint32_t encoder = motor1.wrapped_encoder();
1039
1040 printf("Wheel starting at %d, encoder %" PRId32 "\n",
1041 static_cast<int>(wheel_position * 1000.0f), encoder);
1042
1043 constexpr float kWheelGearRatio = (1.25f + 0.02f) / 0.35f;
Austin Schuh4a8d4922019-04-07 15:31:30 -07001044 const float kWrappedWheelAtZero =
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001045 processor_index == 1 ? (0.934630859375f) : (1809.f / 4096.f);
Brian Silverman6260c092018-01-14 15:21:36 -08001046
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001047 // If we are pistol grip 0, we are the mechanical pistol grip and can't
1048 // wrap. People are very very likely to zero it at the zero position too.
Brian Silverman6260c092018-01-14 15:21:36 -08001049 const int encoder_wraps =
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001050 processor_index == 0 ? 0
1051 : static_cast<int>(lround(
1052 wheel_position * kWheelGearRatio -
1053 (encoder / 4096.f) + kWrappedWheelAtZero));
Brian Silverman6260c092018-01-14 15:21:36 -08001054
1055 printf("Wraps: %d\n", encoder_wraps);
1056 motor1.set_encoder_offset(4096 * encoder_wraps + motor1.encoder_offset() -
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001057 static_cast<int>(kWrappedWheelAtZero * 4096.0f));
Brian Silverman6260c092018-01-14 15:21:36 -08001058 printf("Wheel encoder now at %d\n",
Austin Schuhc1b2ca12019-04-13 00:04:10 -07001059 static_cast<int>(motor1.absolute_encoder(motor1.wrapped_encoder())));
Brian Silverman6260c092018-01-14 15:21:36 -08001060 }
1061
1062 // Turn an LED on for Austin.
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001063 PERIPHERAL_BITBAND(GPIOC_PDDR, 6) = 1;
Brian Silverman6260c092018-01-14 15:21:36 -08001064 GPIOC_PCOR = 1 << 6;
1065 PORTC_PCR6 = PORT_PCR_DSE | PORT_PCR_MUX(1);
1066
1067 // M0_THW
1068 PORTC_PCR11 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1069 // M0_FAULT
1070 PORTD_PCR6 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1071 // M1_THW
1072 PORTC_PCR2 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1073 // M1_FAULT
1074 PORTD_PCR5 = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
1075
1076 motor0.Start();
1077 motor1.Start();
1078 {
1079 // We rely on various things happening faster than the timer period, so make
1080 // sure slow USB or whatever interrupts don't prevent that.
1081 DisableInterrupts disable_interrupts;
1082
1083 // First clear the overflow flag.
1084 FTM3->SC &= ~FTM_SC_TOF;
1085
1086 // Now poke the GTB to actually start both timers.
1087 FTM0->CONF = FTM_CONF_GTBEEN | FTM_CONF_GTBEOUT |
1088 FTM_CONF_NUMTOF(kSwitchingDivisor - 1);
1089
1090 // Wait for it to overflow twice. For some reason, just once doesn't work.
1091 while (!(FTM3->SC & FTM_SC_TOF)) {
1092 }
1093 FTM3->SC &= ~FTM_SC_TOF;
1094 while (!(FTM3->SC & FTM_SC_TOF)) {
1095 }
1096
1097 // Now put the MOD value back to what it was.
1098 FTM3->MOD = original_mod;
1099 FTM3->PWMLOAD = FTM_PWMLOAD_LDOK;
1100
1101 // And then clear the overflow flags before enabling interrupts so we
1102 // actually wait until the next overflow to start doing interrupts.
1103 FTM0->SC &= ~FTM_SC_TOF;
1104 FTM3->SC &= ~FTM_SC_TOF;
1105 NVIC_ENABLE_IRQ(IRQ_FTM0);
1106 NVIC_ENABLE_IRQ(IRQ_FTM3);
1107 }
1108 global_trigger_plant.store(
1109 new StateFeedbackPlant<3, 1, 1, float>(MakeIntegralHapticTriggerPlant()));
1110 global_trigger_observer.store(new StateFeedbackObserver<3, 1, 1, float>(
1111 MakeIntegralHapticTriggerObserver()));
1112 global_trigger_observer.load(::std::memory_order_relaxed)
1113 ->Reset(global_trigger_plant.load(::std::memory_order_relaxed));
1114
1115 global_wheel_plant.store(
1116 new StateFeedbackPlant<3, 1, 1, float>(MakeIntegralHapticWheelPlant()));
1117 global_wheel_observer.store(new StateFeedbackObserver<3, 1, 1, float>(
1118 MakeIntegralHapticWheelObserver()));
1119 global_wheel_observer.load(::std::memory_order_relaxed)
1120 ->Reset(global_wheel_plant.load(::std::memory_order_relaxed));
1121
1122 delay(1000);
1123
1124 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
1125
1126 // TODO(Brian): Use SLEEPONEXIT to reduce interrupt latency?
1127 while (true) {
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001128 if (!PERIPHERAL_BITBAND(GPIOC_PDIR, 11)) {
1129 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001130 printf("M0_THW\n");
1131 }
1132 GPIOC_PSOR = 1 << 5;
1133 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001134 if (!PERIPHERAL_BITBAND(GPIOD_PDIR, 6)) {
1135 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001136 printf("M0_FAULT\n");
1137 }
1138 GPIOC_PSOR = 1 << 5;
1139 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001140 if (!PERIPHERAL_BITBAND(GPIOC_PDIR, 2)) {
1141 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001142 printf("M1_THW\n");
1143 }
1144 GPIOC_PSOR = 1 << 5;
1145 }
Brian Silverman33eb5fa2018-02-11 18:36:19 -05001146 if (!PERIPHERAL_BITBAND(GPIOD_PDIR, 5)) {
1147 if (!PERIPHERAL_BITBAND(GPIOC_PDOR, 5)) {
Brian Silverman6260c092018-01-14 15:21:36 -08001148 printf("M1_FAULT\n");
1149 }
1150 GPIOC_PSOR = 1 << 5;
1151 }
1152 }
1153
1154 return 0;
1155}
1156
Brian Silvermana96c1a42018-05-12 12:11:31 -07001157} // namespace motors
Brian Silverman6260c092018-01-14 15:21:36 -08001158} // namespace frc971