blob: 86539bda49554bd2c0654f151f43fe707aef3484 [file] [log] [blame]
Brian Silvermanb6570e32019-03-21 20:58:32 -07001// This file has the main for the Teensy on the simple receiver board v2 in the
2// new robot.
3
4#include <inttypes.h>
5#include <stdio.h>
6#include <atomic>
7#include <chrono>
8#include <cmath>
9
10#include "frc971/control_loops/drivetrain/polydrivetrain.h"
11#include "motors/core/kinetis.h"
12#include "motors/core/time.h"
13#include "motors/peripheral/can.h"
14#include "motors/peripheral/configuration.h"
15#include "motors/print/print.h"
16#include "motors/seems_reasonable/drivetrain_dog_motor_plant.h"
17#include "motors/seems_reasonable/polydrivetrain_dog_motor_plant.h"
18#include "motors/util.h"
19
20namespace frc971 {
21namespace motors {
22namespace {
23
24using ::frc971::control_loops::drivetrain::DrivetrainConfig;
25using ::frc971::control_loops::drivetrain::PolyDrivetrain;
26using ::frc971::constants::ShifterHallEffect;
27using ::frc971::control_loops::DrivetrainQueue_Goal;
28using ::frc971::control_loops::DrivetrainQueue_Output;
29
30namespace chrono = ::std::chrono;
31
32const ShifterHallEffect kThreeStateDriveShifter{0.0, 0.0, 0.25, 0.75};
33
34const DrivetrainConfig<float> &GetDrivetrainConfig() {
35 static DrivetrainConfig<float> kDrivetrainConfig{
36 ::frc971::control_loops::drivetrain::ShifterType::NO_SHIFTER,
37 ::frc971::control_loops::drivetrain::LoopType::OPEN_LOOP,
38 ::frc971::control_loops::drivetrain::GyroType::SPARTAN_GYRO,
39 ::frc971::control_loops::drivetrain::IMUType::IMU_X,
40
41 ::motors::seems_reasonable::MakeDrivetrainLoop,
42 ::motors::seems_reasonable::MakeVelocityDrivetrainLoop,
43 ::std::function<StateFeedbackLoop<7, 2, 4, float>()>(),
44
45 chrono::duration_cast<chrono::nanoseconds>(
46 chrono::duration<float>(::motors::seems_reasonable::kDt)),
47 ::motors::seems_reasonable::kRobotRadius,
48 ::motors::seems_reasonable::kWheelRadius, ::motors::seems_reasonable::kV,
49
50 ::motors::seems_reasonable::kHighGearRatio,
51 ::motors::seems_reasonable::kLowGearRatio, ::motors::seems_reasonable::kJ,
52 ::motors::seems_reasonable::kMass, kThreeStateDriveShifter,
53 kThreeStateDriveShifter, true /* default_high_gear */,
54 0 /* down_offset */, 0.8 /* wheel_non_linearity */,
55 1.2 /* quickturn_wheel_multiplier */, 1.5 /* wheel_multiplier */,
56 };
57
58 return kDrivetrainConfig;
59};
60
61
62::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
63
64// Last width we received on each channel.
65uint16_t pwm_input_widths[6];
66// When we received a pulse on each channel in milliseconds.
67uint32_t pwm_input_times[6];
68
69constexpr int kChannelTimeout = 100;
70
71bool lost_channel(int channel) {
72 DisableInterrupts disable_interrupts;
73 if (time_after(millis(),
74 time_add(pwm_input_times[channel], kChannelTimeout))) {
75 return true;
76 }
77 return false;
78}
79
80// Returns the most recently captured value for the specified input channel
81// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
82float convert_input_width(int channel) {
83 uint16_t width;
84 {
85 DisableInterrupts disable_interrupts;
86 if (time_after(millis(),
87 time_add(pwm_input_times[channel], kChannelTimeout))) {
88 return 0;
89 }
90
91 width = pwm_input_widths[channel];
92 }
93
94 // Values measured with a channel mapped to a button.
95 static constexpr uint16_t kMinWidth = 4133;
96 static constexpr uint16_t kMaxWidth = 7177;
97 if (width < kMinWidth) {
98 width = kMinWidth;
99 } else if (width > kMaxWidth) {
100 width = kMaxWidth;
101 }
102 return (static_cast<float>(2 * (width - kMinWidth)) /
103 static_cast<float>(kMaxWidth - kMinWidth)) -
104 1.0f;
105}
106
107// Sends a SET_RPM command to the specified VESC.
108// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
109// bandwidth.
110void vesc_set_rpm(int vesc_id, float rpm) {
111 const int32_t rpm_int = rpm;
112 uint32_t id = CAN_EFF_FLAG;
113 id |= vesc_id;
114 id |= (0x03 /* SET_RPM */) << 8;
115 uint8_t data[4] = {
116 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
117 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
118 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
119 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
120 };
121 can_send(id, data, sizeof(data), 2 + vesc_id);
122}
123
124// Sends a SET_CURRENT command to the specified VESC.
125// current is in amps.
126// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
127// bandwidth.
128void vesc_set_current(int vesc_id, float current) {
129 constexpr float kMaxCurrent = 80.0f;
130 const int32_t current_int =
131 ::std::max(-kMaxCurrent, ::std::min(kMaxCurrent, current)) * 1000.0f;
132 uint32_t id = CAN_EFF_FLAG;
133 id |= vesc_id;
134 id |= (0x01 /* SET_CURRENT */) << 8;
135 uint8_t data[4] = {
136 static_cast<uint8_t>((current_int >> 24) & 0xFF),
137 static_cast<uint8_t>((current_int >> 16) & 0xFF),
138 static_cast<uint8_t>((current_int >> 8) & 0xFF),
139 static_cast<uint8_t>((current_int >> 0) & 0xFF),
140 };
141 can_send(id, data, sizeof(data), 2 + vesc_id);
142}
143
144// Sends a SET_DUTY command to the specified VESC.
145// duty is from -1 to 1.
146// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
147// bandwidth.
148void vesc_set_duty(int vesc_id, float duty) {
149 constexpr int32_t kMaxDuty = 99999;
150 const int32_t duty_int = ::std::max(
151 -kMaxDuty, ::std::min(kMaxDuty, static_cast<int32_t>(duty * 100000.0f)));
152 uint32_t id = CAN_EFF_FLAG;
153 id |= vesc_id;
154 id |= (0x00 /* SET_DUTY */) << 8;
155 uint8_t data[4] = {
156 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
157 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
158 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
159 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
160 };
161 can_send(id, data, sizeof(data), 2 + vesc_id);
162}
163
164// TODO(Brian): Move these two test functions somewhere else.
165__attribute__((unused)) void DoVescTest() {
166 uint32_t time = micros();
167 while (true) {
168 for (int i = 0; i < 6; ++i) {
169 const uint32_t end = time_add(time, 500000);
170 while (true) {
171 const bool done = time_after(micros(), end);
172 float current;
173 if (done) {
174 current = -6;
175 } else {
176 current = 6;
177 }
178 vesc_set_current(i, current);
179 if (done) {
180 break;
181 }
182 delay(5);
183 }
184 time = end;
185 }
186 }
187}
188
189__attribute__((unused)) void DoReceiverTest2() {
190 static constexpr float kMaxRpm = 10000.0f;
191 while (true) {
192 const bool flip = convert_input_width(2) > 0;
193
194 {
195 const float value = convert_input_width(0);
196
197 {
198 float rpm = ::std::min(0.0f, value) * kMaxRpm;
199 if (flip) {
200 rpm *= -1.0f;
201 }
202 vesc_set_rpm(0, rpm);
203 }
204
205 {
206 float rpm = ::std::max(0.0f, value) * kMaxRpm;
207 if (flip) {
208 rpm *= -1.0f;
209 }
210 vesc_set_rpm(1, rpm);
211 }
212 }
213
214 {
215 const float value = convert_input_width(1);
216
217 {
218 float rpm = ::std::min(0.0f, value) * kMaxRpm;
219 if (flip) {
220 rpm *= -1.0f;
221 }
222 vesc_set_rpm(2, rpm);
223 }
224
225 {
226 float rpm = ::std::max(0.0f, value) * kMaxRpm;
227 if (flip) {
228 rpm *= -1.0f;
229 }
230 vesc_set_rpm(3, rpm);
231 }
232 }
233
234 {
235 const float value = convert_input_width(4);
236
237 {
238 float rpm = ::std::min(0.0f, value) * kMaxRpm;
239 if (flip) {
240 rpm *= -1.0f;
241 }
242 vesc_set_rpm(4, rpm);
243 }
244
245 {
246 float rpm = ::std::max(0.0f, value) * kMaxRpm;
247 if (flip) {
248 rpm *= -1.0f;
249 }
250 vesc_set_rpm(5, rpm);
251 }
252 }
253 // Give the CAN frames a chance to go out.
254 delay(5);
255 }
256}
257
258void SetupPwmFtm(BigFTM *ftm) {
259 ftm->MODE = FTM_MODE_WPDIS;
260 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
261 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
262
263 // Can't change MOD according to the reference manual ("The Dual Edge Capture
264 // mode must be used with ... the FTM counter in Free running counter.").
265 ftm->MOD = 0xFFFF;
266
267 // Capturing rising edge.
268 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
269 // Capturing falling edge.
270 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
271
272 // Capturing rising edge.
273 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
274 // Capturing falling edge.
275 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
276
277 // Capturing rising edge.
278 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
279 // Capturing falling edge.
280 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
281
282 // Capturing rising edge.
283 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
284 // Capturing falling edge.
285 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
286
287 (void)ftm->STATUS;
288 ftm->STATUS = 0x00;
289
290 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
291 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
292 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
293 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
294
295 // 34.95ms max period before it starts wrapping and being weird.
296 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
297 FTM_SC_PS(4) /* Prescaler=32 */;
298
299 ftm->MODE &= ~FTM_MODE_WPDIS;
300}
301
302extern "C" void ftm0_isr() {
303 while (true) {
304 const uint32_t status = FTM0->STATUS;
305 if (status == 0) {
306 return;
307 }
308
309 if (status & (1 << 1)) {
310 const uint32_t start = FTM0->C0V;
311 const uint32_t end = FTM0->C1V;
312 pwm_input_widths[1] = (end - start) & 0xFFFF;
313 pwm_input_times[1] = millis();
314 }
315 if (status & (1 << 5)) {
316 const uint32_t start = FTM0->C4V;
317 const uint32_t end = FTM0->C5V;
318 pwm_input_widths[3] = (end - start) & 0xFFFF;
319 pwm_input_times[3] = millis();
320 }
321 if (status & (1 << 3)) {
322 const uint32_t start = FTM0->C2V;
323 const uint32_t end = FTM0->C3V;
324 pwm_input_widths[4] = (end - start) & 0xFFFF;
325 pwm_input_times[4] = millis();
326 }
327
328 FTM0->STATUS = 0;
329 }
330}
331
332extern "C" void ftm3_isr() {
333 while (true) {
334 const uint32_t status = FTM3->STATUS;
335 if (status == 0) {
336 return;
337 }
338
339 FTM3->STATUS = 0;
340 }
341}
342
343extern "C" void pit3_isr() {
344 PIT_TFLG3 = 1;
345 PolyDrivetrain<float> *polydrivetrain =
346 global_polydrivetrain.load(::std::memory_order_acquire);
347
348 const bool lost_drive_channel = lost_channel(3) || lost_channel(1);
349
350 if (false) {
351 static int count = 0;
352 if (++count == 50) {
353 count = 0;
354 printf("0: %d 1: %d\n", (int)pwm_input_widths[3],
355 (int)pwm_input_widths[1]);
356 }
357 }
358
359 if (polydrivetrain != nullptr) {
360 DrivetrainQueue_Goal goal;
361 goal.control_loop_driving = false;
362 if (lost_drive_channel) {
363 goal.throttle = 0.0f;
364 goal.wheel = 0.0f;
365 } else {
366 goal.throttle = convert_input_width(1);
367 goal.wheel = -convert_input_width(3);
368 }
369 goal.quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
370
371 if (false) {
372 static int count = 0;
373 if (++count == 50) {
374 count = 0;
375 printf("throttle: %d wheel: %d\n", (int)(goal.throttle * 100),
376 (int)(goal.wheel * 100));
377 }
378 }
379
380 DrivetrainQueue_Output output;
381
382 polydrivetrain->SetGoal(goal);
383 polydrivetrain->Update(12.0f);
384 polydrivetrain->SetOutput(&output);
385
386 if (false) {
387 static int count = 0;
388 if (++count == 50) {
389 count = 0;
390 printf("l: %d r: %d\n", (int)(output.left_voltage * 100),
391 (int)(output.right_voltage * 100));
392 }
393 }
394 vesc_set_duty(0, -output.left_voltage / 12.0f);
395 vesc_set_duty(1, -output.left_voltage / 12.0f);
396
Brian Silvermandcb6ad12019-04-08 18:13:16 -0700397 vesc_set_duty(2, output.right_voltage / 12.0f);
398 vesc_set_duty(3, output.right_voltage / 12.0f);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700399 }
400}
401
402} // namespace
403
404extern "C" {
405
406void *__stack_chk_guard = (void *)0x67111971;
407void __stack_chk_fail(void);
408
409} // extern "C"
410
411extern "C" int main(void) {
412 // for background about this startup delay, please see these conversations
413 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
414 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
415 delay(400);
416
417 // Set all interrupts to the second-lowest priority to start with.
418 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
419
420 // Now set priorities for all the ones we care about. They only have meaning
421 // relative to each other, which means centralizing them here makes it a lot
422 // more manageable.
423 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
424 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
425 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
426 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
427
428 // Builtin LED.
429 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
430 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
431 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
432
433 // Set up the CAN pins.
434 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
435 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
436
437 // PWM_IN0
438 // FTM0_CH1 (doesn't work)
439 // PORTC_PCR2 = PORT_PCR_MUX(4);
440
441 // PWM_IN1
442 // FTM0_CH0
443 PORTC_PCR1 = PORT_PCR_MUX(4);
444
445 // PWM_IN2
446 // FTM0_CH5 (doesn't work)
447 // PORTD_PCR5 = PORT_PCR_MUX(4);
448
449 // PWM_IN3
450 // FTM0_CH4
451 PORTD_PCR4 = PORT_PCR_MUX(4);
452
453 // PWM_IN4
454 // FTM0_CH2
455 PORTC_PCR3 = PORT_PCR_MUX(4);
456
457 // PWM_IN5
458 // FTM0_CH3 (doesn't work)
459 // PORTC_PCR4 = PORT_PCR_MUX(4);
460
461 delay(100);
462
463 PrintingParameters printing_parameters;
464 printing_parameters.dedicated_usb = true;
465 const ::std::unique_ptr<PrintingImplementation> printing =
466 CreatePrinting(printing_parameters);
467 printing->Initialize();
468
469 SIM_SCGC6 |= SIM_SCGC6_PIT;
470 // Workaround for errata e7914.
471 (void)PIT_MCR;
472 PIT_MCR = 0;
473 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
474 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
475
476 can_init(0, 1);
477 SetupPwmFtm(FTM0);
478 SetupPwmFtm(FTM3);
479
480 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
481 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
482
483 // Leave the LED on for a bit longer.
484 delay(300);
485 printf("Done starting up\n");
486
487 // Done starting up, now turn the LED off.
488 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
489
490 NVIC_ENABLE_IRQ(IRQ_FTM0);
491 NVIC_ENABLE_IRQ(IRQ_FTM3);
492 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
493 printf("Done starting up2\n");
494
495 while (true) {
496 }
497
498 return 0;
499}
500
501void __stack_chk_fail(void) {
502 while (true) {
503 GPIOC_PSOR = (1 << 5);
504 printf("Stack corruption detected\n");
505 delay(1000);
506 GPIOC_PCOR = (1 << 5);
507 delay(1000);
508 }
509}
510
511} // namespace motors
512} // namespace frc971