blob: 2ae0c9bc34b25e3ab8ffbb034129c303997f088a [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>
Philipp Schrader790cb542023-07-05 21:06:52 -07005
Brian Silverman54dd2fe2018-03-16 23:44:31 -07006#include <atomic>
Austin Schuhbb735b72019-01-03 12:58:41 -08007#include <chrono>
Brian Silverman54dd2fe2018-03-16 23:44:31 -07008#include <cmath>
9
Austin Schuh4fae0fc2018-03-27 23:51:42 -070010#include "frc971/control_loops/drivetrain/polydrivetrain.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070011#include "motors/core/kinetis.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040012#include "motors/core/time.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070013#include "motors/peripheral/adc.h"
14#include "motors/peripheral/can.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040015#include "motors/peripheral/configuration.h"
Brian Silverman4787a6e2018-10-06 16:00:54 -070016#include "motors/print/print.h"
Austin Schuh4fae0fc2018-03-27 23:51:42 -070017#include "motors/seems_reasonable/drivetrain_dog_motor_plant.h"
18#include "motors/seems_reasonable/polydrivetrain_dog_motor_plant.h"
19#include "motors/seems_reasonable/spring.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070020#include "motors/util.h"
21
Stephan Pleinesf63bde82024-01-13 15:59:33 -080022namespace frc971::motors {
Brian Silverman54dd2fe2018-03-16 23:44:31 -070023namespace {
24
Austin Schuhbcce26a2018-03-26 23:41:24 -070025using ::frc971::constants::ShifterHallEffect;
Alex Perrycb7da4b2019-08-28 19:35:56 -070026using ::frc971::control_loops::drivetrain::DrivetrainConfig;
27using ::frc971::control_loops::drivetrain::OutputT;
28using ::frc971::control_loops::drivetrain::PolyDrivetrain;
Austin Schuh4fae0fc2018-03-27 23:51:42 -070029using ::motors::seems_reasonable::Spring;
Austin Schuhbcce26a2018-03-26 23:41:24 -070030
Austin Schuhbb735b72019-01-03 12:58:41 -080031namespace chrono = ::std::chrono;
32
Brian Silverman9ed2cf12018-05-12 13:06:38 -070033struct SimpleAdcReadings {
34 uint16_t sin, cos;
35};
36
37void AdcInitSimple() {
38 AdcInitCommon();
39
40 // ENC_SIN ADC0_SE23
41 // ENC_COS ADC1_SE23
42}
43
44SimpleAdcReadings AdcReadSimple(const DisableInterrupts &) {
45 SimpleAdcReadings r;
46
47 ADC0_SC1A = 23;
48 ADC1_SC1A = 23;
49 while (!(ADC0_SC1A & ADC_SC1_COCO)) {
50 }
51 while (!(ADC1_SC1A & ADC_SC1_COCO)) {
52 }
53 r.sin = ADC0_RA;
54 r.cos = ADC1_RA;
55
56 return r;
57}
58
Austin Schuhbcce26a2018-03-26 23:41:24 -070059const ShifterHallEffect kThreeStateDriveShifter{0.0, 0.0, 0.25, 0.75};
60
61const DrivetrainConfig<float> &GetDrivetrainConfig() {
62 static DrivetrainConfig<float> kDrivetrainConfig{
63 ::frc971::control_loops::drivetrain::ShifterType::NO_SHIFTER,
64 ::frc971::control_loops::drivetrain::LoopType::OPEN_LOOP,
65 ::frc971::control_loops::drivetrain::GyroType::SPARTAN_GYRO,
66 ::frc971::control_loops::drivetrain::IMUType::IMU_X,
67
68 ::motors::seems_reasonable::MakeDrivetrainLoop,
69 ::motors::seems_reasonable::MakeVelocityDrivetrainLoop,
70 ::std::function<StateFeedbackLoop<7, 2, 4, float>()>(),
71
Austin Schuhbb735b72019-01-03 12:58:41 -080072 chrono::duration_cast<chrono::nanoseconds>(
73 chrono::duration<float>(::motors::seems_reasonable::kDt)),
74 ::motors::seems_reasonable::kRobotRadius,
Philipp Schrader790cb542023-07-05 21:06:52 -070075 ::motors::seems_reasonable::kWheelRadius,
76 ::motors::seems_reasonable::kV,
Austin Schuhbcce26a2018-03-26 23:41:24 -070077
78 ::motors::seems_reasonable::kHighGearRatio,
Austin Schuhe6a9fdf2019-01-12 16:05:43 -080079 ::motors::seems_reasonable::kLowGearRatio,
80 ::motors::seems_reasonable::kJ,
81 ::motors::seems_reasonable::kMass,
82 kThreeStateDriveShifter,
Philipp Schrader790cb542023-07-05 21:06:52 -070083 kThreeStateDriveShifter,
84 true /* default_high_gear */,
Austin Schuhbcce26a2018-03-26 23:41:24 -070085 0 /* down_offset if using constants use
Philipp Schrader790cb542023-07-05 21:06:52 -070086 constants::GetValues().down_error */
87 ,
88 0.8 /* wheel_non_linearity */,
89 1.2 /* quickturn_wheel_multiplier */,
90 1.5 /* wheel_multiplier */,
Austin Schuhbcce26a2018-03-26 23:41:24 -070091 };
92
93 return kDrivetrainConfig;
94};
95
Austin Schuhbcce26a2018-03-26 23:41:24 -070096::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
Austin Schuh4fae0fc2018-03-27 23:51:42 -070097::std::atomic<Spring *> global_spring{nullptr};
Austin Schuhbcce26a2018-03-26 23:41:24 -070098
Brian Silvermana3a172b2018-03-24 03:53:32 -040099// Last width we received on each channel.
Brian Silverman7f5f1442018-04-06 13:00:50 -0400100uint16_t pwm_input_widths[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -0400101// When we received a pulse on each channel in milliseconds.
Brian Silverman7f5f1442018-04-06 13:00:50 -0400102uint32_t pwm_input_times[6];
Brian Silvermana3a172b2018-03-24 03:53:32 -0400103
Austin Schuhbcce26a2018-03-26 23:41:24 -0700104constexpr int kChannelTimeout = 100;
105
106bool lost_channel(int channel) {
107 DisableInterrupts disable_interrupts;
108 if (time_after(millis(),
109 time_add(pwm_input_times[channel], kChannelTimeout))) {
110 return true;
111 }
112 return false;
113}
114
Brian Silvermana3a172b2018-03-24 03:53:32 -0400115// Returns the most recently captured value for the specified input channel
116// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
117float convert_input_width(int channel) {
118 uint16_t width;
119 {
120 DisableInterrupts disable_interrupts;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700121 if (time_after(millis(),
122 time_add(pwm_input_times[channel], kChannelTimeout))) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400123 return 0;
124 }
125
126 width = pwm_input_widths[channel];
127 }
128
129 // Values measured with a channel mapped to a button.
130 static constexpr uint16_t kMinWidth = 4133;
131 static constexpr uint16_t kMaxWidth = 7177;
132 if (width < kMinWidth) {
133 width = kMinWidth;
134 } else if (width > kMaxWidth) {
135 width = kMaxWidth;
136 }
137 return (static_cast<float>(2 * (width - kMinWidth)) /
138 static_cast<float>(kMaxWidth - kMinWidth)) -
139 1.0f;
140}
141
142// Sends a SET_RPM command to the specified VESC.
143// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
144// bandwidth.
145void vesc_set_rpm(int vesc_id, float rpm) {
146 const int32_t rpm_int = rpm;
147 uint32_t id = CAN_EFF_FLAG;
148 id |= vesc_id;
149 id |= (0x03 /* SET_RPM */) << 8;
150 uint8_t data[4] = {
151 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
152 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
153 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
154 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
155 };
156 can_send(id, data, sizeof(data), 2 + vesc_id);
157}
158
159// Sends a SET_CURRENT command to the specified VESC.
160// current is in amps.
161// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
162// bandwidth.
163void vesc_set_current(int vesc_id, float current) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700164 constexpr float kMaxCurrent = 80.0f;
165 const int32_t current_int =
166 ::std::max(-kMaxCurrent, ::std::min(kMaxCurrent, current)) * 1000.0f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400167 uint32_t id = CAN_EFF_FLAG;
168 id |= vesc_id;
169 id |= (0x01 /* SET_CURRENT */) << 8;
170 uint8_t data[4] = {
171 static_cast<uint8_t>((current_int >> 24) & 0xFF),
172 static_cast<uint8_t>((current_int >> 16) & 0xFF),
173 static_cast<uint8_t>((current_int >> 8) & 0xFF),
174 static_cast<uint8_t>((current_int >> 0) & 0xFF),
175 };
176 can_send(id, data, sizeof(data), 2 + vesc_id);
177}
178
Brian Silverman4d1e5272018-03-26 03:18:42 -0400179// Sends a SET_DUTY command to the specified VESC.
180// duty is from -1 to 1.
181// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
182// bandwidth.
183void vesc_set_duty(int vesc_id, float duty) {
Austin Schuhbcce26a2018-03-26 23:41:24 -0700184 constexpr int32_t kMaxDuty = 99999;
185 const int32_t duty_int = ::std::max(
186 -kMaxDuty, ::std::min(kMaxDuty, static_cast<int32_t>(duty * 100000.0f)));
Brian Silverman4d1e5272018-03-26 03:18:42 -0400187 uint32_t id = CAN_EFF_FLAG;
188 id |= vesc_id;
189 id |= (0x00 /* SET_DUTY */) << 8;
190 uint8_t data[4] = {
191 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
192 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
193 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
194 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
195 };
196 can_send(id, data, sizeof(data), 2 + vesc_id);
197}
198
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700199// TODO(Brian): Move these two test functions somewhere else.
200__attribute__((unused)) void DoVescTest() {
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700201 uint32_t time = micros();
202 while (true) {
203 for (int i = 0; i < 6; ++i) {
204 const uint32_t end = time_add(time, 500000);
205 while (true) {
206 const bool done = time_after(micros(), end);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700207 float current;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700208 if (done) {
209 current = -6;
210 } else {
211 current = 6;
212 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400213 vesc_set_current(i, current);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700214 if (done) {
215 break;
216 }
217 delay(5);
218 }
219 time = end;
220 }
221 }
222}
223
Brian Silverman6c8b88b2018-09-03 18:17:02 -0700224__attribute__((unused)) void DoReceiverTest2() {
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700225 static constexpr float kMaxRpm = 10000.0f;
226 while (true) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400227 const bool flip = convert_input_width(2) > 0;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700228
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700229 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400230 const float value = convert_input_width(0);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700231
232 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400233 float rpm = ::std::min(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(0, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700238 }
239
240 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400241 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700242 if (flip) {
243 rpm *= -1.0f;
244 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400245 vesc_set_rpm(1, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700246 }
247 }
248
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700249 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400250 const float value = convert_input_width(1);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700251
252 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400253 float rpm = ::std::min(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(2, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700258 }
259
260 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400261 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700262 if (flip) {
263 rpm *= -1.0f;
264 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400265 vesc_set_rpm(3, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700266 }
267 }
268
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700269 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400270 const float value = convert_input_width(4);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700271
272 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400273 float rpm = ::std::min(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(4, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700278 }
279
280 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400281 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700282 if (flip) {
283 rpm *= -1.0f;
284 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400285 vesc_set_rpm(5, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700286 }
287 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400288 // Give the CAN frames a chance to go out.
289 delay(5);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700290 }
291}
292
293void SetupPwmFtm(BigFTM *ftm) {
294 ftm->MODE = FTM_MODE_WPDIS;
295 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
296 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
297
298 // Can't change MOD according to the reference manual ("The Dual Edge Capture
299 // mode must be used with ... the FTM counter in Free running counter.").
300 ftm->MOD = 0xFFFF;
301
302 // Capturing rising edge.
303 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
304 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400305 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700306
307 // Capturing rising edge.
308 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
309 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400310 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700311
312 // Capturing rising edge.
313 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
314 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400315 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700316
317 // Capturing rising edge.
318 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
319 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400320 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700321
Brian Silvermana3a172b2018-03-24 03:53:32 -0400322 (void)ftm->STATUS;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700323 ftm->STATUS = 0x00;
324
325 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
326 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
327 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
328 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
329
330 // 34.95ms max period before it starts wrapping and being weird.
331 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
332 FTM_SC_PS(4) /* Prescaler=32 */;
333
334 ftm->MODE &= ~FTM_MODE_WPDIS;
335}
336
Brian Silverman2de95d62018-03-31 12:32:24 -0700337struct AccelerometerResult {
338 uint16_t result;
339 bool success;
340};
341
342// Does a transfer on the accelerometer. Returns the resulting frame, or a
343// failure if it takes until after end_micros.
344AccelerometerResult AccelerometerTransfer(uint16_t data, uint32_t end_micros) {
345 SPI0_SR = SPI_SR_RFDF;
346 SPI0_PUSHR = SPI_PUSHR_PCS(1) | data;
347
348 while (!(SPI0_SR & SPI_SR_RFDF)) {
349 if (time_after(micros(), end_micros)) {
350 return {0, false};
351 }
352 }
353 const uint32_t popr = SPI0_POPR;
354 SPI0_SR = SPI_SR_RFDF;
355 return {static_cast<uint16_t>(popr & 0xFFFF), true};
356}
357
358constexpr uint32_t kAccelerometerTimeout = 500;
359
360bool AccelerometerWrite(uint8_t address, uint8_t data, uint32_t end_micros) {
361 const AccelerometerResult r = AccelerometerTransfer(
362 (static_cast<uint16_t>(address) << 8) | static_cast<uint16_t>(data),
363 end_micros);
364 return r.success;
365}
366
367AccelerometerResult AccelerometerRead(uint8_t address, uint32_t end_micros) {
368 AccelerometerResult r = AccelerometerTransfer(
369 (static_cast<uint16_t>(address) << 8) | UINT16_C(0x8000), end_micros);
370 r.result = r.result & UINT16_C(0xFF);
371 return r;
372}
373
374bool accelerometer_inited = false;
375
376void AccelerometerInit() {
377 accelerometer_inited = false;
378 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
379 {
380 const auto who_am_i = AccelerometerRead(0xF, end_micros);
381 if (!who_am_i.success) {
382 return;
383 }
384 if (who_am_i.result != 0x32) {
385 return;
386 }
387 }
Philipp Schrader790cb542023-07-05 21:06:52 -0700388 if (!AccelerometerWrite(0x20,
389 (1 << 5) /* Normal mode */ | (1 << 3) /* 100 Hz */ |
390 (1 << 2) /* Z enabled */ |
391 (1 << 1) /* Y enabled */ |
392 (1 << 0) /* X enabled */,
393 end_micros)) {
Brian Silverman2de95d62018-03-31 12:32:24 -0700394 }
395 // If want to read LSB, need to enable BDU to avoid splitting reads.
Philipp Schrader790cb542023-07-05 21:06:52 -0700396 if (!AccelerometerWrite(0x23,
397 (0 << 6) /* Data LSB at lower address */ |
398 (3 << 4) /* 400g full scale */ |
399 (0 << 0) /* 4-wire interface */,
Brian Silverman2de95d62018-03-31 12:32:24 -0700400 end_micros)) {
401 }
402 accelerometer_inited = true;
403}
404
405float AccelerometerConvert(uint16_t value) {
406 return static_cast<float>(400.0 / 65536.0) * static_cast<float>(value);
407}
408
409// Returns the total acceleration (in any direction) or 0 if there's an error.
410float ReadAccelerometer() {
411 if (!accelerometer_inited) {
412 AccelerometerInit();
413 return 0;
414 }
415
416 const uint32_t end_micros = time_add(micros(), kAccelerometerTimeout);
417 const auto x = AccelerometerRead(0x29, end_micros);
418 const auto y = AccelerometerRead(0x2B, end_micros);
419 const auto z = AccelerometerRead(0x2D, end_micros);
420 if (!x.success || !y.success || !z.success) {
421 accelerometer_inited = false;
422 return 0;
423 }
424
425 const float x_g = AccelerometerConvert(x.result);
426 const float y_g = AccelerometerConvert(y.result);
427 const float z_g = AccelerometerConvert(z.result);
428 return ::std::sqrt(x_g * x_g + y_g * y_g + z_g * z_g);
429}
430
Brian Silvermana3a172b2018-03-24 03:53:32 -0400431extern "C" void ftm0_isr() {
432 while (true) {
433 const uint32_t status = FTM0->STATUS;
434 if (status == 0) {
435 return;
436 }
437
438 if (status & (1 << 1)) {
439 const uint32_t start = FTM0->C0V;
440 const uint32_t end = FTM0->C1V;
441 pwm_input_widths[0] = (end - start) & 0xFFFF;
442 pwm_input_times[0] = millis();
443 }
444 if (status & (1 << 7)) {
445 const uint32_t start = FTM0->C6V;
446 const uint32_t end = FTM0->C7V;
447 pwm_input_widths[1] = (end - start) & 0xFFFF;
448 pwm_input_times[1] = millis();
449 }
450 if (status & (1 << 5)) {
451 const uint32_t start = FTM0->C4V;
452 const uint32_t end = FTM0->C5V;
453 pwm_input_widths[2] = (end - start) & 0xFFFF;
454 pwm_input_times[2] = millis();
455 }
456 if (status & (1 << 3)) {
457 const uint32_t start = FTM0->C2V;
458 const uint32_t end = FTM0->C3V;
459 pwm_input_widths[4] = (end - start) & 0xFFFF;
460 pwm_input_times[4] = millis();
461 }
462
463 FTM0->STATUS = 0;
464 }
465}
466
467extern "C" void ftm3_isr() {
468 while (true) {
469 const uint32_t status = FTM3->STATUS;
470 if (status == 0) {
471 return;
472 }
473
474 if (status & (1 << 3)) {
475 const uint32_t start = FTM3->C2V;
476 const uint32_t end = FTM3->C3V;
477 pwm_input_widths[3] = (end - start) & 0xFFFF;
478 pwm_input_times[3] = millis();
479 }
Brian Silverman7f5f1442018-04-06 13:00:50 -0400480 if (status & (1 << 7)) {
481 const uint32_t start = FTM3->C6V;
482 const uint32_t end = FTM3->C7V;
483 pwm_input_widths[5] = (end - start) & 0xFFFF;
484 pwm_input_times[5] = millis();
485 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400486
487 FTM3->STATUS = 0;
488 }
489}
490
491float ConvertEncoderChannel(uint16_t reading) {
492 // Theoretical values based on the datasheet are 931 and 2917.
493 // With these values, the magnitude ranges from 0.99-1.03, which works fine
494 // (the encoder's output appears to get less accurate in one quadrant for some
495 // reason, hence the variation).
496 static constexpr uint16_t kMin = 802, kMax = 3088;
497 if (reading < kMin) {
498 reading = kMin;
499 } else if (reading > kMax) {
500 reading = kMax;
501 }
502 return (static_cast<float>(2 * (reading - kMin)) /
503 static_cast<float>(kMax - kMin)) -
504 1.0f;
505}
506
507struct EncoderReading {
Brian Silvermana96c1a42018-05-12 12:11:31 -0700508 EncoderReading(const SimpleAdcReadings &adc_readings) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400509 const float sin = ConvertEncoderChannel(adc_readings.sin);
510 const float cos = ConvertEncoderChannel(adc_readings.cos);
511
Austin Schuhbcce26a2018-03-26 23:41:24 -0700512 const float magnitude = hypot(sin, cos);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400513 const float magnitude_error = ::std::abs(magnitude - 1.0f);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700514 valid = magnitude_error < 0.30f;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400515
516 angle = ::std::atan2(sin, cos);
517 }
518
519 // Angle in radians, in [-pi, pi].
520 float angle;
521
522 bool valid;
523};
524
525extern "C" void pit3_isr() {
526 PIT_TFLG3 = 1;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700527 PolyDrivetrain<float> *polydrivetrain =
528 global_polydrivetrain.load(::std::memory_order_acquire);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700529 Spring *spring = global_spring.load(::std::memory_order_acquire);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400530
Brian Silvermana96c1a42018-05-12 12:11:31 -0700531 SimpleAdcReadings adc_readings;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400532 {
533 DisableInterrupts disable_interrupts;
Brian Silvermana96c1a42018-05-12 12:11:31 -0700534 adc_readings = AdcReadSimple(disable_interrupts);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400535 }
536
537 EncoderReading encoder(adc_readings);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700538 static float last_good_encoder = 0.0f;
539 static int invalid_encoder_count = 0;
540 if (encoder.valid) {
541 last_good_encoder = encoder.angle;
542 invalid_encoder_count = 0;
543 } else {
544 ++invalid_encoder_count;
545 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400546
Austin Schuhe666dc62018-08-08 21:09:12 -0700547 const bool lost_spring_channel = lost_channel(2) || lost_channel(3) ||
548 lost_channel(4) || lost_channel(5) ||
549 (convert_input_width(4) < 0.5f);
550
551 const bool lost_drive_channel = lost_channel(0) || lost_channel(1) ||
552 (::std::abs(convert_input_width(4)) < 0.5f);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700553
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700554 if (polydrivetrain != nullptr && spring != nullptr) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700555 float throttle;
556 float wheel;
Austin Schuhe666dc62018-08-08 21:09:12 -0700557 if (lost_drive_channel) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700558 throttle = 0.0f;
559 wheel = 0.0f;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700560 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700561 throttle = convert_input_width(1);
562 wheel = -convert_input_width(0);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700563 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700564 const bool quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700565
Alex Perrycb7da4b2019-08-28 19:35:56 -0700566 OutputT output;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700567
Alex Perrycb7da4b2019-08-28 19:35:56 -0700568 polydrivetrain->SetGoal(wheel, throttle, quickturn, false);
Austin Schuheeec74a2019-01-27 20:58:59 -0800569 polydrivetrain->Update(12.0f);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700570 polydrivetrain->SetOutput(&output);
571
572 vesc_set_duty(0, -output.left_voltage / 12.0f);
573 vesc_set_duty(1, -output.left_voltage / 12.0f);
574
575 vesc_set_duty(2, output.right_voltage / 12.0f);
576 vesc_set_duty(3, output.right_voltage / 12.0f);
577
Austin Schuhe666dc62018-08-08 21:09:12 -0700578 const bool prime = convert_input_width(2) > 0.1f;
579 const bool fire = convert_input_width(3) > 0.1f;
580 const bool force_move =
581 (convert_input_width(5) > 0.1f) && !lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700582
Austin Schuhe666dc62018-08-08 21:09:12 -0700583 bool unload = lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700584 static bool was_lost = true;
Austin Schuhe666dc62018-08-08 21:09:12 -0700585 bool force_reset = !lost_spring_channel && was_lost;
586 was_lost = lost_spring_channel;
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700587
Austin Schuhe666dc62018-08-08 21:09:12 -0700588 spring->Iterate(unload, prime, fire, force_reset, force_move,
589 invalid_encoder_count <= 2, last_good_encoder);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700590
591 float spring_output = spring->output();
592
593 vesc_set_duty(4, -spring_output);
594 vesc_set_duty(5, spring_output);
595
Brian Silverman2de95d62018-03-31 12:32:24 -0700596 const float accelerometer = ReadAccelerometer();
597 (void)accelerometer;
598
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700599 /*
600 // Massive debug. Turn on for useful bits.
Brian Silverman2de95d62018-03-31 12:32:24 -0700601 printf("acc %d/1000\n", (int)(accelerometer / 1000));
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700602 if (!encoder.valid) {
603 printf("Stuck encoder: ADC %" PRIu16 " %" PRIu16
604 " enc %d/1000 %s mag %d\n",
605 adc_readings.sin, adc_readings.cos, (int)(encoder.angle * 1000),
606 encoder.valid ? "T" : "f",
607 (int)(hypot(ConvertEncoderChannel(adc_readings.sin),
608 ConvertEncoderChannel(adc_readings.cos)) *
609 1000));
610 }
Austin Schuhbcce26a2018-03-26 23:41:24 -0700611 static int i = 0;
612 ++i;
613 if (i > 20) {
614 i = 0;
Austin Schuhe666dc62018-08-08 21:09:12 -0700615 if (lost_spring_channel || lost_drive_channel) {
616 printf("200Hz loop, disabled %d %d %d %d %d %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700617 (int)(convert_input_width(0) * 1000),
618 (int)(convert_input_width(1) * 1000),
619 (int)(convert_input_width(2) * 1000),
620 (int)(convert_input_width(3) * 1000),
Austin Schuhe666dc62018-08-08 21:09:12 -0700621 (int)(convert_input_width(4) * 1000),
622 (int)(convert_input_width(5) * 1000));
Austin Schuhbcce26a2018-03-26 23:41:24 -0700623 } else {
624 printf(
Austin Schuhe666dc62018-08-08 21:09:12 -0700625 "TODO(Austin): 200Hz loop %d %d %d %d %d %d, lr, %d, %d velocity %d
626 "
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700627 " state: %d, near %d angle %d goal %d to: %d ADC %" PRIu16
628 " %" PRIu16 " enc %d/1000 %s from %d\n",
Austin Schuhbcce26a2018-03-26 23:41:24 -0700629 (int)(convert_input_width(0) * 1000),
630 (int)(convert_input_width(1) * 1000),
631 (int)(convert_input_width(2) * 1000),
632 (int)(convert_input_width(3) * 1000),
633 (int)(convert_input_width(4) * 1000),
Austin Schuhe666dc62018-08-08 21:09:12 -0700634 (int)(convert_input_width(5) * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700635 static_cast<int>(output.left_voltage * 100),
636 static_cast<int>(output.right_voltage * 100),
637 static_cast<int>(polydrivetrain->velocity() * 100),
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700638 static_cast<int>(spring->state()), static_cast<int>(spring->Near()),
639 static_cast<int>(spring->angle() * 1000),
640 static_cast<int>(spring->goal() * 1000),
641 static_cast<int>(spring->timeout()), adc_readings.sin,
642 adc_readings.cos, (int)(encoder.angle * 1000),
Austin Schuhbcce26a2018-03-26 23:41:24 -0700643 encoder.valid ? "T" : "f",
644 (int)(::std::sqrt(ConvertEncoderChannel(adc_readings.sin) *
645 ConvertEncoderChannel(adc_readings.sin) +
646 ConvertEncoderChannel(adc_readings.cos) *
647 ConvertEncoderChannel(adc_readings.cos)) *
648 1000));
649 }
650 }
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700651 */
Austin Schuhbcce26a2018-03-26 23:41:24 -0700652 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400653}
654
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700655} // namespace
656
657extern "C" {
658
659void *__stack_chk_guard = (void *)0x67111971;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700660void __stack_chk_fail(void);
661
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700662} // extern "C"
663
664extern "C" int main(void) {
665 // for background about this startup delay, please see these conversations
666 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
667 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
668 delay(400);
669
670 // Set all interrupts to the second-lowest priority to start with.
671 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
672
673 // Now set priorities for all the ones we care about. They only have meaning
674 // relative to each other, which means centralizing them here makes it a lot
675 // more manageable.
676 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400677 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
678 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
679 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700680
681 // Builtin LED.
682 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
683 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
684 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
685
686 // Set up the CAN pins.
687 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
688 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
689
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700690 // PWM_IN0
691 // FTM0_CH0
692 PORTC_PCR1 = PORT_PCR_MUX(4);
693
694 // PWM_IN1
695 // FTM0_CH6
696 PORTD_PCR6 = PORT_PCR_MUX(4);
697
698 // PWM_IN2
699 // FTM0_CH4
700 PORTD_PCR4 = PORT_PCR_MUX(4);
701
702 // PWM_IN3
703 // FTM3_CH2
704 PORTD_PCR2 = PORT_PCR_MUX(4);
705
706 // PWM_IN4
707 // FTM0_CH2
708 PORTC_PCR3 = PORT_PCR_MUX(4);
709
Brian Silverman7f5f1442018-04-06 13:00:50 -0400710 // PWM_IN5
711 // FTM3_CH6
712 PORTC_PCR10 = PORT_PCR_MUX(3);
713
Brian Silverman2de95d62018-03-31 12:32:24 -0700714 // SPI0
715 // ACC_CS PCS0
716 PORTA_PCR14 = PORT_PCR_DSE | PORT_PCR_MUX(2);
717 // SCK
718 PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(2);
719 // MOSI
720 PORTA_PCR16 = PORT_PCR_DSE | PORT_PCR_MUX(2);
721 // MISO
722 PORTA_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(2);
723
724 SIM_SCGC6 |= SIM_SCGC6_SPI0;
725 SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(1) | SPI_MCR_CLR_TXF |
726 SPI_MCR_CLR_RXF | SPI_MCR_HALT;
727 // 60 MHz "protocol clock"
728 // 6ns CS setup
729 // 8ns CS hold
730 SPI0_CTAR0 = SPI_CTAR_FMSZ(15) | SPI_CTAR_CPOL /* Clock idles high */ |
731 SPI_CTAR_CPHA /* Data captured on trailing edge */ |
732 0 /* !LSBFE MSB first */ |
733 SPI_CTAR_PCSSCK(0) /* PCS->SCK prescaler = 1 */ |
734 SPI_CTAR_PASC(0) /* SCK->PCS prescaler = 1 */ |
735 SPI_CTAR_PDT(0) /* PCS->PCS prescaler = 1 */ |
736 SPI_CTAR_PBR(0) /* baud prescaler = 1 */ |
737 SPI_CTAR_CSSCK(0) /* PCS->SCK 2/60MHz = 33.33ns */ |
738 SPI_CTAR_ASC(0) /* SCK->PCS 2/60MHz = 33.33ns */ |
739 SPI_CTAR_DT(2) /* PCS->PSC 8/60MHz = 133.33ns */ |
740 SPI_CTAR_BR(2) /* BR 60MHz/6 = 10MHz */;
741
742 SPI0_MCR &= ~SPI_MCR_HALT;
743
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700744 delay(100);
745
Brian Silverman4787a6e2018-10-06 16:00:54 -0700746 PrintingParameters printing_parameters;
747 printing_parameters.dedicated_usb = true;
748 const ::std::unique_ptr<PrintingImplementation> printing =
749 CreatePrinting(printing_parameters);
750 printing->Initialize();
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700751
Brian Silvermana3a172b2018-03-24 03:53:32 -0400752 SIM_SCGC6 |= SIM_SCGC6_PIT;
753 // Workaround for errata e7914.
754 (void)PIT_MCR;
755 PIT_MCR = 0;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700756 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
Brian Silvermana3a172b2018-03-24 03:53:32 -0400757 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
758
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700759 can_init(0, 1);
Brian Silvermana96c1a42018-05-12 12:11:31 -0700760 AdcInitSimple();
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700761 SetupPwmFtm(FTM0);
762 SetupPwmFtm(FTM3);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700763
Austin Schuhbcce26a2018-03-26 23:41:24 -0700764 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
765 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
Austin Schuh4fae0fc2018-03-27 23:51:42 -0700766 Spring spring;
767 global_spring.store(&spring, ::std::memory_order_release);
Austin Schuhbcce26a2018-03-26 23:41:24 -0700768
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700769 // Leave the LEDs on for a bit longer.
770 delay(300);
771 printf("Done starting up\n");
772
Brian Silverman2de95d62018-03-31 12:32:24 -0700773 AccelerometerInit();
774 printf("Accelerometer init %s\n", accelerometer_inited ? "success" : "fail");
775
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700776 // Done starting up, now turn the LED off.
777 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
778
Brian Silvermana3a172b2018-03-24 03:53:32 -0400779 NVIC_ENABLE_IRQ(IRQ_FTM0);
780 NVIC_ENABLE_IRQ(IRQ_FTM3);
781 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
Austin Schuhe666dc62018-08-08 21:09:12 -0700782 printf("Done starting up2\n");
Brian Silvermana3a172b2018-03-24 03:53:32 -0400783
Philipp Schrader790cb542023-07-05 21:06:52 -0700784 // DoReceiverTest2();
Austin Schuhbcce26a2018-03-26 23:41:24 -0700785 while (true) {
786 }
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700787
788 return 0;
789}
790
791void __stack_chk_fail(void) {
792 while (true) {
793 GPIOC_PSOR = (1 << 5);
794 printf("Stack corruption detected\n");
795 delay(1000);
796 GPIOC_PCOR = (1 << 5);
797 delay(1000);
798 }
799}
800
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800801} // namespace frc971::motors