blob: 3b174b0f4468618deab21eaa39d63fd5c40209d0 [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
Brian Silverman9ed2cf12018-05-12 13:06:38 -070032struct SimpleAdcReadings {
33 uint16_t sin, cos;
34};
35
36void AdcInitSimple() {
37 AdcInitCommon();
38
39 // ENC_SIN ADC0_SE23
40 // ENC_COS ADC1_SE23
41}
42
43SimpleAdcReadings AdcReadSimple(const DisableInterrupts &) {
44 SimpleAdcReadings r;
45
46 ADC0_SC1A = 23;
47 ADC1_SC1A = 23;
48 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
49 }
50 while (!(ADC1_SC1A & ADC_SC1_COCO)) {
51 }
52 r.sin = ADC0_RA;
53 r.cos = ADC1_RA;
54
55 return r;
56}
57
Austin Schuhbcce26a2018-03-26 23:41:24 -070058const ShifterHallEffect kThreeStateDriveShifter{0.0, 0.0, 0.25, 0.75};
59
60const DrivetrainConfig<float> &GetDrivetrainConfig() {
61 static DrivetrainConfig<float> kDrivetrainConfig{
62 ::frc971::control_loops::drivetrain::ShifterType::NO_SHIFTER,
63 ::frc971::control_loops::drivetrain::LoopType::OPEN_LOOP,
64 ::frc971::control_loops::drivetrain::GyroType::SPARTAN_GYRO,
65 ::frc971::control_loops::drivetrain::IMUType::IMU_X,
66
67 ::motors::seems_reasonable::MakeDrivetrainLoop,
68 ::motors::seems_reasonable::MakeVelocityDrivetrainLoop,
69 ::std::function<StateFeedbackLoop<7, 2, 4, float>()>(),
70
71 ::motors::seems_reasonable::kDt, ::motors::seems_reasonable::kRobotRadius,
72 ::motors::seems_reasonable::kWheelRadius, ::motors::seems_reasonable::kV,
73
74 ::motors::seems_reasonable::kHighGearRatio,
75 ::motors::seems_reasonable::kLowGearRatio, kThreeStateDriveShifter,
76 kThreeStateDriveShifter, true /* default_high_gear */,
77 0 /* down_offset if using constants use
78 constants::GetValues().down_error */, 0.8 /* wheel_non_linearity */,
79 1.2 /* quickturn_wheel_multiplier */, 1.5 /* wheel_multiplier */,
80 };
81
82 return kDrivetrainConfig;
83};
84
85
Brian Silverman54dd2fe2018-03-16 23:44:31 -070086::std::atomic<teensy::AcmTty *> global_stdout{nullptr};
87
Austin Schuhbcce26a2018-03-26 23:41:24 -070088::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
Austin Schuh4fae0fc2018-03-27 23:51:42 -070089::std::atomic<Spring *> global_spring{nullptr};
Austin Schuhbcce26a2018-03-26 23:41:24 -070090
Brian Silvermana3a172b2018-03-24 03:53:32 -040091// Last width we received on each channel.
Brian Silverman7f5f1442018-04-06 13:00:50 -040092uint16_t pwm_input_widths[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -040093// When we received a pulse on each channel in milliseconds.
Brian Silverman7f5f1442018-04-06 13:00:50 -040094uint32_t pwm_input_times[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -040095
Austin Schuhbcce26a2018-03-26 23:41:24 -070096constexpr int kChannelTimeout = 100;
97
98bool lost_channel(int channel) {
99 DisableInterrupts disable_interrupts;
100 if (time_after(millis(),
101 time_add(pwm_input_times[channel], kChannelTimeout))) {
102 return true;
103 }
104 return false;
105}
106
Brian Silvermana3a172b2018-03-24 03:53:32 -0400107// Returns the most recently captured value for the specified input channel
108// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
109float convert_input_width(int channel) {
110 uint16_t width;
111 {
112 DisableInterrupts disable_interrupts;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700113 if (time_after(millis(),
114 time_add(pwm_input_times[channel], kChannelTimeout))) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400115 return 0;
116 }
117
118 width = pwm_input_widths[channel];
119 }
120
121 // Values measured with a channel mapped to a button.
122 static constexpr uint16_t kMinWidth = 4133;
123 static constexpr uint16_t kMaxWidth = 7177;
124 if (width < kMinWidth) {
125 width = kMinWidth;
126 } else if (width > kMaxWidth) {
127 width = kMaxWidth;
128 }
129 return (static_cast<float>(2 * (width - kMinWidth)) /
130 static_cast<float>(kMaxWidth - kMinWidth)) -
131 1.0f;
132}
133
134// Sends a SET_RPM command to the specified VESC.
135// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
136// bandwidth.
137void vesc_set_rpm(int vesc_id, float rpm) {
138 const int32_t rpm_int = rpm;
139 uint32_t id = CAN_EFF_FLAG;
140 id |= vesc_id;
141 id |= (0x03 /* SET_RPM */) << 8;
142 uint8_t data[4] = {
143 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
144 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
145 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
146 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
147 };
148 can_send(id, data, sizeof(data), 2 + vesc_id);
149}
150
151// Sends a SET_CURRENT command to the specified VESC.
152// current is in amps.
153// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
154// bandwidth.
155void vesc_set_current(int vesc_id, float current) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700156 constexpr float kMaxCurrent = 80.0f;
157 const int32_t current_int =
158 ::std::max(-kMaxCurrent, ::std::min(kMaxCurrent, current)) * 1000.0f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400159 uint32_t id = CAN_EFF_FLAG;
160 id |= vesc_id;
161 id |= (0x01 /* SET_CURRENT */) << 8;
162 uint8_t data[4] = {
163 static_cast<uint8_t>((current_int >> 24) & 0xFF),
164 static_cast<uint8_t>((current_int >> 16) & 0xFF),
165 static_cast<uint8_t>((current_int >> 8) & 0xFF),
166 static_cast<uint8_t>((current_int >> 0) & 0xFF),
167 };
168 can_send(id, data, sizeof(data), 2 + vesc_id);
169}
170
Brian Silverman4d1e5272018-03-26 03:18:42 -0400171// Sends a SET_DUTY command to the specified VESC.
172// duty is from -1 to 1.
173// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
174// bandwidth.
175void vesc_set_duty(int vesc_id, float duty) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700176 constexpr int32_t kMaxDuty = 99999;
177 const int32_t duty_int = ::std::max(
178 -kMaxDuty, ::std::min(kMaxDuty, static_cast<int32_t>(duty * 100000.0f)));
Brian Silverman4d1e5272018-03-26 03:18:42 -0400179 uint32_t id = CAN_EFF_FLAG;
180 id |= vesc_id;
181 id |= (0x00 /* SET_DUTY */) << 8;
182 uint8_t data[4] = {
183 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
184 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
185 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
186 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
187 };
188 can_send(id, data, sizeof(data), 2 + vesc_id);
189}
190
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700191// TODO(Brian): Move these two test functions somewhere else.
192__attribute__((unused)) void DoVescTest() {
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700193 uint32_t time = micros();
194 while (true) {
195 for (int i = 0; i < 6; ++i) {
196 const uint32_t end = time_add(time, 500000);
197 while (true) {
198 const bool done = time_after(micros(), end);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700199 float current;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700200 if (done) {
201 current = -6;
202 } else {
203 current = 6;
204 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400205 vesc_set_current(i, current);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700206 if (done) {
207 break;
208 }
209 delay(5);
210 }
211 time = end;
212 }
213 }
214}
215
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700216__attribute__((unused)) void DoReceiverTest2() {
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700217 static constexpr float kMaxRpm = 10000.0f;
218 while (true) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400219 const bool flip = convert_input_width(2) > 0;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700220
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700221 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400222 const float value = convert_input_width(0);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700223
224 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400225 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700226 if (flip) {
227 rpm *= -1.0f;
228 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400229 vesc_set_rpm(0, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700230 }
231
232 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400233 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700234 if (flip) {
235 rpm *= -1.0f;
236 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400237 vesc_set_rpm(1, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700238 }
239 }
240
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700241 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400242 const float value = convert_input_width(1);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700243
244 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400245 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700246 if (flip) {
247 rpm *= -1.0f;
248 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400249 vesc_set_rpm(2, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700250 }
251
252 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400253 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700254 if (flip) {
255 rpm *= -1.0f;
256 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400257 vesc_set_rpm(3, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700258 }
259 }
260
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700261 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400262 const float value = convert_input_width(4);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700263
264 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400265 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700266 if (flip) {
267 rpm *= -1.0f;
268 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400269 vesc_set_rpm(4, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700270 }
271
272 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400273 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700274 if (flip) {
275 rpm *= -1.0f;
276 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400277 vesc_set_rpm(5, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700278 }
279 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400280 // Give the CAN frames a chance to go out.
281 delay(5);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700282 }
283}
284
285void SetupPwmFtm(BigFTM *ftm) {
286 ftm->MODE = FTM_MODE_WPDIS;
287 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
288 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
289
290 // Can't change MOD according to the reference manual ("The Dual Edge Capture
291 // mode must be used with ... the FTM counter in Free running counter.").
292 ftm->MOD = 0xFFFF;
293
294 // Capturing rising edge.
295 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
296 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400297 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700298
299 // Capturing rising edge.
300 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
301 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400302 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700303
304 // Capturing rising edge.
305 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
306 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400307 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700308
309 // Capturing rising edge.
310 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
311 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400312 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700313
Brian Silvermana3a172b2018-03-24 03:53:32 -0400314 (void)ftm->STATUS;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700315 ftm->STATUS = 0x00;
316
317 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
318 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
319 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
320 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
321
322 // 34.95ms max period before it starts wrapping and being weird.
323 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
324 FTM_SC_PS(4) /* Prescaler=32 */;
325
326 ftm->MODE &= ~FTM_MODE_WPDIS;
327}
328
Brian Silverman2de95d62018-03-31 12:32:24 -0700329struct AccelerometerResult {
330 uint16_t result;
331 bool success;
332};
333
334// Does a transfer on the accelerometer. Returns the resulting frame, or a
335// failure if it takes until after end_micros.
336AccelerometerResult AccelerometerTransfer(uint16_t data, uint32_t end_micros) {
337 SPI0_SR = SPI_SR_RFDF;
338 SPI0_PUSHR = SPI_PUSHR_PCS(1) | data;
339
340 while (!(SPI0_SR & SPI_SR_RFDF)) {
341 if (time_after(micros(), end_micros)) {
342 return {0, false};
343 }
344 }
345 const uint32_t popr = SPI0_POPR;
346 SPI0_SR = SPI_SR_RFDF;
347 return {static_cast<uint16_t>(popr & 0xFFFF), true};
348}
349
350constexpr uint32_t kAccelerometerTimeout = 500;
351
352bool AccelerometerWrite(uint8_t address, uint8_t data, uint32_t end_micros) {
353 const AccelerometerResult r = AccelerometerTransfer(
354 (static_cast<uint16_t>(address) << 8) | static_cast<uint16_t>(data),
355 end_micros);
356 return r.success;
357}
358
359AccelerometerResult AccelerometerRead(uint8_t address, uint32_t end_micros) {
360 AccelerometerResult r = AccelerometerTransfer(
361 (static_cast<uint16_t>(address) << 8) | UINT16_C(0x8000), end_micros);
362 r.result = r.result & UINT16_C(0xFF);
363 return r;
364}
365
366bool accelerometer_inited = false;
367
368void AccelerometerInit() {
369 accelerometer_inited = false;
370 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
371 {
372 const auto who_am_i = AccelerometerRead(0xF, end_micros);
373 if (!who_am_i.success) {
374 return;
375 }
376 if (who_am_i.result != 0x32) {
377 return;
378 }
379 }
380 if (!AccelerometerWrite(
381 0x20, (1 << 5) /* Normal mode */ | (1 << 3) /* 100 Hz */ |
382 (1 << 2) /* Z enabled */ | (1 << 1) /* Y enabled */ |
383 (1 << 0) /* X enabled */,
384 end_micros)) {
385 }
386 // If want to read LSB, need to enable BDU to avoid splitting reads.
387 if (!AccelerometerWrite(0x23, (0 << 6) /* Data LSB at lower address */ |
388 (3 << 4) /* 400g full scale */ |
389 (0 << 0) /* 4-wire interface */,
390 end_micros)) {
391 }
392 accelerometer_inited = true;
393}
394
395float AccelerometerConvert(uint16_t value) {
396 return static_cast<float>(400.0 / 65536.0) * static_cast<float>(value);
397}
398
399// Returns the total acceleration (in any direction) or 0 if there's an error.
400float ReadAccelerometer() {
401 if (!accelerometer_inited) {
402 AccelerometerInit();
403 return 0;
404 }
405
406 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
407 const auto x = AccelerometerRead(0x29, end_micros);
408 const auto y = AccelerometerRead(0x2B, end_micros);
409 const auto z = AccelerometerRead(0x2D, end_micros);
410 if (!x.success || !y.success || !z.success) {
411 accelerometer_inited = false;
412 return 0;
413 }
414
415 const float x_g = AccelerometerConvert(x.result);
416 const float y_g = AccelerometerConvert(y.result);
417 const float z_g = AccelerometerConvert(z.result);
418 return ::std::sqrt(x_g * x_g + y_g * y_g + z_g * z_g);
419}
420
Brian Silvermana3a172b2018-03-24 03:53:32 -0400421extern "C" void ftm0_isr() {
422 while (true) {
423 const uint32_t status = FTM0->STATUS;
424 if (status == 0) {
425 return;
426 }
427
428 if (status & (1 << 1)) {
429 const uint32_t start = FTM0->C0V;
430 const uint32_t end = FTM0->C1V;
431 pwm_input_widths[0] = (end - start) & 0xFFFF;
432 pwm_input_times[0] = millis();
433 }
434 if (status & (1 << 7)) {
435 const uint32_t start = FTM0->C6V;
436 const uint32_t end = FTM0->C7V;
437 pwm_input_widths[1] = (end - start) & 0xFFFF;
438 pwm_input_times[1] = millis();
439 }
440 if (status & (1 << 5)) {
441 const uint32_t start = FTM0->C4V;
442 const uint32_t end = FTM0->C5V;
443 pwm_input_widths[2] = (end - start) & 0xFFFF;
444 pwm_input_times[2] = millis();
445 }
446 if (status & (1 << 3)) {
447 const uint32_t start = FTM0->C2V;
448 const uint32_t end = FTM0->C3V;
449 pwm_input_widths[4] = (end - start) & 0xFFFF;
450 pwm_input_times[4] = millis();
451 }
452
453 FTM0->STATUS = 0;
454 }
455}
456
457extern "C" void ftm3_isr() {
458 while (true) {
459 const uint32_t status = FTM3->STATUS;
460 if (status == 0) {
461 return;
462 }
463
464 if (status & (1 << 3)) {
465 const uint32_t start = FTM3->C2V;
466 const uint32_t end = FTM3->C3V;
467 pwm_input_widths[3] = (end - start) & 0xFFFF;
468 pwm_input_times[3] = millis();
469 }
Brian Silverman7f5f1442018-04-06 13:00:50 -0400470 if (status & (1 << 7)) {
471 const uint32_t start = FTM3->C6V;
472 const uint32_t end = FTM3->C7V;
473 pwm_input_widths[5] = (end - start) & 0xFFFF;
474 pwm_input_times[5] = millis();
475 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400476
477 FTM3->STATUS = 0;
478 }
479}
480
481float ConvertEncoderChannel(uint16_t reading) {
482 // Theoretical values based on the datasheet are 931 and 2917.
483 // With these values, the magnitude ranges from 0.99-1.03, which works fine
484 // (the encoder's output appears to get less accurate in one quadrant for some
485 // reason, hence the variation).
486 static constexpr uint16_t kMin = 802, kMax = 3088;
487 if (reading < kMin) {
488 reading = kMin;
489 } else if (reading > kMax) {
490 reading = kMax;
491 }
492 return (static_cast<float>(2 * (reading - kMin)) /
493 static_cast<float>(kMax - kMin)) -
494 1.0f;
495}
496
497struct EncoderReading {
Brian Silvermana96c1a42018-05-12 12:11:31 -0700498 EncoderReading(const SimpleAdcReadings &adc_readings) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400499 const float sin = ConvertEncoderChannel(adc_readings.sin);
500 const float cos = ConvertEncoderChannel(adc_readings.cos);
501
Austin Schuhbcce26a2018-03-26 23:41:24 -0700502 const float magnitude = hypot(sin, cos);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400503 const float magnitude_error = ::std::abs(magnitude - 1.0f);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700504 valid = magnitude_error < 0.30f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400505
506 angle = ::std::atan2(sin, cos);
507 }
508
509 // Angle in radians, in [-pi, pi].
510 float angle;
511
512 bool valid;
513};
514
515extern "C" void pit3_isr() {
516 PIT_TFLG3 = 1;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700517 PolyDrivetrain<float> *polydrivetrain =
518 global_polydrivetrain.load(::std::memory_order_acquire);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700519 Spring *spring = global_spring.load(::std::memory_order_acquire);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400520
Brian Silvermana96c1a42018-05-12 12:11:31 -0700521 SimpleAdcReadings adc_readings;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400522 {
523 DisableInterrupts disable_interrupts;
Brian Silvermana96c1a42018-05-12 12:11:31 -0700524 adc_readings = AdcReadSimple(disable_interrupts);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400525 }
526
527 EncoderReading encoder(adc_readings);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700528 static float last_good_encoder = 0.0f;
529 static int invalid_encoder_count = 0;
530 if (encoder.valid) {
531 last_good_encoder = encoder.angle;
532 invalid_encoder_count = 0;
533 } else {
534 ++invalid_encoder_count;
535 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400536
Austin Schuhe666dc62018-08-08 21:09:12 -0700537 const bool lost_spring_channel = lost_channel(2) || lost_channel(3) ||
538 lost_channel(4) || lost_channel(5) ||
539 (convert_input_width(4) < 0.5f);
540
541 const bool lost_drive_channel = lost_channel(0) || lost_channel(1) ||
542 (::std::abs(convert_input_width(4)) < 0.5f);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700543
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700544 if (polydrivetrain != nullptr && spring != nullptr) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700545 DrivetrainQueue_Goal goal;
546 goal.control_loop_driving = false;
Austin Schuhe666dc62018-08-08 21:09:12 -0700547 if (lost_drive_channel) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700548 goal.throttle = 0.0f;
549 goal.wheel = 0.0f;
550 } else {
551 goal.throttle = convert_input_width(1);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700552 goal.wheel = -convert_input_width(0);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700553 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700554 goal.quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
555
556 DrivetrainQueue_Output output;
557
558 polydrivetrain->SetGoal(goal);
559 polydrivetrain->Update();
560 polydrivetrain->SetOutput(&output);
561
562 vesc_set_duty(0, -output.left_voltage / 12.0f);
563 vesc_set_duty(1, -output.left_voltage / 12.0f);
564
565 vesc_set_duty(2, output.right_voltage / 12.0f);
566 vesc_set_duty(3, output.right_voltage / 12.0f);
567
Austin Schuhe666dc62018-08-08 21:09:12 -0700568 const bool prime = convert_input_width(2) > 0.1f;
569 const bool fire = convert_input_width(3) > 0.1f;
570 const bool force_move =
571 (convert_input_width(5) > 0.1f) && !lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700572
Austin Schuhe666dc62018-08-08 21:09:12 -0700573 bool unload = lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700574 static bool was_lost = true;
Austin Schuhe666dc62018-08-08 21:09:12 -0700575 bool force_reset = !lost_spring_channel && was_lost;
576 was_lost = lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700577
Austin Schuhe666dc62018-08-08 21:09:12 -0700578 spring->Iterate(unload, prime, fire, force_reset, force_move,
579 invalid_encoder_count <= 2, last_good_encoder);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700580
581 float spring_output = spring->output();
582
583 vesc_set_duty(4, -spring_output);
584 vesc_set_duty(5, spring_output);
585
Brian Silverman2de95d62018-03-31 12:32:24 -0700586 const float accelerometer = ReadAccelerometer();
587 (void)accelerometer;
588
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700589 /*
590 // Massive debug. Turn on for useful bits.
Brian Silverman2de95d62018-03-31 12:32:24 -0700591 printf("acc %d/1000\n", (int)(accelerometer / 1000));
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700592 if (!encoder.valid) {
593 printf("Stuck encoder: ADC %" PRIu16 " %" PRIu16
594 " enc %d/1000 %s mag %d\n",
595 adc_readings.sin, adc_readings.cos, (int)(encoder.angle * 1000),
596 encoder.valid ? "T" : "f",
597 (int)(hypot(ConvertEncoderChannel(adc_readings.sin),
598 ConvertEncoderChannel(adc_readings.cos)) *
599 1000));
600 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700601 static int i = 0;
602 ++i;
603 if (i > 20) {
604 i = 0;
Austin Schuhe666dc62018-08-08 21:09:12 -0700605 if (lost_spring_channel || lost_drive_channel) {
606 printf("200Hz loop, disabled %d %d %d %d %d %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700607 (int)(convert_input_width(0) * 1000),
608 (int)(convert_input_width(1) * 1000),
609 (int)(convert_input_width(2) * 1000),
610 (int)(convert_input_width(3) * 1000),
Austin Schuhe666dc62018-08-08 21:09:12 -0700611 (int)(convert_input_width(4) * 1000),
612 (int)(convert_input_width(5) * 1000));
Austin Schuhbcce26a2018-03-26 23:41:24 -0700613 } else {
614 printf(
Austin Schuhe666dc62018-08-08 21:09:12 -0700615 "TODO(Austin): 200Hz loop %d %d %d %d %d %d, lr, %d, %d velocity %d
616 "
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700617 " state: %d, near %d angle %d goal %d to: %d ADC %" PRIu16
618 " %" PRIu16 " enc %d/1000 %s from %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700619 (int)(convert_input_width(0) * 1000),
620 (int)(convert_input_width(1) * 1000),
621 (int)(convert_input_width(2) * 1000),
622 (int)(convert_input_width(3) * 1000),
623 (int)(convert_input_width(4) * 1000),
Austin Schuhe666dc62018-08-08 21:09:12 -0700624 (int)(convert_input_width(5) * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700625 static_cast<int>(output.left_voltage * 100),
626 static_cast<int>(output.right_voltage * 100),
627 static_cast<int>(polydrivetrain->velocity() * 100),
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700628 static_cast<int>(spring->state()), static_cast<int>(spring->Near()),
629 static_cast<int>(spring->angle() * 1000),
630 static_cast<int>(spring->goal() * 1000),
631 static_cast<int>(spring->timeout()), adc_readings.sin,
632 adc_readings.cos, (int)(encoder.angle * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700633 encoder.valid ? "T" : "f",
634 (int)(::std::sqrt(ConvertEncoderChannel(adc_readings.sin) *
635 ConvertEncoderChannel(adc_readings.sin) +
636 ConvertEncoderChannel(adc_readings.cos) *
637 ConvertEncoderChannel(adc_readings.cos)) *
638 1000));
639 }
640 }
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700641 */
Austin Schuhbcce26a2018-03-26 23:41:24 -0700642 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400643}
644
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700645} // namespace
646
647extern "C" {
648
649void *__stack_chk_guard = (void *)0x67111971;
650
651int _write(int /*file*/, char *ptr, int len) {
652 teensy::AcmTty *const tty = global_stdout.load(::std::memory_order_acquire);
653 if (tty != nullptr) {
654 return tty->Write(ptr, len);
655 }
656 return 0;
657}
658
659void __stack_chk_fail(void);
660
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700661} // extern "C"
662
663extern "C" int main(void) {
664 // for background about this startup delay, please see these conversations
665 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
666 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
667 delay(400);
668
669 // Set all interrupts to the second-lowest priority to start with.
670 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
671
672 // Now set priorities for all the ones we care about. They only have meaning
673 // relative to each other, which means centralizing them here makes it a lot
674 // more manageable.
675 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400676 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
677 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
678 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700679
680 // Builtin LED.
681 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
682 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
683 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
684
685 // Set up the CAN pins.
686 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
687 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
688
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700689 // PWM_IN0
690 // FTM0_CH0
691 PORTC_PCR1 = PORT_PCR_MUX(4);
692
693 // PWM_IN1
694 // FTM0_CH6
695 PORTD_PCR6 = PORT_PCR_MUX(4);
696
697 // PWM_IN2
698 // FTM0_CH4
699 PORTD_PCR4 = PORT_PCR_MUX(4);
700
701 // PWM_IN3
702 // FTM3_CH2
703 PORTD_PCR2 = PORT_PCR_MUX(4);
704
705 // PWM_IN4
706 // FTM0_CH2
707 PORTC_PCR3 = PORT_PCR_MUX(4);
708
Brian Silverman7f5f1442018-04-06 13:00:50 -0400709 // PWM_IN5
710 // FTM3_CH6
711 PORTC_PCR10 = PORT_PCR_MUX(3);
712
Brian Silverman2de95d62018-03-31 12:32:24 -0700713 // SPI0
714 // ACC_CS PCS0
715 PORTA_PCR14 = PORT_PCR_DSE | PORT_PCR_MUX(2);
716 // SCK
717 PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(2);
718 // MOSI
719 PORTA_PCR16 = PORT_PCR_DSE | PORT_PCR_MUX(2);
720 // MISO
721 PORTA_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(2);
722
723 SIM_SCGC6 |= SIM_SCGC6_SPI0;
724 SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(1) | SPI_MCR_CLR_TXF |
725 SPI_MCR_CLR_RXF | SPI_MCR_HALT;
726 // 60 MHz "protocol clock"
727 // 6ns CS setup
728 // 8ns CS hold
729 SPI0_CTAR0 = SPI_CTAR_FMSZ(15) | SPI_CTAR_CPOL /* Clock idles high */ |
730 SPI_CTAR_CPHA /* Data captured on trailing edge */ |
731 0 /* !LSBFE MSB first */ |
732 SPI_CTAR_PCSSCK(0) /* PCS->SCK prescaler = 1 */ |
733 SPI_CTAR_PASC(0) /* SCK->PCS prescaler = 1 */ |
734 SPI_CTAR_PDT(0) /* PCS->PCS prescaler = 1 */ |
735 SPI_CTAR_PBR(0) /* baud prescaler = 1 */ |
736 SPI_CTAR_CSSCK(0) /* PCS->SCK 2/60MHz = 33.33ns */ |
737 SPI_CTAR_ASC(0) /* SCK->PCS 2/60MHz = 33.33ns */ |
738 SPI_CTAR_DT(2) /* PCS->PSC 8/60MHz = 133.33ns */ |
739 SPI_CTAR_BR(2) /* BR 60MHz/6 = 10MHz */;
740
741 SPI0_MCR &= ~SPI_MCR_HALT;
742
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700743 delay(100);
744
745 teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
746 usb_device.SetManufacturer("Seems Reasonable LLC");
747 usb_device.SetProduct("Simple Receiver Board");
748
749 teensy::AcmTty tty0(&usb_device);
750 global_stdout.store(&tty0, ::std::memory_order_release);
751 usb_device.Initialize();
752
Brian Silvermana3a172b2018-03-24 03:53:32 -0400753 SIM_SCGC6 |= SIM_SCGC6_PIT;
754 // Workaround for errata e7914.
755 (void)PIT_MCR;
756 PIT_MCR = 0;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700757 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400758 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
759
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700760 can_init(0, 1);
Brian Silvermana96c1a42018-05-12 12:11:31 -0700761 AdcInitSimple();
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700762 SetupPwmFtm(FTM0);
763 SetupPwmFtm(FTM3);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700764
Austin Schuhbcce26a2018-03-26 23:41:24 -0700765 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
766 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700767 Spring spring;
768 global_spring.store(&spring, ::std::memory_order_release);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700769
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700770 // Leave the LEDs on for a bit longer.
771 delay(300);
772 printf("Done starting up\n");
773
Brian Silverman2de95d62018-03-31 12:32:24 -0700774 AccelerometerInit();
775 printf("Accelerometer init %s\n", accelerometer_inited ? "success" : "fail");
776
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700777 // Done starting up, now turn the LED off.
778 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
779
Brian Silvermana3a172b2018-03-24 03:53:32 -0400780 NVIC_ENABLE_IRQ(IRQ_FTM0);
781 NVIC_ENABLE_IRQ(IRQ_FTM3);
782 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
Austin Schuhe666dc62018-08-08 21:09:12 -0700783 printf("Done starting up2\n");
Brian Silvermana3a172b2018-03-24 03:53:32 -0400784
Austin Schuhe666dc62018-08-08 21:09:12 -0700785 //DoReceiverTest2();
Austin Schuhbcce26a2018-03-26 23:41:24 -0700786 while (true) {
787 }
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700788
789 return 0;
790}
791
792void __stack_chk_fail(void) {
793 while (true) {
794 GPIOC_PSOR = (1 << 5);
795 printf("Stack corruption detected\n");
796 delay(1000);
797 GPIOC_PCOR = (1 << 5);
798 delay(1000);
799 }
800}
801
802} // namespace motors
803} // namespace frc971