blob: 91e22291cba9f4a8364f2e85d00fa351fcd411a4 [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>
Austin Schuhbb735b72019-01-03 12:58:41 -08006#include <chrono>
Brian Silverman54dd2fe2018-03-16 23:44:31 -07007#include <cmath>
8
Austin Schuh4fae0fc2018-03-27 23:51:42 -07009#include "frc971/control_loops/drivetrain/polydrivetrain.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070010#include "motors/core/kinetis.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040011#include "motors/core/time.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070012#include "motors/peripheral/adc.h"
13#include "motors/peripheral/can.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040014#include "motors/peripheral/configuration.h"
Brian Silverman4787a6e2018-10-06 16:00:54 -070015#include "motors/print/print.h"
Austin Schuh4fae0fc2018-03-27 23:51:42 -070016#include "motors/seems_reasonable/drivetrain_dog_motor_plant.h"
17#include "motors/seems_reasonable/polydrivetrain_dog_motor_plant.h"
18#include "motors/seems_reasonable/spring.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
Austin Schuhbb735b72019-01-03 12:58:41 -080032namespace chrono = ::std::chrono;
33
Brian Silverman9ed2cf12018-05-12 13:06:38 -070034struct SimpleAdcReadings {
35 uint16_t sin, cos;
36};
37
38void AdcInitSimple() {
39 AdcInitCommon();
40
41 // ENC_SIN ADC0_SE23
42 // ENC_COS ADC1_SE23
43}
44
45SimpleAdcReadings AdcReadSimple(const DisableInterrupts &) {
46 SimpleAdcReadings r;
47
48 ADC0_SC1A = 23;
49 ADC1_SC1A = 23;
50 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
51 }
52 while (!(ADC1_SC1A & ADC_SC1_COCO)) {
53 }
54 r.sin = ADC0_RA;
55 r.cos = ADC1_RA;
56
57 return r;
58}
59
Austin Schuhbcce26a2018-03-26 23:41:24 -070060const ShifterHallEffect kThreeStateDriveShifter{0.0, 0.0, 0.25, 0.75};
61
62const DrivetrainConfig<float> &GetDrivetrainConfig() {
63 static DrivetrainConfig<float> kDrivetrainConfig{
64 ::frc971::control_loops::drivetrain::ShifterType::NO_SHIFTER,
65 ::frc971::control_loops::drivetrain::LoopType::OPEN_LOOP,
66 ::frc971::control_loops::drivetrain::GyroType::SPARTAN_GYRO,
67 ::frc971::control_loops::drivetrain::IMUType::IMU_X,
68
69 ::motors::seems_reasonable::MakeDrivetrainLoop,
70 ::motors::seems_reasonable::MakeVelocityDrivetrainLoop,
71 ::std::function<StateFeedbackLoop<7, 2, 4, float>()>(),
72
Austin Schuhbb735b72019-01-03 12:58:41 -080073 chrono::duration_cast<chrono::nanoseconds>(
74 chrono::duration<float>(::motors::seems_reasonable::kDt)),
75 ::motors::seems_reasonable::kRobotRadius,
Austin Schuhbcce26a2018-03-26 23:41:24 -070076 ::motors::seems_reasonable::kWheelRadius, ::motors::seems_reasonable::kV,
77
78 ::motors::seems_reasonable::kHighGearRatio,
79 ::motors::seems_reasonable::kLowGearRatio, kThreeStateDriveShifter,
80 kThreeStateDriveShifter, true /* default_high_gear */,
81 0 /* down_offset if using constants use
82 constants::GetValues().down_error */, 0.8 /* wheel_non_linearity */,
83 1.2 /* quickturn_wheel_multiplier */, 1.5 /* wheel_multiplier */,
84 };
85
86 return kDrivetrainConfig;
87};
88
89
Austin Schuhbcce26a2018-03-26 23:41:24 -070090::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
Austin Schuh4fae0fc2018-03-27 23:51:42 -070091::std::atomic<Spring *> global_spring{nullptr};
Austin Schuhbcce26a2018-03-26 23:41:24 -070092
Brian Silvermana3a172b2018-03-24 03:53:32 -040093// Last width we received on each channel.
Brian Silverman7f5f1442018-04-06 13:00:50 -040094uint16_t pwm_input_widths[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -040095// When we received a pulse on each channel in milliseconds.
Brian Silverman7f5f1442018-04-06 13:00:50 -040096uint32_t pwm_input_times[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -040097
Austin Schuhbcce26a2018-03-26 23:41:24 -070098constexpr int kChannelTimeout = 100;
99
100bool lost_channel(int channel) {
101 DisableInterrupts disable_interrupts;
102 if (time_after(millis(),
103 time_add(pwm_input_times[channel], kChannelTimeout))) {
104 return true;
105 }
106 return false;
107}
108
Brian Silvermana3a172b2018-03-24 03:53:32 -0400109// Returns the most recently captured value for the specified input channel
110// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
111float convert_input_width(int channel) {
112 uint16_t width;
113 {
114 DisableInterrupts disable_interrupts;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700115 if (time_after(millis(),
116 time_add(pwm_input_times[channel], kChannelTimeout))) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400117 return 0;
118 }
119
120 width = pwm_input_widths[channel];
121 }
122
123 // Values measured with a channel mapped to a button.
124 static constexpr uint16_t kMinWidth = 4133;
125 static constexpr uint16_t kMaxWidth = 7177;
126 if (width < kMinWidth) {
127 width = kMinWidth;
128 } else if (width > kMaxWidth) {
129 width = kMaxWidth;
130 }
131 return (static_cast<float>(2 * (width - kMinWidth)) /
132 static_cast<float>(kMaxWidth - kMinWidth)) -
133 1.0f;
134}
135
136// Sends a SET_RPM command to the specified VESC.
137// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
138// bandwidth.
139void vesc_set_rpm(int vesc_id, float rpm) {
140 const int32_t rpm_int = rpm;
141 uint32_t id = CAN_EFF_FLAG;
142 id |= vesc_id;
143 id |= (0x03 /* SET_RPM */) << 8;
144 uint8_t data[4] = {
145 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
146 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
147 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
148 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
149 };
150 can_send(id, data, sizeof(data), 2 + vesc_id);
151}
152
153// Sends a SET_CURRENT command to the specified VESC.
154// current is in amps.
155// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
156// bandwidth.
157void vesc_set_current(int vesc_id, float current) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700158 constexpr float kMaxCurrent = 80.0f;
159 const int32_t current_int =
160 ::std::max(-kMaxCurrent, ::std::min(kMaxCurrent, current)) * 1000.0f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400161 uint32_t id = CAN_EFF_FLAG;
162 id |= vesc_id;
163 id |= (0x01 /* SET_CURRENT */) << 8;
164 uint8_t data[4] = {
165 static_cast<uint8_t>((current_int >> 24) & 0xFF),
166 static_cast<uint8_t>((current_int >> 16) & 0xFF),
167 static_cast<uint8_t>((current_int >> 8) & 0xFF),
168 static_cast<uint8_t>((current_int >> 0) & 0xFF),
169 };
170 can_send(id, data, sizeof(data), 2 + vesc_id);
171}
172
Brian Silverman4d1e5272018-03-26 03:18:42 -0400173// Sends a SET_DUTY command to the specified VESC.
174// duty is from -1 to 1.
175// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
176// bandwidth.
177void vesc_set_duty(int vesc_id, float duty) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700178 constexpr int32_t kMaxDuty = 99999;
179 const int32_t duty_int = ::std::max(
180 -kMaxDuty, ::std::min(kMaxDuty, static_cast<int32_t>(duty * 100000.0f)));
Brian Silverman4d1e5272018-03-26 03:18:42 -0400181 uint32_t id = CAN_EFF_FLAG;
182 id |= vesc_id;
183 id |= (0x00 /* SET_DUTY */) << 8;
184 uint8_t data[4] = {
185 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
186 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
187 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
188 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
189 };
190 can_send(id, data, sizeof(data), 2 + vesc_id);
191}
192
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700193// TODO(Brian): Move these two test functions somewhere else.
194__attribute__((unused)) void DoVescTest() {
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700195 uint32_t time = micros();
196 while (true) {
197 for (int i = 0; i < 6; ++i) {
198 const uint32_t end = time_add(time, 500000);
199 while (true) {
200 const bool done = time_after(micros(), end);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700201 float current;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700202 if (done) {
203 current = -6;
204 } else {
205 current = 6;
206 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400207 vesc_set_current(i, current);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700208 if (done) {
209 break;
210 }
211 delay(5);
212 }
213 time = end;
214 }
215 }
216}
217
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700218__attribute__((unused)) void DoReceiverTest2() {
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700219 static constexpr float kMaxRpm = 10000.0f;
220 while (true) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400221 const bool flip = convert_input_width(2) > 0;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700222
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700223 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400224 const float value = convert_input_width(0);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700225
226 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400227 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700228 if (flip) {
229 rpm *= -1.0f;
230 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400231 vesc_set_rpm(0, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700232 }
233
234 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400235 float rpm = ::std::max(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(1, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700240 }
241 }
242
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700243 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400244 const float value = convert_input_width(1);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700245
246 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400247 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700248 if (flip) {
249 rpm *= -1.0f;
250 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400251 vesc_set_rpm(2, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700252 }
253
254 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400255 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700256 if (flip) {
257 rpm *= -1.0f;
258 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400259 vesc_set_rpm(3, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700260 }
261 }
262
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700263 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400264 const float value = convert_input_width(4);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700265
266 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400267 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700268 if (flip) {
269 rpm *= -1.0f;
270 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400271 vesc_set_rpm(4, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700272 }
273
274 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400275 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700276 if (flip) {
277 rpm *= -1.0f;
278 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400279 vesc_set_rpm(5, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700280 }
281 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400282 // Give the CAN frames a chance to go out.
283 delay(5);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700284 }
285}
286
287void SetupPwmFtm(BigFTM *ftm) {
288 ftm->MODE = FTM_MODE_WPDIS;
289 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
290 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
291
292 // Can't change MOD according to the reference manual ("The Dual Edge Capture
293 // mode must be used with ... the FTM counter in Free running counter.").
294 ftm->MOD = 0xFFFF;
295
296 // Capturing rising edge.
297 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
298 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400299 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700300
301 // Capturing rising edge.
302 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
303 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400304 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700305
306 // Capturing rising edge.
307 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
308 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400309 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700310
311 // Capturing rising edge.
312 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
313 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400314 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700315
Brian Silvermana3a172b2018-03-24 03:53:32 -0400316 (void)ftm->STATUS;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700317 ftm->STATUS = 0x00;
318
319 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
320 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
321 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
322 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
323
324 // 34.95ms max period before it starts wrapping and being weird.
325 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
326 FTM_SC_PS(4) /* Prescaler=32 */;
327
328 ftm->MODE &= ~FTM_MODE_WPDIS;
329}
330
Brian Silverman2de95d62018-03-31 12:32:24 -0700331struct AccelerometerResult {
332 uint16_t result;
333 bool success;
334};
335
336// Does a transfer on the accelerometer. Returns the resulting frame, or a
337// failure if it takes until after end_micros.
338AccelerometerResult AccelerometerTransfer(uint16_t data, uint32_t end_micros) {
339 SPI0_SR = SPI_SR_RFDF;
340 SPI0_PUSHR = SPI_PUSHR_PCS(1) | data;
341
342 while (!(SPI0_SR & SPI_SR_RFDF)) {
343 if (time_after(micros(), end_micros)) {
344 return {0, false};
345 }
346 }
347 const uint32_t popr = SPI0_POPR;
348 SPI0_SR = SPI_SR_RFDF;
349 return {static_cast<uint16_t>(popr & 0xFFFF), true};
350}
351
352constexpr uint32_t kAccelerometerTimeout = 500;
353
354bool AccelerometerWrite(uint8_t address, uint8_t data, uint32_t end_micros) {
355 const AccelerometerResult r = AccelerometerTransfer(
356 (static_cast<uint16_t>(address) << 8) | static_cast<uint16_t>(data),
357 end_micros);
358 return r.success;
359}
360
361AccelerometerResult AccelerometerRead(uint8_t address, uint32_t end_micros) {
362 AccelerometerResult r = AccelerometerTransfer(
363 (static_cast<uint16_t>(address) << 8) | UINT16_C(0x8000), end_micros);
364 r.result = r.result & UINT16_C(0xFF);
365 return r;
366}
367
368bool accelerometer_inited = false;
369
370void AccelerometerInit() {
371 accelerometer_inited = false;
372 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
373 {
374 const auto who_am_i = AccelerometerRead(0xF, end_micros);
375 if (!who_am_i.success) {
376 return;
377 }
378 if (who_am_i.result != 0x32) {
379 return;
380 }
381 }
382 if (!AccelerometerWrite(
383 0x20, (1 << 5) /* Normal mode */ | (1 << 3) /* 100 Hz */ |
384 (1 << 2) /* Z enabled */ | (1 << 1) /* Y enabled */ |
385 (1 << 0) /* X enabled */,
386 end_micros)) {
387 }
388 // If want to read LSB, need to enable BDU to avoid splitting reads.
389 if (!AccelerometerWrite(0x23, (0 << 6) /* Data LSB at lower address */ |
390 (3 << 4) /* 400g full scale */ |
391 (0 << 0) /* 4-wire interface */,
392 end_micros)) {
393 }
394 accelerometer_inited = true;
395}
396
397float AccelerometerConvert(uint16_t value) {
398 return static_cast<float>(400.0 / 65536.0) * static_cast<float>(value);
399}
400
401// Returns the total acceleration (in any direction) or 0 if there's an error.
402float ReadAccelerometer() {
403 if (!accelerometer_inited) {
404 AccelerometerInit();
405 return 0;
406 }
407
408 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
409 const auto x = AccelerometerRead(0x29, end_micros);
410 const auto y = AccelerometerRead(0x2B, end_micros);
411 const auto z = AccelerometerRead(0x2D, end_micros);
412 if (!x.success || !y.success || !z.success) {
413 accelerometer_inited = false;
414 return 0;
415 }
416
417 const float x_g = AccelerometerConvert(x.result);
418 const float y_g = AccelerometerConvert(y.result);
419 const float z_g = AccelerometerConvert(z.result);
420 return ::std::sqrt(x_g * x_g + y_g * y_g + z_g * z_g);
421}
422
Brian Silvermana3a172b2018-03-24 03:53:32 -0400423extern "C" void ftm0_isr() {
424 while (true) {
425 const uint32_t status = FTM0->STATUS;
426 if (status == 0) {
427 return;
428 }
429
430 if (status & (1 << 1)) {
431 const uint32_t start = FTM0->C0V;
432 const uint32_t end = FTM0->C1V;
433 pwm_input_widths[0] = (end - start) & 0xFFFF;
434 pwm_input_times[0] = millis();
435 }
436 if (status & (1 << 7)) {
437 const uint32_t start = FTM0->C6V;
438 const uint32_t end = FTM0->C7V;
439 pwm_input_widths[1] = (end - start) & 0xFFFF;
440 pwm_input_times[1] = millis();
441 }
442 if (status & (1 << 5)) {
443 const uint32_t start = FTM0->C4V;
444 const uint32_t end = FTM0->C5V;
445 pwm_input_widths[2] = (end - start) & 0xFFFF;
446 pwm_input_times[2] = millis();
447 }
448 if (status & (1 << 3)) {
449 const uint32_t start = FTM0->C2V;
450 const uint32_t end = FTM0->C3V;
451 pwm_input_widths[4] = (end - start) & 0xFFFF;
452 pwm_input_times[4] = millis();
453 }
454
455 FTM0->STATUS = 0;
456 }
457}
458
459extern "C" void ftm3_isr() {
460 while (true) {
461 const uint32_t status = FTM3->STATUS;
462 if (status == 0) {
463 return;
464 }
465
466 if (status & (1 << 3)) {
467 const uint32_t start = FTM3->C2V;
468 const uint32_t end = FTM3->C3V;
469 pwm_input_widths[3] = (end - start) & 0xFFFF;
470 pwm_input_times[3] = millis();
471 }
Brian Silverman7f5f1442018-04-06 13:00:50 -0400472 if (status & (1 << 7)) {
473 const uint32_t start = FTM3->C6V;
474 const uint32_t end = FTM3->C7V;
475 pwm_input_widths[5] = (end - start) & 0xFFFF;
476 pwm_input_times[5] = millis();
477 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400478
479 FTM3->STATUS = 0;
480 }
481}
482
483float ConvertEncoderChannel(uint16_t reading) {
484 // Theoretical values based on the datasheet are 931 and 2917.
485 // With these values, the magnitude ranges from 0.99-1.03, which works fine
486 // (the encoder's output appears to get less accurate in one quadrant for some
487 // reason, hence the variation).
488 static constexpr uint16_t kMin = 802, kMax = 3088;
489 if (reading < kMin) {
490 reading = kMin;
491 } else if (reading > kMax) {
492 reading = kMax;
493 }
494 return (static_cast<float>(2 * (reading - kMin)) /
495 static_cast<float>(kMax - kMin)) -
496 1.0f;
497}
498
499struct EncoderReading {
Brian Silvermana96c1a42018-05-12 12:11:31 -0700500 EncoderReading(const SimpleAdcReadings &adc_readings) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400501 const float sin = ConvertEncoderChannel(adc_readings.sin);
502 const float cos = ConvertEncoderChannel(adc_readings.cos);
503
Austin Schuhbcce26a2018-03-26 23:41:24 -0700504 const float magnitude = hypot(sin, cos);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400505 const float magnitude_error = ::std::abs(magnitude - 1.0f);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700506 valid = magnitude_error < 0.30f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400507
508 angle = ::std::atan2(sin, cos);
509 }
510
511 // Angle in radians, in [-pi, pi].
512 float angle;
513
514 bool valid;
515};
516
517extern "C" void pit3_isr() {
518 PIT_TFLG3 = 1;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700519 PolyDrivetrain<float> *polydrivetrain =
520 global_polydrivetrain.load(::std::memory_order_acquire);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700521 Spring *spring = global_spring.load(::std::memory_order_acquire);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400522
Brian Silvermana96c1a42018-05-12 12:11:31 -0700523 SimpleAdcReadings adc_readings;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400524 {
525 DisableInterrupts disable_interrupts;
Brian Silvermana96c1a42018-05-12 12:11:31 -0700526 adc_readings = AdcReadSimple(disable_interrupts);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400527 }
528
529 EncoderReading encoder(adc_readings);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700530 static float last_good_encoder = 0.0f;
531 static int invalid_encoder_count = 0;
532 if (encoder.valid) {
533 last_good_encoder = encoder.angle;
534 invalid_encoder_count = 0;
535 } else {
536 ++invalid_encoder_count;
537 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400538
Austin Schuhe666dc62018-08-08 21:09:12 -0700539 const bool lost_spring_channel = lost_channel(2) || lost_channel(3) ||
540 lost_channel(4) || lost_channel(5) ||
541 (convert_input_width(4) < 0.5f);
542
543 const bool lost_drive_channel = lost_channel(0) || lost_channel(1) ||
544 (::std::abs(convert_input_width(4)) < 0.5f);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700545
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700546 if (polydrivetrain != nullptr && spring != nullptr) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700547 DrivetrainQueue_Goal goal;
548 goal.control_loop_driving = false;
Austin Schuhe666dc62018-08-08 21:09:12 -0700549 if (lost_drive_channel) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700550 goal.throttle = 0.0f;
551 goal.wheel = 0.0f;
552 } else {
553 goal.throttle = convert_input_width(1);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700554 goal.wheel = -convert_input_width(0);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700555 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700556 goal.quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
557
558 DrivetrainQueue_Output output;
559
560 polydrivetrain->SetGoal(goal);
561 polydrivetrain->Update();
562 polydrivetrain->SetOutput(&output);
563
564 vesc_set_duty(0, -output.left_voltage / 12.0f);
565 vesc_set_duty(1, -output.left_voltage / 12.0f);
566
567 vesc_set_duty(2, output.right_voltage / 12.0f);
568 vesc_set_duty(3, output.right_voltage / 12.0f);
569
Austin Schuhe666dc62018-08-08 21:09:12 -0700570 const bool prime = convert_input_width(2) > 0.1f;
571 const bool fire = convert_input_width(3) > 0.1f;
572 const bool force_move =
573 (convert_input_width(5) > 0.1f) && !lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700574
Austin Schuhe666dc62018-08-08 21:09:12 -0700575 bool unload = lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700576 static bool was_lost = true;
Austin Schuhe666dc62018-08-08 21:09:12 -0700577 bool force_reset = !lost_spring_channel && was_lost;
578 was_lost = lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700579
Austin Schuhe666dc62018-08-08 21:09:12 -0700580 spring->Iterate(unload, prime, fire, force_reset, force_move,
581 invalid_encoder_count <= 2, last_good_encoder);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700582
583 float spring_output = spring->output();
584
585 vesc_set_duty(4, -spring_output);
586 vesc_set_duty(5, spring_output);
587
Brian Silverman2de95d62018-03-31 12:32:24 -0700588 const float accelerometer = ReadAccelerometer();
589 (void)accelerometer;
590
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700591 /*
592 // Massive debug. Turn on for useful bits.
Brian Silverman2de95d62018-03-31 12:32:24 -0700593 printf("acc %d/1000\n", (int)(accelerometer / 1000));
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700594 if (!encoder.valid) {
595 printf("Stuck encoder: ADC %" PRIu16 " %" PRIu16
596 " enc %d/1000 %s mag %d\n",
597 adc_readings.sin, adc_readings.cos, (int)(encoder.angle * 1000),
598 encoder.valid ? "T" : "f",
599 (int)(hypot(ConvertEncoderChannel(adc_readings.sin),
600 ConvertEncoderChannel(adc_readings.cos)) *
601 1000));
602 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700603 static int i = 0;
604 ++i;
605 if (i > 20) {
606 i = 0;
Austin Schuhe666dc62018-08-08 21:09:12 -0700607 if (lost_spring_channel || lost_drive_channel) {
608 printf("200Hz loop, disabled %d %d %d %d %d %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700609 (int)(convert_input_width(0) * 1000),
610 (int)(convert_input_width(1) * 1000),
611 (int)(convert_input_width(2) * 1000),
612 (int)(convert_input_width(3) * 1000),
Austin Schuhe666dc62018-08-08 21:09:12 -0700613 (int)(convert_input_width(4) * 1000),
614 (int)(convert_input_width(5) * 1000));
Austin Schuhbcce26a2018-03-26 23:41:24 -0700615 } else {
616 printf(
Austin Schuhe666dc62018-08-08 21:09:12 -0700617 "TODO(Austin): 200Hz loop %d %d %d %d %d %d, lr, %d, %d velocity %d
618 "
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700619 " state: %d, near %d angle %d goal %d to: %d ADC %" PRIu16
620 " %" PRIu16 " enc %d/1000 %s from %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700621 (int)(convert_input_width(0) * 1000),
622 (int)(convert_input_width(1) * 1000),
623 (int)(convert_input_width(2) * 1000),
624 (int)(convert_input_width(3) * 1000),
625 (int)(convert_input_width(4) * 1000),
Austin Schuhe666dc62018-08-08 21:09:12 -0700626 (int)(convert_input_width(5) * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700627 static_cast<int>(output.left_voltage * 100),
628 static_cast<int>(output.right_voltage * 100),
629 static_cast<int>(polydrivetrain->velocity() * 100),
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700630 static_cast<int>(spring->state()), static_cast<int>(spring->Near()),
631 static_cast<int>(spring->angle() * 1000),
632 static_cast<int>(spring->goal() * 1000),
633 static_cast<int>(spring->timeout()), adc_readings.sin,
634 adc_readings.cos, (int)(encoder.angle * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700635 encoder.valid ? "T" : "f",
636 (int)(::std::sqrt(ConvertEncoderChannel(adc_readings.sin) *
637 ConvertEncoderChannel(adc_readings.sin) +
638 ConvertEncoderChannel(adc_readings.cos) *
639 ConvertEncoderChannel(adc_readings.cos)) *
640 1000));
641 }
642 }
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700643 */
Austin Schuhbcce26a2018-03-26 23:41:24 -0700644 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400645}
646
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700647} // namespace
648
649extern "C" {
650
651void *__stack_chk_guard = (void *)0x67111971;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700652void __stack_chk_fail(void);
653
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700654} // extern "C"
655
656extern "C" int main(void) {
657 // for background about this startup delay, please see these conversations
658 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
659 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
660 delay(400);
661
662 // Set all interrupts to the second-lowest priority to start with.
663 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
664
665 // Now set priorities for all the ones we care about. They only have meaning
666 // relative to each other, which means centralizing them here makes it a lot
667 // more manageable.
668 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400669 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
670 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
671 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700672
673 // Builtin LED.
674 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
675 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
676 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
677
678 // Set up the CAN pins.
679 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
680 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
681
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700682 // PWM_IN0
683 // FTM0_CH0
684 PORTC_PCR1 = PORT_PCR_MUX(4);
685
686 // PWM_IN1
687 // FTM0_CH6
688 PORTD_PCR6 = PORT_PCR_MUX(4);
689
690 // PWM_IN2
691 // FTM0_CH4
692 PORTD_PCR4 = PORT_PCR_MUX(4);
693
694 // PWM_IN3
695 // FTM3_CH2
696 PORTD_PCR2 = PORT_PCR_MUX(4);
697
698 // PWM_IN4
699 // FTM0_CH2
700 PORTC_PCR3 = PORT_PCR_MUX(4);
701
Brian Silverman7f5f1442018-04-06 13:00:50 -0400702 // PWM_IN5
703 // FTM3_CH6
704 PORTC_PCR10 = PORT_PCR_MUX(3);
705
Brian Silverman2de95d62018-03-31 12:32:24 -0700706 // SPI0
707 // ACC_CS PCS0
708 PORTA_PCR14 = PORT_PCR_DSE | PORT_PCR_MUX(2);
709 // SCK
710 PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(2);
711 // MOSI
712 PORTA_PCR16 = PORT_PCR_DSE | PORT_PCR_MUX(2);
713 // MISO
714 PORTA_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(2);
715
716 SIM_SCGC6 |= SIM_SCGC6_SPI0;
717 SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(1) | SPI_MCR_CLR_TXF |
718 SPI_MCR_CLR_RXF | SPI_MCR_HALT;
719 // 60 MHz "protocol clock"
720 // 6ns CS setup
721 // 8ns CS hold
722 SPI0_CTAR0 = SPI_CTAR_FMSZ(15) | SPI_CTAR_CPOL /* Clock idles high */ |
723 SPI_CTAR_CPHA /* Data captured on trailing edge */ |
724 0 /* !LSBFE MSB first */ |
725 SPI_CTAR_PCSSCK(0) /* PCS->SCK prescaler = 1 */ |
726 SPI_CTAR_PASC(0) /* SCK->PCS prescaler = 1 */ |
727 SPI_CTAR_PDT(0) /* PCS->PCS prescaler = 1 */ |
728 SPI_CTAR_PBR(0) /* baud prescaler = 1 */ |
729 SPI_CTAR_CSSCK(0) /* PCS->SCK 2/60MHz = 33.33ns */ |
730 SPI_CTAR_ASC(0) /* SCK->PCS 2/60MHz = 33.33ns */ |
731 SPI_CTAR_DT(2) /* PCS->PSC 8/60MHz = 133.33ns */ |
732 SPI_CTAR_BR(2) /* BR 60MHz/6 = 10MHz */;
733
734 SPI0_MCR &= ~SPI_MCR_HALT;
735
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700736 delay(100);
737
Brian Silverman4787a6e2018-10-06 16:00:54 -0700738 PrintingParameters printing_parameters;
739 printing_parameters.dedicated_usb = true;
740 const ::std::unique_ptr<PrintingImplementation> printing =
741 CreatePrinting(printing_parameters);
742 printing->Initialize();
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700743
Brian Silvermana3a172b2018-03-24 03:53:32 -0400744 SIM_SCGC6 |= SIM_SCGC6_PIT;
745 // Workaround for errata e7914.
746 (void)PIT_MCR;
747 PIT_MCR = 0;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700748 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400749 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
750
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700751 can_init(0, 1);
Brian Silvermana96c1a42018-05-12 12:11:31 -0700752 AdcInitSimple();
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700753 SetupPwmFtm(FTM0);
754 SetupPwmFtm(FTM3);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700755
Austin Schuhbcce26a2018-03-26 23:41:24 -0700756 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
757 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700758 Spring spring;
759 global_spring.store(&spring, ::std::memory_order_release);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700760
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700761 // Leave the LEDs on for a bit longer.
762 delay(300);
763 printf("Done starting up\n");
764
Brian Silverman2de95d62018-03-31 12:32:24 -0700765 AccelerometerInit();
766 printf("Accelerometer init %s\n", accelerometer_inited ? "success" : "fail");
767
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700768 // Done starting up, now turn the LED off.
769 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
770
Brian Silvermana3a172b2018-03-24 03:53:32 -0400771 NVIC_ENABLE_IRQ(IRQ_FTM0);
772 NVIC_ENABLE_IRQ(IRQ_FTM3);
773 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
Austin Schuhe666dc62018-08-08 21:09:12 -0700774 printf("Done starting up2\n");
Brian Silvermana3a172b2018-03-24 03:53:32 -0400775
Austin Schuhe666dc62018-08-08 21:09:12 -0700776 //DoReceiverTest2();
Austin Schuhbcce26a2018-03-26 23:41:24 -0700777 while (true) {
778 }
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700779
780 return 0;
781}
782
783void __stack_chk_fail(void) {
784 while (true) {
785 GPIOC_PSOR = (1 << 5);
786 printf("Stack corruption detected\n");
787 delay(1000);
788 GPIOC_PCOR = (1 << 5);
789 delay(1000);
790 }
791}
792
793} // namespace motors
794} // namespace frc971