blob: 2724f3b3e0507cb2fb50be79c52d2527ec4b125c [file] [log] [blame]
Brian Silvermanb6570e32019-03-21 20:58:32 -07001// This file has the main for the Teensy on the simple receiver board v2 in the
2// new robot.
3
4#include <inttypes.h>
5#include <stdio.h>
Philipp Schrader790cb542023-07-05 21:06:52 -07006
Brian Silvermanb6570e32019-03-21 20:58:32 -07007#include <atomic>
8#include <chrono>
9#include <cmath>
10
11#include "frc971/control_loops/drivetrain/polydrivetrain.h"
12#include "motors/core/kinetis.h"
13#include "motors/core/time.h"
Brian Silvermanb6570e32019-03-21 20:58:32 -070014#include "motors/peripheral/configuration.h"
15#include "motors/print/print.h"
16#include "motors/seems_reasonable/drivetrain_dog_motor_plant.h"
17#include "motors/seems_reasonable/polydrivetrain_dog_motor_plant.h"
18#include "motors/util.h"
19
Stephan Pleinesf63bde82024-01-13 15:59:33 -080020namespace frc971::motors {
Brian Silvermanb6570e32019-03-21 20:58:32 -070021namespace {
22
Brian Silvermanb6570e32019-03-21 20:58:32 -070023using ::frc971::constants::ShifterHallEffect;
Alex Perrycb7da4b2019-08-28 19:35:56 -070024using ::frc971::control_loops::drivetrain::DrivetrainConfig;
25using ::frc971::control_loops::drivetrain::OutputT;
26using ::frc971::control_loops::drivetrain::PolyDrivetrain;
Brian Silvermanb6570e32019-03-21 20:58:32 -070027
28namespace chrono = ::std::chrono;
29
James Kuszmaul68025332024-01-20 17:06:02 -080030const ShifterHallEffect kThreeStateDriveShifter{{}, 0.0, 0.0, 0.25, 0.75};
Brian Silvermanb6570e32019-03-21 20:58:32 -070031
32const DrivetrainConfig<float> &GetDrivetrainConfig() {
33 static DrivetrainConfig<float> kDrivetrainConfig{
James Kuszmaul68025332024-01-20 17:06:02 -080034 ::frc971::control_loops::drivetrain::ShifterType::kNoShifter,
35 ::frc971::control_loops::drivetrain::LoopType::kOpenLoop,
36 ::frc971::control_loops::drivetrain::GyroType::kSpartanGyro,
37 ::frc971::control_loops::drivetrain::ImuType::kImuX,
Brian Silvermanb6570e32019-03-21 20:58:32 -070038
39 ::motors::seems_reasonable::MakeDrivetrainLoop,
40 ::motors::seems_reasonable::MakeVelocityDrivetrainLoop,
41 ::std::function<StateFeedbackLoop<7, 2, 4, float>()>(),
42
43 chrono::duration_cast<chrono::nanoseconds>(
44 chrono::duration<float>(::motors::seems_reasonable::kDt)),
45 ::motors::seems_reasonable::kRobotRadius,
Philipp Schrader790cb542023-07-05 21:06:52 -070046 ::motors::seems_reasonable::kWheelRadius,
47 ::motors::seems_reasonable::kV,
Brian Silvermanb6570e32019-03-21 20:58:32 -070048
49 ::motors::seems_reasonable::kHighGearRatio,
Philipp Schrader790cb542023-07-05 21:06:52 -070050 ::motors::seems_reasonable::kLowGearRatio,
51 ::motors::seems_reasonable::kJ,
52 ::motors::seems_reasonable::kMass,
53 kThreeStateDriveShifter,
54 kThreeStateDriveShifter,
55 true /* default_high_gear */,
56 0 /* down_offset */,
57 0.8 /* wheel_non_linearity */,
58 1.2 /* quickturn_wheel_multiplier */,
59 1.5 /* wheel_multiplier */,
Brian Silvermanb6570e32019-03-21 20:58:32 -070060 };
61
62 return kDrivetrainConfig;
63};
64
Brian Silvermanb6570e32019-03-21 20:58:32 -070065::std::atomic<PolyDrivetrain<float> *> global_polydrivetrain{nullptr};
66
67// Last width we received on each channel.
68uint16_t pwm_input_widths[6];
69// When we received a pulse on each channel in milliseconds.
70uint32_t pwm_input_times[6];
71
72constexpr int kChannelTimeout = 100;
73
74bool lost_channel(int channel) {
75 DisableInterrupts disable_interrupts;
76 if (time_after(millis(),
77 time_add(pwm_input_times[channel], kChannelTimeout))) {
78 return true;
79 }
80 return false;
81}
82
83// Returns the most recently captured value for the specified input channel
84// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
85float convert_input_width(int channel) {
86 uint16_t width;
87 {
88 DisableInterrupts disable_interrupts;
89 if (time_after(millis(),
90 time_add(pwm_input_times[channel], kChannelTimeout))) {
91 return 0;
92 }
93
94 width = pwm_input_widths[channel];
95 }
96
97 // Values measured with a channel mapped to a button.
98 static constexpr uint16_t kMinWidth = 4133;
99 static constexpr uint16_t kMaxWidth = 7177;
100 if (width < kMinWidth) {
101 width = kMinWidth;
102 } else if (width > kMaxWidth) {
103 width = kMaxWidth;
104 }
105 return (static_cast<float>(2 * (width - kMinWidth)) /
106 static_cast<float>(kMaxWidth - kMinWidth)) -
107 1.0f;
108}
109
Brian Silverman6a860722019-04-23 13:18:32 -0700110void SetupPwmInFtm(BigFTM *ftm) {
Brian Silvermanb6570e32019-03-21 20:58:32 -0700111 ftm->MODE = FTM_MODE_WPDIS;
112 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
113 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
114
115 // Can't change MOD according to the reference manual ("The Dual Edge Capture
116 // mode must be used with ... the FTM counter in Free running counter.").
117 ftm->MOD = 0xFFFF;
118
119 // Capturing rising edge.
120 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
121 // Capturing falling edge.
122 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
123
124 // Capturing rising edge.
125 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
126 // Capturing falling edge.
127 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
128
129 // Capturing rising edge.
130 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
131 // Capturing falling edge.
132 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
133
134 // Capturing rising edge.
135 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
136 // Capturing falling edge.
137 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
138
139 (void)ftm->STATUS;
140 ftm->STATUS = 0x00;
141
142 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
143 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
144 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
145 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
146
147 // 34.95ms max period before it starts wrapping and being weird.
148 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
149 FTM_SC_PS(4) /* Prescaler=32 */;
150
151 ftm->MODE &= ~FTM_MODE_WPDIS;
152}
153
Brian Silverman6a860722019-04-23 13:18:32 -0700154constexpr int kOutputCounts = 37500;
155constexpr int kOutputPrescalerShift = 5;
156
157void SetOutputWidth(int output, float ms) {
158 static constexpr float kScale = static_cast<float>(
159 static_cast<double>(kOutputCounts) / 20.0 /* milliseconds per period */);
160 const int width = static_cast<int>(ms * kScale + 0.5f);
161 switch (output) {
162 case 0:
163 FTM3->C0V = width - 1;
164 break;
165 case 1:
166 FTM3->C2V = width - 1;
167 break;
168 case 2:
169 FTM3->C3V = width - 1;
170 break;
171 case 3:
172 FTM3->C1V = width - 1;
173 break;
174 }
175 FTM3->PWMLOAD = FTM_PWMLOAD_LDOK;
176}
177
178// 1ms - 2ms (default for a VESC).
179void SetupPwmOutFtm(BigFTM *const pwm_ftm) {
180 // PWMSYNC doesn't matter because we set SYNCMODE down below.
181 pwm_ftm->MODE = FTM_MODE_WPDIS;
182 pwm_ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
183 pwm_ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */ |
184 FTM_SC_PS(kOutputPrescalerShift);
185
186 pwm_ftm->CNTIN = 0;
187 pwm_ftm->CNT = 0;
188 pwm_ftm->MOD = kOutputCounts - 1;
189
190 // High-true edge-aligned mode (turns on at start, off at match).
191 pwm_ftm->C0SC = FTM_CSC_MSB | FTM_CSC_ELSB;
192 pwm_ftm->C1SC = FTM_CSC_MSB | FTM_CSC_ELSB;
193 pwm_ftm->C2SC = FTM_CSC_MSB | FTM_CSC_ELSB;
194 pwm_ftm->C3SC = FTM_CSC_MSB | FTM_CSC_ELSB;
195 pwm_ftm->C4SC = FTM_CSC_MSB | FTM_CSC_ELSB;
196 pwm_ftm->C5SC = FTM_CSC_MSB | FTM_CSC_ELSB;
197 pwm_ftm->C6SC = FTM_CSC_MSB | FTM_CSC_ELSB;
198 pwm_ftm->C7SC = FTM_CSC_MSB | FTM_CSC_ELSB;
199
200 pwm_ftm->COMBINE = FTM_COMBINE_SYNCEN3 /* Synchronize updates usefully */ |
201 FTM_COMBINE_SYNCEN2 /* Synchronize updates usefully */ |
202 FTM_COMBINE_SYNCEN1 /* Synchronize updates usefully */ |
203 FTM_COMBINE_SYNCEN0 /* Synchronize updates usefully */;
204
205 // Initialize all the channels to 0.
206 pwm_ftm->OUTINIT = 0;
207
208 // All of the channels are active high.
209 pwm_ftm->POL = 0;
210
211 pwm_ftm->SYNCONF =
212 FTM_SYNCONF_HWWRBUF /* Hardware trigger flushes switching points */ |
213 FTM_SYNCONF_SWWRBUF /* Software trigger flushes switching points */ |
214 FTM_SYNCONF_SWRSTCNT /* Software trigger resets the count */ |
215 FTM_SYNCONF_SYNCMODE /* Use the new synchronization mode */;
216
217 // Don't want any intermediate loading points.
218 pwm_ftm->PWMLOAD = 0;
219
220 // This has to happen after messing with SYNCONF, and should happen after
221 // messing with various other things so the values can get flushed out of the
222 // buffers.
223 pwm_ftm->SYNC = FTM_SYNC_SWSYNC /* Flush everything out right now */ |
224 FTM_SYNC_CNTMAX /* Load new values at the end of the cycle */;
225 // Wait for the software synchronization to finish.
226 while (pwm_ftm->SYNC & FTM_SYNC_SWSYNC) {
227 }
228
229 pwm_ftm->SC = FTM_SC_TOIE /* Interrupt on overflow */ |
230 FTM_SC_CLKS(1) /* Use the system clock */ |
231 FTM_SC_PS(kOutputPrescalerShift);
232 pwm_ftm->MODE &= ~FTM_MODE_WPDIS;
233}
234
Brian Silvermanb6570e32019-03-21 20:58:32 -0700235extern "C" void ftm0_isr() {
236 while (true) {
237 const uint32_t status = FTM0->STATUS;
238 if (status == 0) {
239 return;
240 }
241
242 if (status & (1 << 1)) {
243 const uint32_t start = FTM0->C0V;
244 const uint32_t end = FTM0->C1V;
245 pwm_input_widths[1] = (end - start) & 0xFFFF;
246 pwm_input_times[1] = millis();
247 }
248 if (status & (1 << 5)) {
249 const uint32_t start = FTM0->C4V;
250 const uint32_t end = FTM0->C5V;
251 pwm_input_widths[3] = (end - start) & 0xFFFF;
252 pwm_input_times[3] = millis();
253 }
254 if (status & (1 << 3)) {
255 const uint32_t start = FTM0->C2V;
256 const uint32_t end = FTM0->C3V;
257 pwm_input_widths[4] = (end - start) & 0xFFFF;
258 pwm_input_times[4] = millis();
259 }
260
261 FTM0->STATUS = 0;
262 }
263}
264
265extern "C" void ftm3_isr() {
266 while (true) {
267 const uint32_t status = FTM3->STATUS;
268 if (status == 0) {
269 return;
270 }
271
272 FTM3->STATUS = 0;
273 }
274}
275
276extern "C" void pit3_isr() {
277 PIT_TFLG3 = 1;
278 PolyDrivetrain<float> *polydrivetrain =
279 global_polydrivetrain.load(::std::memory_order_acquire);
280
281 const bool lost_drive_channel = lost_channel(3) || lost_channel(1);
282
283 if (false) {
284 static int count = 0;
285 if (++count == 50) {
286 count = 0;
287 printf("0: %d 1: %d\n", (int)pwm_input_widths[3],
288 (int)pwm_input_widths[1]);
289 }
290 }
291
292 if (polydrivetrain != nullptr) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700293 float throttle;
294 float wheel;
Brian Silvermanb6570e32019-03-21 20:58:32 -0700295 if (lost_drive_channel) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700296 throttle = 0.0f;
297 wheel = 0.0f;
Brian Silvermanb6570e32019-03-21 20:58:32 -0700298 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700299 throttle = convert_input_width(1);
300 wheel = -convert_input_width(3);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700301 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700302 const bool quickturn = ::std::abs(polydrivetrain->velocity()) < 0.25f;
Brian Silvermanb6570e32019-03-21 20:58:32 -0700303
304 if (false) {
305 static int count = 0;
306 if (++count == 50) {
307 count = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700308 printf("throttle: %d wheel: %d\n", (int)(throttle * 100),
309 (int)(wheel * 100));
Brian Silvermanb6570e32019-03-21 20:58:32 -0700310 }
311 }
312
Alex Perrycb7da4b2019-08-28 19:35:56 -0700313 OutputT output;
Brian Silvermanb6570e32019-03-21 20:58:32 -0700314
Alex Perrycb7da4b2019-08-28 19:35:56 -0700315 polydrivetrain->SetGoal(wheel, throttle, quickturn, false);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700316 polydrivetrain->Update(12.0f);
317 polydrivetrain->SetOutput(&output);
318
319 if (false) {
320 static int count = 0;
321 if (++count == 50) {
322 count = 0;
323 printf("l: %d r: %d\n", (int)(output.left_voltage * 100),
324 (int)(output.right_voltage * 100));
325 }
326 }
Brian Silvermanb6570e32019-03-21 20:58:32 -0700327
Brian Silverman6a860722019-04-23 13:18:32 -0700328 SetOutputWidth(0, 1.5f - output.left_voltage / 12.0f * 0.5f);
329 SetOutputWidth(1, 1.5f + output.right_voltage / 12.0f * 0.5f);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700330 }
331}
332
333} // namespace
334
335extern "C" {
336
337void *__stack_chk_guard = (void *)0x67111971;
338void __stack_chk_fail(void);
339
340} // extern "C"
341
342extern "C" int main(void) {
343 // for background about this startup delay, please see these conversations
344 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
345 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
346 delay(400);
347
348 // Set all interrupts to the second-lowest priority to start with.
349 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
350
351 // Now set priorities for all the ones we care about. They only have meaning
352 // relative to each other, which means centralizing them here makes it a lot
353 // more manageable.
354 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
355 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700356 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
357
358 // Builtin LED.
359 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
360 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
361 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
362
Brian Silverman6a860722019-04-23 13:18:32 -0700363 // PWM_OUT0
364 // FTM3_CH0
365 PORTD_PCR0 = PORT_PCR_DSE | PORT_PCR_MUX(4);
366
367 // PWM_OUT1
368 // FTM3_CH2
369 PORTD_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
370
371 // PWM_OUT2
372 // FTM3_CH3
373 PORTD_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
374
375 // PWM_OUT3
376 // FTM3_CH1
377 PORTD_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700378
379 // PWM_IN0
380 // FTM0_CH1 (doesn't work)
381 // PORTC_PCR2 = PORT_PCR_MUX(4);
382
383 // PWM_IN1
384 // FTM0_CH0
385 PORTC_PCR1 = PORT_PCR_MUX(4);
386
387 // PWM_IN2
388 // FTM0_CH5 (doesn't work)
389 // PORTD_PCR5 = PORT_PCR_MUX(4);
390
391 // PWM_IN3
392 // FTM0_CH4
393 PORTD_PCR4 = PORT_PCR_MUX(4);
394
395 // PWM_IN4
396 // FTM0_CH2
397 PORTC_PCR3 = PORT_PCR_MUX(4);
398
399 // PWM_IN5
400 // FTM0_CH3 (doesn't work)
401 // PORTC_PCR4 = PORT_PCR_MUX(4);
402
403 delay(100);
404
405 PrintingParameters printing_parameters;
406 printing_parameters.dedicated_usb = true;
407 const ::std::unique_ptr<PrintingImplementation> printing =
408 CreatePrinting(printing_parameters);
409 printing->Initialize();
410
411 SIM_SCGC6 |= SIM_SCGC6_PIT;
412 // Workaround for errata e7914.
413 (void)PIT_MCR;
414 PIT_MCR = 0;
415 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 200) - 1;
416 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
417
Brian Silverman6a860722019-04-23 13:18:32 -0700418 SetupPwmInFtm(FTM0);
419 SetupPwmOutFtm(FTM3);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700420
421 PolyDrivetrain<float> polydrivetrain(GetDrivetrainConfig(), nullptr);
422 global_polydrivetrain.store(&polydrivetrain, ::std::memory_order_release);
423
424 // Leave the LED on for a bit longer.
425 delay(300);
426 printf("Done starting up\n");
427
428 // Done starting up, now turn the LED off.
429 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
430
431 NVIC_ENABLE_IRQ(IRQ_FTM0);
Brian Silvermanb6570e32019-03-21 20:58:32 -0700432 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
433 printf("Done starting up2\n");
434
435 while (true) {
436 }
437
438 return 0;
439}
440
441void __stack_chk_fail(void) {
442 while (true) {
443 GPIOC_PSOR = (1 << 5);
444 printf("Stack corruption detected\n");
445 delay(1000);
446 GPIOC_PCOR = (1 << 5);
447 delay(1000);
448 }
449}
450
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800451} // namespace frc971::motors