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