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