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