blob: c6d70b80fbe8ccaa047bca8a5b16fb29ee873004 [file] [log] [blame]
Brian Silverman54dd2fe2018-03-16 23:44:31 -07001// This file has the main for the Teensy on the simple receiver board.
2
3#include <inttypes.h>
4#include <stdio.h>
5#include <atomic>
6#include <cmath>
7
Brian Silverman54dd2fe2018-03-16 23:44:31 -07008#include "motors/core/kinetis.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -04009#include "motors/core/time.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070010#include "motors/peripheral/adc.h"
11#include "motors/peripheral/can.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040012#include "motors/peripheral/configuration.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070013#include "motors/usb/cdc.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040014#include "motors/usb/usb.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070015#include "motors/util.h"
Austin Schuhbcce26a2018-03-26 23:41:24 -070016#include "frc971/control_loops/drivetrain/polydrivetrain.h"
17#include "motors/seems_reasonable/drivetrain_dog_motor_plant.h"
18#include "motors/seems_reasonable/polydrivetrain_dog_motor_plant.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070019
20namespace frc971 {
21namespace motors {
22namespace {
23
Austin Schuhbcce26a2018-03-26 23:41:24 -070024using ::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
30const ShifterHallEffect kThreeStateDriveShifter{0.0, 0.0, 0.25, 0.75};
31
32const DrivetrainConfig<float> &GetDrivetrainConfig() {
33 static DrivetrainConfig<float> kDrivetrainConfig{
34 ::frc971::control_loops::drivetrain::ShifterType::NO_SHIFTER,
35 ::frc971::control_loops::drivetrain::LoopType::OPEN_LOOP,
36 ::frc971::control_loops::drivetrain::GyroType::SPARTAN_GYRO,
37 ::frc971::control_loops::drivetrain::IMUType::IMU_X,
38
39 ::motors::seems_reasonable::MakeDrivetrainLoop,
40 ::motors::seems_reasonable::MakeVelocityDrivetrainLoop,
41 ::std::function<StateFeedbackLoop<7, 2, 4, float>()>(),
42
43 ::motors::seems_reasonable::kDt, ::motors::seems_reasonable::kRobotRadius,
44 ::motors::seems_reasonable::kWheelRadius, ::motors::seems_reasonable::kV,
45
46 ::motors::seems_reasonable::kHighGearRatio,
47 ::motors::seems_reasonable::kLowGearRatio, kThreeStateDriveShifter,
48 kThreeStateDriveShifter, true /* default_high_gear */,
49 0 /* down_offset if using constants use
50 constants::GetValues().down_error */, 0.8 /* wheel_non_linearity */,
51 1.2 /* quickturn_wheel_multiplier */, 1.5 /* wheel_multiplier */,
52 };
53
54 return kDrivetrainConfig;
55};
56
57
Brian Silverman54dd2fe2018-03-16 23:44:31 -070058::std::atomic<teensy::AcmTty *> global_stdout{nullptr};
59
Austin Schuhbcce26a2018-03-26 23:41:24 -070060::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
61
Brian Silvermana3a172b2018-03-24 03:53:32 -040062// Last width we received on each channel.
63uint16_t pwm_input_widths[5];
64// When we received a pulse on each channel in milliseconds.
65uint32_t pwm_input_times[5];
66
Austin Schuhbcce26a2018-03-26 23:41:24 -070067constexpr int kChannelTimeout = 100;
68
69bool lost_channel(int channel) {
70 DisableInterrupts disable_interrupts;
71 if (time_after(millis(),
72 time_add(pwm_input_times[channel], kChannelTimeout))) {
73 return true;
74 }
75 return false;
76}
77
Brian Silvermana3a172b2018-03-24 03:53:32 -040078// Returns the most recently captured value for the specified input channel
79// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
80float convert_input_width(int channel) {
81 uint16_t width;
82 {
83 DisableInterrupts disable_interrupts;
Austin Schuhbcce26a2018-03-26 23:41:24 -070084 if (time_after(millis(),
85 time_add(pwm_input_times[channel], kChannelTimeout))) {
Brian Silvermana3a172b2018-03-24 03:53:32 -040086 return 0;
87 }
88
89 width = pwm_input_widths[channel];
90 }
91
92 // Values measured with a channel mapped to a button.
93 static constexpr uint16_t kMinWidth = 4133;
94 static constexpr uint16_t kMaxWidth = 7177;
95 if (width < kMinWidth) {
96 width = kMinWidth;
97 } else if (width > kMaxWidth) {
98 width = kMaxWidth;
99 }
100 return (static_cast<float>(2 * (width - kMinWidth)) /
101 static_cast<float>(kMaxWidth - kMinWidth)) -
102 1.0f;
103}
104
105// Sends a SET_RPM command to the specified VESC.
106// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
107// bandwidth.
108void vesc_set_rpm(int vesc_id, float rpm) {
109 const int32_t rpm_int = rpm;
110 uint32_t id = CAN_EFF_FLAG;
111 id |= vesc_id;
112 id |= (0x03 /* SET_RPM */) << 8;
113 uint8_t data[4] = {
114 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
115 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
116 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
117 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
118 };
119 can_send(id, data, sizeof(data), 2 + vesc_id);
120}
121
122// Sends a SET_CURRENT command to the specified VESC.
123// current is in amps.
124// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
125// bandwidth.
126void vesc_set_current(int vesc_id, float current) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700127 constexpr float kMaxCurrent = 80.0f;
128 const int32_t current_int =
129 ::std::max(-kMaxCurrent, ::std::min(kMaxCurrent, current)) * 1000.0f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400130 uint32_t id = CAN_EFF_FLAG;
131 id |= vesc_id;
132 id |= (0x01 /* SET_CURRENT */) << 8;
133 uint8_t data[4] = {
134 static_cast<uint8_t>((current_int >> 24) & 0xFF),
135 static_cast<uint8_t>((current_int >> 16) & 0xFF),
136 static_cast<uint8_t>((current_int >> 8) & 0xFF),
137 static_cast<uint8_t>((current_int >> 0) & 0xFF),
138 };
139 can_send(id, data, sizeof(data), 2 + vesc_id);
140}
141
Brian Silverman4d1e5272018-03-26 03:18:42 -0400142// Sends a SET_DUTY command to the specified VESC.
143// duty is from -1 to 1.
144// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
145// bandwidth.
146void vesc_set_duty(int vesc_id, float duty) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700147 constexpr int32_t kMaxDuty = 99999;
148 const int32_t duty_int = ::std::max(
149 -kMaxDuty, ::std::min(kMaxDuty, static_cast<int32_t>(duty * 100000.0f)));
Brian Silverman4d1e5272018-03-26 03:18:42 -0400150 uint32_t id = CAN_EFF_FLAG;
151 id |= vesc_id;
152 id |= (0x00 /* SET_DUTY */) << 8;
153 uint8_t data[4] = {
154 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
155 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
156 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
157 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
158 };
159 can_send(id, data, sizeof(data), 2 + vesc_id);
160}
161
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700162void DoVescTest() {
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700163 uint32_t time = micros();
164 while (true) {
165 for (int i = 0; i < 6; ++i) {
166 const uint32_t end = time_add(time, 500000);
167 while (true) {
168 const bool done = time_after(micros(), end);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700169 float current;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700170 if (done) {
171 current = -6;
172 } else {
173 current = 6;
174 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400175 vesc_set_current(i, current);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700176 if (done) {
177 break;
178 }
179 delay(5);
180 }
181 time = end;
182 }
183 }
184}
185
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700186void DoReceiverTest2() {
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700187 static constexpr float kMaxRpm = 10000.0f;
188 while (true) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400189 const bool flip = convert_input_width(2) > 0;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700190
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700191 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400192 const float value = convert_input_width(0);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700193
194 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400195 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700196 if (flip) {
197 rpm *= -1.0f;
198 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400199 vesc_set_rpm(0, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700200 }
201
202 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400203 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700204 if (flip) {
205 rpm *= -1.0f;
206 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400207 vesc_set_rpm(1, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700208 }
209 }
210
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700211 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400212 const float value = convert_input_width(1);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700213
214 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400215 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700216 if (flip) {
217 rpm *= -1.0f;
218 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400219 vesc_set_rpm(2, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700220 }
221
222 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400223 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700224 if (flip) {
225 rpm *= -1.0f;
226 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400227 vesc_set_rpm(3, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700228 }
229 }
230
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700231 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400232 const float value = convert_input_width(4);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700233
234 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400235 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700236 if (flip) {
237 rpm *= -1.0f;
238 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400239 vesc_set_rpm(4, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700240 }
241
242 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400243 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700244 if (flip) {
245 rpm *= -1.0f;
246 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400247 vesc_set_rpm(5, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700248 }
249 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400250 // Give the CAN frames a chance to go out.
251 delay(5);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700252 }
253}
254
255void SetupPwmFtm(BigFTM *ftm) {
256 ftm->MODE = FTM_MODE_WPDIS;
257 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
258 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
259
260 // Can't change MOD according to the reference manual ("The Dual Edge Capture
261 // mode must be used with ... the FTM counter in Free running counter.").
262 ftm->MOD = 0xFFFF;
263
264 // Capturing rising edge.
265 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
266 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400267 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700268
269 // Capturing rising edge.
270 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
271 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400272 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700273
274 // Capturing rising edge.
275 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
276 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400277 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700278
279 // Capturing rising edge.
280 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
281 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400282 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700283
Brian Silvermana3a172b2018-03-24 03:53:32 -0400284 (void)ftm->STATUS;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700285 ftm->STATUS = 0x00;
286
287 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
288 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
289 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
290 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
291
292 // 34.95ms max period before it starts wrapping and being weird.
293 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
294 FTM_SC_PS(4) /* Prescaler=32 */;
295
296 ftm->MODE &= ~FTM_MODE_WPDIS;
297}
298
Brian Silvermana3a172b2018-03-24 03:53:32 -0400299extern "C" void ftm0_isr() {
300 while (true) {
301 const uint32_t status = FTM0->STATUS;
302 if (status == 0) {
303 return;
304 }
305
306 if (status & (1 << 1)) {
307 const uint32_t start = FTM0->C0V;
308 const uint32_t end = FTM0->C1V;
309 pwm_input_widths[0] = (end - start) & 0xFFFF;
310 pwm_input_times[0] = millis();
311 }
312 if (status & (1 << 7)) {
313 const uint32_t start = FTM0->C6V;
314 const uint32_t end = FTM0->C7V;
315 pwm_input_widths[1] = (end - start) & 0xFFFF;
316 pwm_input_times[1] = millis();
317 }
318 if (status & (1 << 5)) {
319 const uint32_t start = FTM0->C4V;
320 const uint32_t end = FTM0->C5V;
321 pwm_input_widths[2] = (end - start) & 0xFFFF;
322 pwm_input_times[2] = millis();
323 }
324 if (status & (1 << 3)) {
325 const uint32_t start = FTM0->C2V;
326 const uint32_t end = FTM0->C3V;
327 pwm_input_widths[4] = (end - start) & 0xFFFF;
328 pwm_input_times[4] = millis();
329 }
330
331 FTM0->STATUS = 0;
332 }
333}
334
335extern "C" void ftm3_isr() {
336 while (true) {
337 const uint32_t status = FTM3->STATUS;
338 if (status == 0) {
339 return;
340 }
341
342 if (status & (1 << 3)) {
343 const uint32_t start = FTM3->C2V;
344 const uint32_t end = FTM3->C3V;
345 pwm_input_widths[3] = (end - start) & 0xFFFF;
346 pwm_input_times[3] = millis();
347 }
348
349 FTM3->STATUS = 0;
350 }
351}
352
353float ConvertEncoderChannel(uint16_t reading) {
354 // Theoretical values based on the datasheet are 931 and 2917.
355 // With these values, the magnitude ranges from 0.99-1.03, which works fine
356 // (the encoder's output appears to get less accurate in one quadrant for some
357 // reason, hence the variation).
358 static constexpr uint16_t kMin = 802, kMax = 3088;
359 if (reading < kMin) {
360 reading = kMin;
361 } else if (reading > kMax) {
362 reading = kMax;
363 }
364 return (static_cast<float>(2 * (reading - kMin)) /
365 static_cast<float>(kMax - kMin)) -
366 1.0f;
367}
368
369struct EncoderReading {
370 EncoderReading(const salsa::SimpleAdcReadings &adc_readings) {
371 const float sin = ConvertEncoderChannel(adc_readings.sin);
372 const float cos = ConvertEncoderChannel(adc_readings.cos);
373
Austin Schuhbcce26a2018-03-26 23:41:24 -0700374 const float magnitude = hypot(sin, cos);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400375 const float magnitude_error = ::std::abs(magnitude - 1.0f);
376 valid = magnitude_error < 0.15f;
377
378 angle = ::std::atan2(sin, cos);
379 }
380
381 // Angle in radians, in [-pi, pi].
382 float angle;
383
384 bool valid;
385};
386
387extern "C" void pit3_isr() {
388 PIT_TFLG3 = 1;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700389 PolyDrivetrain<float> *polydrivetrain =
390 global_polydrivetrain.load(::std::memory_order_acquire);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400391
392 salsa::SimpleAdcReadings adc_readings;
393 {
394 DisableInterrupts disable_interrupts;
395 adc_readings = salsa::AdcReadSimple(disable_interrupts);
396 }
397
398 EncoderReading encoder(adc_readings);
399
Austin Schuhbcce26a2018-03-26 23:41:24 -0700400 const bool lost_any_channel = lost_channel(0) || lost_channel(1) ||
401 lost_channel(2) || lost_channel(3) ||
402 lost_channel(4) ||
403 (::std::abs(convert_input_width(4)) < 0.05f);
404
405 if (polydrivetrain != nullptr) {
406 DrivetrainQueue_Goal goal;
407 goal.control_loop_driving = false;
408 if (lost_any_channel) {
409 goal.throttle = 0.0f;
410 goal.wheel = 0.0f;
411 } else {
412 goal.throttle = convert_input_width(1);
413 goal.wheel = convert_input_width(0);
414 }
415 goal.throttle = convert_input_width(1);
416 goal.quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
417
418 DrivetrainQueue_Output output;
419
420 polydrivetrain->SetGoal(goal);
421 polydrivetrain->Update();
422 polydrivetrain->SetOutput(&output);
423
424 vesc_set_duty(0, -output.left_voltage / 12.0f);
425 vesc_set_duty(1, -output.left_voltage / 12.0f);
426
427 vesc_set_duty(2, output.right_voltage / 12.0f);
428 vesc_set_duty(3, output.right_voltage / 12.0f);
429
430 static int i = 0;
431 ++i;
432 if (i > 20) {
433 i = 0;
434 if (lost_any_channel) {
435 printf("200Hz loop, disabled %d %d %d %d %d\n",
436 (int)(convert_input_width(0) * 1000),
437 (int)(convert_input_width(1) * 1000),
438 (int)(convert_input_width(2) * 1000),
439 (int)(convert_input_width(3) * 1000),
440 (int)(convert_input_width(4) * 1000));
441 } else {
442 printf(
443 "TODO(Austin): 200Hz loop %d %d %d %d %d, lr, %d, %d velocity %d "
444 "ADC %" PRIu16 " %" PRIu16 " enc %d/1000 %s from %d\n",
445 (int)(convert_input_width(0) * 1000),
446 (int)(convert_input_width(1) * 1000),
447 (int)(convert_input_width(2) * 1000),
448 (int)(convert_input_width(3) * 1000),
449 (int)(convert_input_width(4) * 1000),
450 static_cast<int>(output.left_voltage * 100),
451 static_cast<int>(output.right_voltage * 100),
452 static_cast<int>(polydrivetrain->velocity() * 100),
453 adc_readings.sin, adc_readings.cos, (int)(encoder.angle * 1000),
454 encoder.valid ? "T" : "f",
455 (int)(::std::sqrt(ConvertEncoderChannel(adc_readings.sin) *
456 ConvertEncoderChannel(adc_readings.sin) +
457 ConvertEncoderChannel(adc_readings.cos) *
458 ConvertEncoderChannel(adc_readings.cos)) *
459 1000));
460 }
461 }
462 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400463}
464
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700465} // namespace
466
467extern "C" {
468
469void *__stack_chk_guard = (void *)0x67111971;
470
471int _write(int /*file*/, char *ptr, int len) {
472 teensy::AcmTty *const tty = global_stdout.load(::std::memory_order_acquire);
473 if (tty != nullptr) {
474 return tty->Write(ptr, len);
475 }
476 return 0;
477}
478
479void __stack_chk_fail(void);
480
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700481} // extern "C"
482
483extern "C" int main(void) {
484 // for background about this startup delay, please see these conversations
485 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
486 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
487 delay(400);
488
489 // Set all interrupts to the second-lowest priority to start with.
490 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
491
492 // Now set priorities for all the ones we care about. They only have meaning
493 // relative to each other, which means centralizing them here makes it a lot
494 // more manageable.
495 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400496 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
497 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
498 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700499
500 // Builtin LED.
501 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
502 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
503 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
504
505 // Set up the CAN pins.
506 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
507 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
508
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700509 // PWM_IN0
510 // FTM0_CH0
511 PORTC_PCR1 = PORT_PCR_MUX(4);
512
513 // PWM_IN1
514 // FTM0_CH6
515 PORTD_PCR6 = PORT_PCR_MUX(4);
516
517 // PWM_IN2
518 // FTM0_CH4
519 PORTD_PCR4 = PORT_PCR_MUX(4);
520
521 // PWM_IN3
522 // FTM3_CH2
523 PORTD_PCR2 = PORT_PCR_MUX(4);
524
525 // PWM_IN4
526 // FTM0_CH2
527 PORTC_PCR3 = PORT_PCR_MUX(4);
528
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700529 delay(100);
530
531 teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
532 usb_device.SetManufacturer("Seems Reasonable LLC");
533 usb_device.SetProduct("Simple Receiver Board");
534
535 teensy::AcmTty tty0(&usb_device);
536 global_stdout.store(&tty0, ::std::memory_order_release);
537 usb_device.Initialize();
538
Brian Silvermana3a172b2018-03-24 03:53:32 -0400539 SIM_SCGC6 |= SIM_SCGC6_PIT;
540 // Workaround for errata e7914.
541 (void)PIT_MCR;
542 PIT_MCR = 0;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700543 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400544 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
545
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700546 can_init(0, 1);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400547 salsa::AdcInitSimple();
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700548 SetupPwmFtm(FTM0);
549 SetupPwmFtm(FTM3);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700550
Austin Schuhbcce26a2018-03-26 23:41:24 -0700551 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
552 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
553
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700554 // Leave the LEDs on for a bit longer.
555 delay(300);
556 printf("Done starting up\n");
557
558 // Done starting up, now turn the LED off.
559 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
560
Brian Silvermana3a172b2018-03-24 03:53:32 -0400561 NVIC_ENABLE_IRQ(IRQ_FTM0);
562 NVIC_ENABLE_IRQ(IRQ_FTM3);
563 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
564
Austin Schuhbcce26a2018-03-26 23:41:24 -0700565 while (true) {
566 }
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700567
568 return 0;
569}
570
571void __stack_chk_fail(void) {
572 while (true) {
573 GPIOC_PSOR = (1 << 5);
574 printf("Stack corruption detected\n");
575 delay(1000);
576 GPIOC_PCOR = (1 << 5);
577 delay(1000);
578 }
579}
580
581} // namespace motors
582} // namespace frc971