blob: 49f5ecb95facdaecb595285fc85644511b882a3c [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
Austin Schuh4fae0fc2018-03-27 23:51:42 -07008#include "frc971/control_loops/drivetrain/polydrivetrain.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -07009#include "motors/core/kinetis.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040010#include "motors/core/time.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070011#include "motors/peripheral/adc.h"
12#include "motors/peripheral/can.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040013#include "motors/peripheral/configuration.h"
Austin Schuh4fae0fc2018-03-27 23:51:42 -070014#include "motors/seems_reasonable/drivetrain_dog_motor_plant.h"
15#include "motors/seems_reasonable/polydrivetrain_dog_motor_plant.h"
16#include "motors/seems_reasonable/spring.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070017#include "motors/usb/cdc.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040018#include "motors/usb/usb.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070019#include "motors/util.h"
20
21namespace frc971 {
22namespace motors {
23namespace {
24
Austin Schuhbcce26a2018-03-26 23:41:24 -070025using ::frc971::control_loops::drivetrain::DrivetrainConfig;
26using ::frc971::control_loops::drivetrain::PolyDrivetrain;
27using ::frc971::constants::ShifterHallEffect;
28using ::frc971::control_loops::DrivetrainQueue_Goal;
29using ::frc971::control_loops::DrivetrainQueue_Output;
Austin Schuh4fae0fc2018-03-27 23:51:42 -070030using ::motors::seems_reasonable::Spring;
Austin Schuhbcce26a2018-03-26 23:41:24 -070031
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 ::motors::seems_reasonable::kDt, ::motors::seems_reasonable::kRobotRadius,
46 ::motors::seems_reasonable::kWheelRadius, ::motors::seems_reasonable::kV,
47
48 ::motors::seems_reasonable::kHighGearRatio,
49 ::motors::seems_reasonable::kLowGearRatio, kThreeStateDriveShifter,
50 kThreeStateDriveShifter, true /* default_high_gear */,
51 0 /* down_offset if using constants use
52 constants::GetValues().down_error */, 0.8 /* wheel_non_linearity */,
53 1.2 /* quickturn_wheel_multiplier */, 1.5 /* wheel_multiplier */,
54 };
55
56 return kDrivetrainConfig;
57};
58
59
Brian Silverman54dd2fe2018-03-16 23:44:31 -070060::std::atomic<teensy::AcmTty *> global_stdout{nullptr};
61
Austin Schuhbcce26a2018-03-26 23:41:24 -070062::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
Austin Schuh4fae0fc2018-03-27 23:51:42 -070063::std::atomic<Spring *> global_spring{nullptr};
Austin Schuhbcce26a2018-03-26 23:41:24 -070064
Brian Silvermana3a172b2018-03-24 03:53:32 -040065// Last width we received on each channel.
Brian Silverman7f5f1442018-04-06 13:00:50 -040066uint16_t pwm_input_widths[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -040067// When we received a pulse on each channel in milliseconds.
Brian Silverman7f5f1442018-04-06 13:00:50 -040068uint32_t pwm_input_times[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -040069
Austin Schuhbcce26a2018-03-26 23:41:24 -070070constexpr int kChannelTimeout = 100;
71
72bool lost_channel(int channel) {
73 DisableInterrupts disable_interrupts;
74 if (time_after(millis(),
75 time_add(pwm_input_times[channel], kChannelTimeout))) {
76 return true;
77 }
78 return false;
79}
80
Brian Silvermana3a172b2018-03-24 03:53:32 -040081// Returns the most recently captured value for the specified input channel
82// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
83float convert_input_width(int channel) {
84 uint16_t width;
85 {
86 DisableInterrupts disable_interrupts;
Austin Schuhbcce26a2018-03-26 23:41:24 -070087 if (time_after(millis(),
88 time_add(pwm_input_times[channel], kChannelTimeout))) {
Brian Silvermana3a172b2018-03-24 03:53:32 -040089 return 0;
90 }
91
92 width = pwm_input_widths[channel];
93 }
94
95 // Values measured with a channel mapped to a button.
96 static constexpr uint16_t kMinWidth = 4133;
97 static constexpr uint16_t kMaxWidth = 7177;
98 if (width < kMinWidth) {
99 width = kMinWidth;
100 } else if (width > kMaxWidth) {
101 width = kMaxWidth;
102 }
103 return (static_cast<float>(2 * (width - kMinWidth)) /
104 static_cast<float>(kMaxWidth - kMinWidth)) -
105 1.0f;
106}
107
108// Sends a SET_RPM command to the specified VESC.
109// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
110// bandwidth.
111void vesc_set_rpm(int vesc_id, float rpm) {
112 const int32_t rpm_int = rpm;
113 uint32_t id = CAN_EFF_FLAG;
114 id |= vesc_id;
115 id |= (0x03 /* SET_RPM */) << 8;
116 uint8_t data[4] = {
117 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
118 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
119 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
120 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
121 };
122 can_send(id, data, sizeof(data), 2 + vesc_id);
123}
124
125// Sends a SET_CURRENT command to the specified VESC.
126// current is in amps.
127// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
128// bandwidth.
129void vesc_set_current(int vesc_id, float current) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700130 constexpr float kMaxCurrent = 80.0f;
131 const int32_t current_int =
132 ::std::max(-kMaxCurrent, ::std::min(kMaxCurrent, current)) * 1000.0f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400133 uint32_t id = CAN_EFF_FLAG;
134 id |= vesc_id;
135 id |= (0x01 /* SET_CURRENT */) << 8;
136 uint8_t data[4] = {
137 static_cast<uint8_t>((current_int >> 24) & 0xFF),
138 static_cast<uint8_t>((current_int >> 16) & 0xFF),
139 static_cast<uint8_t>((current_int >> 8) & 0xFF),
140 static_cast<uint8_t>((current_int >> 0) & 0xFF),
141 };
142 can_send(id, data, sizeof(data), 2 + vesc_id);
143}
144
Brian Silverman4d1e5272018-03-26 03:18:42 -0400145// Sends a SET_DUTY command to the specified VESC.
146// duty is from -1 to 1.
147// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
148// bandwidth.
149void vesc_set_duty(int vesc_id, float duty) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700150 constexpr int32_t kMaxDuty = 99999;
151 const int32_t duty_int = ::std::max(
152 -kMaxDuty, ::std::min(kMaxDuty, static_cast<int32_t>(duty * 100000.0f)));
Brian Silverman4d1e5272018-03-26 03:18:42 -0400153 uint32_t id = CAN_EFF_FLAG;
154 id |= vesc_id;
155 id |= (0x00 /* SET_DUTY */) << 8;
156 uint8_t data[4] = {
157 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
158 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
159 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
160 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
161 };
162 can_send(id, data, sizeof(data), 2 + vesc_id);
163}
164
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700165void DoVescTest() {
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700166 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);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700172 float current;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700173 if (done) {
174 current = -6;
175 } else {
176 current = 6;
177 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400178 vesc_set_current(i, current);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700179 if (done) {
180 break;
181 }
182 delay(5);
183 }
184 time = end;
185 }
186 }
187}
188
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700189void DoReceiverTest2() {
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700190 static constexpr float kMaxRpm = 10000.0f;
191 while (true) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400192 const bool flip = convert_input_width(2) > 0;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700193
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700194 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400195 const float value = convert_input_width(0);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700196
197 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400198 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700199 if (flip) {
200 rpm *= -1.0f;
201 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400202 vesc_set_rpm(0, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700203 }
204
205 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400206 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700207 if (flip) {
208 rpm *= -1.0f;
209 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400210 vesc_set_rpm(1, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700211 }
212 }
213
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700214 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400215 const float value = convert_input_width(1);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700216
217 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400218 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700219 if (flip) {
220 rpm *= -1.0f;
221 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400222 vesc_set_rpm(2, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700223 }
224
225 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400226 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700227 if (flip) {
228 rpm *= -1.0f;
229 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400230 vesc_set_rpm(3, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700231 }
232 }
233
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700234 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400235 const float value = convert_input_width(4);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700236
237 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400238 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700239 if (flip) {
240 rpm *= -1.0f;
241 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400242 vesc_set_rpm(4, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700243 }
244
245 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400246 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700247 if (flip) {
248 rpm *= -1.0f;
249 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400250 vesc_set_rpm(5, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700251 }
252 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400253 // Give the CAN frames a chance to go out.
254 delay(5);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700255 }
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.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400270 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700271
272 // Capturing rising edge.
273 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
274 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400275 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700276
277 // Capturing rising edge.
278 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
279 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400280 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700281
282 // Capturing rising edge.
283 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
284 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400285 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700286
Brian Silvermana3a172b2018-03-24 03:53:32 -0400287 (void)ftm->STATUS;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700288 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
Brian Silverman2de95d62018-03-31 12:32:24 -0700302struct AccelerometerResult {
303 uint16_t result;
304 bool success;
305};
306
307// Does a transfer on the accelerometer. Returns the resulting frame, or a
308// failure if it takes until after end_micros.
309AccelerometerResult AccelerometerTransfer(uint16_t data, uint32_t end_micros) {
310 SPI0_SR = SPI_SR_RFDF;
311 SPI0_PUSHR = SPI_PUSHR_PCS(1) | data;
312
313 while (!(SPI0_SR & SPI_SR_RFDF)) {
314 if (time_after(micros(), end_micros)) {
315 return {0, false};
316 }
317 }
318 const uint32_t popr = SPI0_POPR;
319 SPI0_SR = SPI_SR_RFDF;
320 return {static_cast<uint16_t>(popr & 0xFFFF), true};
321}
322
323constexpr uint32_t kAccelerometerTimeout = 500;
324
325bool AccelerometerWrite(uint8_t address, uint8_t data, uint32_t end_micros) {
326 const AccelerometerResult r = AccelerometerTransfer(
327 (static_cast<uint16_t>(address) << 8) | static_cast<uint16_t>(data),
328 end_micros);
329 return r.success;
330}
331
332AccelerometerResult AccelerometerRead(uint8_t address, uint32_t end_micros) {
333 AccelerometerResult r = AccelerometerTransfer(
334 (static_cast<uint16_t>(address) << 8) | UINT16_C(0x8000), end_micros);
335 r.result = r.result & UINT16_C(0xFF);
336 return r;
337}
338
339bool accelerometer_inited = false;
340
341void AccelerometerInit() {
342 accelerometer_inited = false;
343 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
344 {
345 const auto who_am_i = AccelerometerRead(0xF, end_micros);
346 if (!who_am_i.success) {
347 return;
348 }
349 if (who_am_i.result != 0x32) {
350 return;
351 }
352 }
353 if (!AccelerometerWrite(
354 0x20, (1 << 5) /* Normal mode */ | (1 << 3) /* 100 Hz */ |
355 (1 << 2) /* Z enabled */ | (1 << 1) /* Y enabled */ |
356 (1 << 0) /* X enabled */,
357 end_micros)) {
358 }
359 // If want to read LSB, need to enable BDU to avoid splitting reads.
360 if (!AccelerometerWrite(0x23, (0 << 6) /* Data LSB at lower address */ |
361 (3 << 4) /* 400g full scale */ |
362 (0 << 0) /* 4-wire interface */,
363 end_micros)) {
364 }
365 accelerometer_inited = true;
366}
367
368float AccelerometerConvert(uint16_t value) {
369 return static_cast<float>(400.0 / 65536.0) * static_cast<float>(value);
370}
371
372// Returns the total acceleration (in any direction) or 0 if there's an error.
373float ReadAccelerometer() {
374 if (!accelerometer_inited) {
375 AccelerometerInit();
376 return 0;
377 }
378
379 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
380 const auto x = AccelerometerRead(0x29, end_micros);
381 const auto y = AccelerometerRead(0x2B, end_micros);
382 const auto z = AccelerometerRead(0x2D, end_micros);
383 if (!x.success || !y.success || !z.success) {
384 accelerometer_inited = false;
385 return 0;
386 }
387
388 const float x_g = AccelerometerConvert(x.result);
389 const float y_g = AccelerometerConvert(y.result);
390 const float z_g = AccelerometerConvert(z.result);
391 return ::std::sqrt(x_g * x_g + y_g * y_g + z_g * z_g);
392}
393
Brian Silvermana3a172b2018-03-24 03:53:32 -0400394extern "C" void ftm0_isr() {
395 while (true) {
396 const uint32_t status = FTM0->STATUS;
397 if (status == 0) {
398 return;
399 }
400
401 if (status & (1 << 1)) {
402 const uint32_t start = FTM0->C0V;
403 const uint32_t end = FTM0->C1V;
404 pwm_input_widths[0] = (end - start) & 0xFFFF;
405 pwm_input_times[0] = millis();
406 }
407 if (status & (1 << 7)) {
408 const uint32_t start = FTM0->C6V;
409 const uint32_t end = FTM0->C7V;
410 pwm_input_widths[1] = (end - start) & 0xFFFF;
411 pwm_input_times[1] = millis();
412 }
413 if (status & (1 << 5)) {
414 const uint32_t start = FTM0->C4V;
415 const uint32_t end = FTM0->C5V;
416 pwm_input_widths[2] = (end - start) & 0xFFFF;
417 pwm_input_times[2] = millis();
418 }
419 if (status & (1 << 3)) {
420 const uint32_t start = FTM0->C2V;
421 const uint32_t end = FTM0->C3V;
422 pwm_input_widths[4] = (end - start) & 0xFFFF;
423 pwm_input_times[4] = millis();
424 }
425
426 FTM0->STATUS = 0;
427 }
428}
429
430extern "C" void ftm3_isr() {
431 while (true) {
432 const uint32_t status = FTM3->STATUS;
433 if (status == 0) {
434 return;
435 }
436
437 if (status & (1 << 3)) {
438 const uint32_t start = FTM3->C2V;
439 const uint32_t end = FTM3->C3V;
440 pwm_input_widths[3] = (end - start) & 0xFFFF;
441 pwm_input_times[3] = millis();
442 }
Brian Silverman7f5f1442018-04-06 13:00:50 -0400443 if (status & (1 << 7)) {
444 const uint32_t start = FTM3->C6V;
445 const uint32_t end = FTM3->C7V;
446 pwm_input_widths[5] = (end - start) & 0xFFFF;
447 pwm_input_times[5] = millis();
448 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400449
450 FTM3->STATUS = 0;
451 }
452}
453
454float ConvertEncoderChannel(uint16_t reading) {
455 // Theoretical values based on the datasheet are 931 and 2917.
456 // With these values, the magnitude ranges from 0.99-1.03, which works fine
457 // (the encoder's output appears to get less accurate in one quadrant for some
458 // reason, hence the variation).
459 static constexpr uint16_t kMin = 802, kMax = 3088;
460 if (reading < kMin) {
461 reading = kMin;
462 } else if (reading > kMax) {
463 reading = kMax;
464 }
465 return (static_cast<float>(2 * (reading - kMin)) /
466 static_cast<float>(kMax - kMin)) -
467 1.0f;
468}
469
470struct EncoderReading {
Brian Silvermana96c1a42018-05-12 12:11:31 -0700471 EncoderReading(const SimpleAdcReadings &adc_readings) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400472 const float sin = ConvertEncoderChannel(adc_readings.sin);
473 const float cos = ConvertEncoderChannel(adc_readings.cos);
474
Austin Schuhbcce26a2018-03-26 23:41:24 -0700475 const float magnitude = hypot(sin, cos);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400476 const float magnitude_error = ::std::abs(magnitude - 1.0f);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700477 valid = magnitude_error < 0.30f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400478
479 angle = ::std::atan2(sin, cos);
480 }
481
482 // Angle in radians, in [-pi, pi].
483 float angle;
484
485 bool valid;
486};
487
488extern "C" void pit3_isr() {
489 PIT_TFLG3 = 1;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700490 PolyDrivetrain<float> *polydrivetrain =
491 global_polydrivetrain.load(::std::memory_order_acquire);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700492 Spring *spring = global_spring.load(::std::memory_order_acquire);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400493
Brian Silvermana96c1a42018-05-12 12:11:31 -0700494 SimpleAdcReadings adc_readings;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400495 {
496 DisableInterrupts disable_interrupts;
Brian Silvermana96c1a42018-05-12 12:11:31 -0700497 adc_readings = AdcReadSimple(disable_interrupts);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400498 }
499
500 EncoderReading encoder(adc_readings);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700501 static float last_good_encoder = 0.0f;
502 static int invalid_encoder_count = 0;
503 if (encoder.valid) {
504 last_good_encoder = encoder.angle;
505 invalid_encoder_count = 0;
506 } else {
507 ++invalid_encoder_count;
508 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400509
Austin Schuhbcce26a2018-03-26 23:41:24 -0700510 const bool lost_any_channel = lost_channel(0) || lost_channel(1) ||
511 lost_channel(2) || lost_channel(3) ||
512 lost_channel(4) ||
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700513 (::std::abs(convert_input_width(4)) < 0.5f);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700514
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700515 if (polydrivetrain != nullptr && spring != nullptr) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700516 DrivetrainQueue_Goal goal;
517 goal.control_loop_driving = false;
518 if (lost_any_channel) {
519 goal.throttle = 0.0f;
520 goal.wheel = 0.0f;
521 } else {
522 goal.throttle = convert_input_width(1);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700523 goal.wheel = -convert_input_width(0);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700524 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700525 goal.quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
526
527 DrivetrainQueue_Output output;
528
529 polydrivetrain->SetGoal(goal);
530 polydrivetrain->Update();
531 polydrivetrain->SetOutput(&output);
532
533 vesc_set_duty(0, -output.left_voltage / 12.0f);
534 vesc_set_duty(1, -output.left_voltage / 12.0f);
535
536 vesc_set_duty(2, output.right_voltage / 12.0f);
537 vesc_set_duty(3, output.right_voltage / 12.0f);
538
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700539 bool prime = convert_input_width(2) > 0.1f;
540 bool fire = convert_input_width(3) > 0.1f;
541
542 bool unload = lost_any_channel;
543 static bool was_lost = true;
544 bool force_reset = !lost_any_channel && was_lost;
545 was_lost = lost_any_channel;
546
547 spring->Iterate(unload, prime, fire, force_reset, invalid_encoder_count <= 2,
548 last_good_encoder);
549
550 float spring_output = spring->output();
551
552 vesc_set_duty(4, -spring_output);
553 vesc_set_duty(5, spring_output);
554
Brian Silverman2de95d62018-03-31 12:32:24 -0700555 const float accelerometer = ReadAccelerometer();
556 (void)accelerometer;
557
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700558 /*
559 // Massive debug. Turn on for useful bits.
Brian Silverman2de95d62018-03-31 12:32:24 -0700560 printf("acc %d/1000\n", (int)(accelerometer / 1000));
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700561 if (!encoder.valid) {
562 printf("Stuck encoder: ADC %" PRIu16 " %" PRIu16
563 " enc %d/1000 %s mag %d\n",
564 adc_readings.sin, adc_readings.cos, (int)(encoder.angle * 1000),
565 encoder.valid ? "T" : "f",
566 (int)(hypot(ConvertEncoderChannel(adc_readings.sin),
567 ConvertEncoderChannel(adc_readings.cos)) *
568 1000));
569 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700570 static int i = 0;
571 ++i;
572 if (i > 20) {
573 i = 0;
574 if (lost_any_channel) {
575 printf("200Hz loop, disabled %d %d %d %d %d\n",
576 (int)(convert_input_width(0) * 1000),
577 (int)(convert_input_width(1) * 1000),
578 (int)(convert_input_width(2) * 1000),
579 (int)(convert_input_width(3) * 1000),
580 (int)(convert_input_width(4) * 1000));
581 } else {
582 printf(
583 "TODO(Austin): 200Hz loop %d %d %d %d %d, lr, %d, %d velocity %d "
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700584 " state: %d, near %d angle %d goal %d to: %d ADC %" PRIu16
585 " %" PRIu16 " enc %d/1000 %s from %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700586 (int)(convert_input_width(0) * 1000),
587 (int)(convert_input_width(1) * 1000),
588 (int)(convert_input_width(2) * 1000),
589 (int)(convert_input_width(3) * 1000),
590 (int)(convert_input_width(4) * 1000),
591 static_cast<int>(output.left_voltage * 100),
592 static_cast<int>(output.right_voltage * 100),
593 static_cast<int>(polydrivetrain->velocity() * 100),
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700594 static_cast<int>(spring->state()), static_cast<int>(spring->Near()),
595 static_cast<int>(spring->angle() * 1000),
596 static_cast<int>(spring->goal() * 1000),
597 static_cast<int>(spring->timeout()), adc_readings.sin,
598 adc_readings.cos, (int)(encoder.angle * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700599 encoder.valid ? "T" : "f",
600 (int)(::std::sqrt(ConvertEncoderChannel(adc_readings.sin) *
601 ConvertEncoderChannel(adc_readings.sin) +
602 ConvertEncoderChannel(adc_readings.cos) *
603 ConvertEncoderChannel(adc_readings.cos)) *
604 1000));
605 }
606 }
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700607 */
Austin Schuhbcce26a2018-03-26 23:41:24 -0700608 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400609}
610
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700611} // namespace
612
613extern "C" {
614
615void *__stack_chk_guard = (void *)0x67111971;
616
617int _write(int /*file*/, char *ptr, int len) {
618 teensy::AcmTty *const tty = global_stdout.load(::std::memory_order_acquire);
619 if (tty != nullptr) {
620 return tty->Write(ptr, len);
621 }
622 return 0;
623}
624
625void __stack_chk_fail(void);
626
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700627} // extern "C"
628
629extern "C" int main(void) {
630 // for background about this startup delay, please see these conversations
631 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
632 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
633 delay(400);
634
635 // Set all interrupts to the second-lowest priority to start with.
636 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
637
638 // Now set priorities for all the ones we care about. They only have meaning
639 // relative to each other, which means centralizing them here makes it a lot
640 // more manageable.
641 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400642 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
643 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
644 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700645
646 // Builtin LED.
647 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
648 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
649 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
650
651 // Set up the CAN pins.
652 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
653 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
654
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700655 // PWM_IN0
656 // FTM0_CH0
657 PORTC_PCR1 = PORT_PCR_MUX(4);
658
659 // PWM_IN1
660 // FTM0_CH6
661 PORTD_PCR6 = PORT_PCR_MUX(4);
662
663 // PWM_IN2
664 // FTM0_CH4
665 PORTD_PCR4 = PORT_PCR_MUX(4);
666
667 // PWM_IN3
668 // FTM3_CH2
669 PORTD_PCR2 = PORT_PCR_MUX(4);
670
671 // PWM_IN4
672 // FTM0_CH2
673 PORTC_PCR3 = PORT_PCR_MUX(4);
674
Brian Silverman7f5f1442018-04-06 13:00:50 -0400675 // PWM_IN5
676 // FTM3_CH6
677 PORTC_PCR10 = PORT_PCR_MUX(3);
678
Brian Silverman2de95d62018-03-31 12:32:24 -0700679 // SPI0
680 // ACC_CS PCS0
681 PORTA_PCR14 = PORT_PCR_DSE | PORT_PCR_MUX(2);
682 // SCK
683 PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(2);
684 // MOSI
685 PORTA_PCR16 = PORT_PCR_DSE | PORT_PCR_MUX(2);
686 // MISO
687 PORTA_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(2);
688
689 SIM_SCGC6 |= SIM_SCGC6_SPI0;
690 SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(1) | SPI_MCR_CLR_TXF |
691 SPI_MCR_CLR_RXF | SPI_MCR_HALT;
692 // 60 MHz "protocol clock"
693 // 6ns CS setup
694 // 8ns CS hold
695 SPI0_CTAR0 = SPI_CTAR_FMSZ(15) | SPI_CTAR_CPOL /* Clock idles high */ |
696 SPI_CTAR_CPHA /* Data captured on trailing edge */ |
697 0 /* !LSBFE MSB first */ |
698 SPI_CTAR_PCSSCK(0) /* PCS->SCK prescaler = 1 */ |
699 SPI_CTAR_PASC(0) /* SCK->PCS prescaler = 1 */ |
700 SPI_CTAR_PDT(0) /* PCS->PCS prescaler = 1 */ |
701 SPI_CTAR_PBR(0) /* baud prescaler = 1 */ |
702 SPI_CTAR_CSSCK(0) /* PCS->SCK 2/60MHz = 33.33ns */ |
703 SPI_CTAR_ASC(0) /* SCK->PCS 2/60MHz = 33.33ns */ |
704 SPI_CTAR_DT(2) /* PCS->PSC 8/60MHz = 133.33ns */ |
705 SPI_CTAR_BR(2) /* BR 60MHz/6 = 10MHz */;
706
707 SPI0_MCR &= ~SPI_MCR_HALT;
708
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700709 delay(100);
710
711 teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
712 usb_device.SetManufacturer("Seems Reasonable LLC");
713 usb_device.SetProduct("Simple Receiver Board");
714
715 teensy::AcmTty tty0(&usb_device);
716 global_stdout.store(&tty0, ::std::memory_order_release);
717 usb_device.Initialize();
718
Brian Silvermana3a172b2018-03-24 03:53:32 -0400719 SIM_SCGC6 |= SIM_SCGC6_PIT;
720 // Workaround for errata e7914.
721 (void)PIT_MCR;
722 PIT_MCR = 0;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700723 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400724 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
725
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700726 can_init(0, 1);
Brian Silvermana96c1a42018-05-12 12:11:31 -0700727 AdcInitSimple();
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700728 SetupPwmFtm(FTM0);
729 SetupPwmFtm(FTM3);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700730
Austin Schuhbcce26a2018-03-26 23:41:24 -0700731 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
732 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700733 Spring spring;
734 global_spring.store(&spring, ::std::memory_order_release);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700735
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700736 // Leave the LEDs on for a bit longer.
737 delay(300);
738 printf("Done starting up\n");
739
Brian Silverman2de95d62018-03-31 12:32:24 -0700740 AccelerometerInit();
741 printf("Accelerometer init %s\n", accelerometer_inited ? "success" : "fail");
742
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700743 // Done starting up, now turn the LED off.
744 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
745
Brian Silvermana3a172b2018-03-24 03:53:32 -0400746 NVIC_ENABLE_IRQ(IRQ_FTM0);
747 NVIC_ENABLE_IRQ(IRQ_FTM3);
748 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
749
Austin Schuhbcce26a2018-03-26 23:41:24 -0700750 while (true) {
751 }
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700752
753 return 0;
754}
755
756void __stack_chk_fail(void) {
757 while (true) {
758 GPIOC_PSOR = (1 << 5);
759 printf("Stack corruption detected\n");
760 delay(1000);
761 GPIOC_PCOR = (1 << 5);
762 delay(1000);
763 }
764}
765
766} // namespace motors
767} // namespace frc971