blob: 046759911e31cd8571f62ac0d5cdadba653be1ba [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>
5#include <atomic>
6#include <cmath>
7
Brian Silverman54dd2fe2018-03-16 23:44:31 -07008#include "motors/core/kinetis.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -04009#include "motors/core/time.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070010#include "motors/peripheral/adc.h"
11#include "motors/peripheral/can.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040012#include "motors/peripheral/configuration.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070013#include "motors/usb/cdc.h"
Brian Silvermana3a172b2018-03-24 03:53:32 -040014#include "motors/usb/usb.h"
Brian Silverman54dd2fe2018-03-16 23:44:31 -070015#include "motors/util.h"
16
17namespace frc971 {
18namespace motors {
19namespace {
20
21::std::atomic<teensy::AcmTty *> global_stdout{nullptr};
22
Brian Silvermana3a172b2018-03-24 03:53:32 -040023// Last width we received on each channel.
24uint16_t pwm_input_widths[5];
25// When we received a pulse on each channel in milliseconds.
26uint32_t pwm_input_times[5];
27
28// Returns the most recently captured value for the specified input channel
29// scaled from -1 to 1, or 0 if it was captured over 100ms ago.
30float convert_input_width(int channel) {
31 uint16_t width;
32 {
33 DisableInterrupts disable_interrupts;
34 if (time_after(millis(), time_add(pwm_input_times[channel], 100))) {
35 return 0;
36 }
37
38 width = pwm_input_widths[channel];
39 }
40
41 // Values measured with a channel mapped to a button.
42 static constexpr uint16_t kMinWidth = 4133;
43 static constexpr uint16_t kMaxWidth = 7177;
44 if (width < kMinWidth) {
45 width = kMinWidth;
46 } else if (width > kMaxWidth) {
47 width = kMaxWidth;
48 }
49 return (static_cast<float>(2 * (width - kMinWidth)) /
50 static_cast<float>(kMaxWidth - kMinWidth)) -
51 1.0f;
52}
53
54// Sends a SET_RPM command to the specified VESC.
55// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
56// bandwidth.
57void vesc_set_rpm(int vesc_id, float rpm) {
58 const int32_t rpm_int = rpm;
59 uint32_t id = CAN_EFF_FLAG;
60 id |= vesc_id;
61 id |= (0x03 /* SET_RPM */) << 8;
62 uint8_t data[4] = {
63 static_cast<uint8_t>((rpm_int >> 24) & 0xFF),
64 static_cast<uint8_t>((rpm_int >> 16) & 0xFF),
65 static_cast<uint8_t>((rpm_int >> 8) & 0xFF),
66 static_cast<uint8_t>((rpm_int >> 0) & 0xFF),
67 };
68 can_send(id, data, sizeof(data), 2 + vesc_id);
69}
70
71// Sends a SET_CURRENT command to the specified VESC.
72// current is in amps.
73// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
74// bandwidth.
75void vesc_set_current(int vesc_id, float current) {
Brian Silverman4d1e5272018-03-26 03:18:42 -040076 const int32_t current_int = current * 1000.0f;
Brian Silvermana3a172b2018-03-24 03:53:32 -040077 uint32_t id = CAN_EFF_FLAG;
78 id |= vesc_id;
79 id |= (0x01 /* SET_CURRENT */) << 8;
80 uint8_t data[4] = {
81 static_cast<uint8_t>((current_int >> 24) & 0xFF),
82 static_cast<uint8_t>((current_int >> 16) & 0xFF),
83 static_cast<uint8_t>((current_int >> 8) & 0xFF),
84 static_cast<uint8_t>((current_int >> 0) & 0xFF),
85 };
86 can_send(id, data, sizeof(data), 2 + vesc_id);
87}
88
Brian Silverman4d1e5272018-03-26 03:18:42 -040089// Sends a SET_DUTY command to the specified VESC.
90// duty is from -1 to 1.
91// Note that sending 6 VESC commands every 1ms doesn't quite fit in the CAN
92// bandwidth.
93void vesc_set_duty(int vesc_id, float duty) {
94 const int32_t duty_int = duty * 100000.0f;
95 uint32_t id = CAN_EFF_FLAG;
96 id |= vesc_id;
97 id |= (0x00 /* SET_DUTY */) << 8;
98 uint8_t data[4] = {
99 static_cast<uint8_t>((duty_int >> 24) & 0xFF),
100 static_cast<uint8_t>((duty_int >> 16) & 0xFF),
101 static_cast<uint8_t>((duty_int >> 8) & 0xFF),
102 static_cast<uint8_t>((duty_int >> 0) & 0xFF),
103 };
104 can_send(id, data, sizeof(data), 2 + vesc_id);
105}
106
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700107void DoVescTest() {
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700108 uint32_t time = micros();
109 while (true) {
110 for (int i = 0; i < 6; ++i) {
111 const uint32_t end = time_add(time, 500000);
112 while (true) {
113 const bool done = time_after(micros(), end);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700114 float current;
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700115 if (done) {
116 current = -6;
117 } else {
118 current = 6;
119 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400120 vesc_set_current(i, current);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700121 if (done) {
122 break;
123 }
124 delay(5);
125 }
126 time = end;
127 }
128 }
129}
130
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700131void DoReceiverTest2() {
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700132 static constexpr float kMaxRpm = 10000.0f;
133 while (true) {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400134 const bool flip = convert_input_width(2) > 0;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700135
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700136 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400137 const float value = convert_input_width(0);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700138
139 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400140 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700141 if (flip) {
142 rpm *= -1.0f;
143 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400144 vesc_set_rpm(0, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700145 }
146
147 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400148 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700149 if (flip) {
150 rpm *= -1.0f;
151 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400152 vesc_set_rpm(1, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700153 }
154 }
155
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700156 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400157 const float value = convert_input_width(1);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700158
159 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400160 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700161 if (flip) {
162 rpm *= -1.0f;
163 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400164 vesc_set_rpm(2, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700165 }
166
167 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400168 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700169 if (flip) {
170 rpm *= -1.0f;
171 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400172 vesc_set_rpm(3, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700173 }
174 }
175
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700176 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400177 const float value = convert_input_width(4);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700178
179 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400180 float rpm = ::std::min(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700181 if (flip) {
182 rpm *= -1.0f;
183 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400184 vesc_set_rpm(4, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700185 }
186
187 {
Brian Silvermana3a172b2018-03-24 03:53:32 -0400188 float rpm = ::std::max(0.0f, value) * kMaxRpm;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700189 if (flip) {
190 rpm *= -1.0f;
191 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400192 vesc_set_rpm(5, rpm);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700193 }
194 }
Brian Silvermana3a172b2018-03-24 03:53:32 -0400195 // Give the CAN frames a chance to go out.
196 delay(5);
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700197 }
198}
199
200void SetupPwmFtm(BigFTM *ftm) {
201 ftm->MODE = FTM_MODE_WPDIS;
202 ftm->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
203 ftm->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
204
205 // Can't change MOD according to the reference manual ("The Dual Edge Capture
206 // mode must be used with ... the FTM counter in Free running counter.").
207 ftm->MOD = 0xFFFF;
208
209 // Capturing rising edge.
210 ftm->C0SC = FTM_CSC_MSA | FTM_CSC_ELSA;
211 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400212 ftm->C1SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700213
214 // Capturing rising edge.
215 ftm->C2SC = FTM_CSC_MSA | FTM_CSC_ELSA;
216 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400217 ftm->C3SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700218
219 // Capturing rising edge.
220 ftm->C4SC = FTM_CSC_MSA | FTM_CSC_ELSA;
221 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400222 ftm->C5SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700223
224 // Capturing rising edge.
225 ftm->C6SC = FTM_CSC_MSA | FTM_CSC_ELSA;
226 // Capturing falling edge.
Brian Silvermana3a172b2018-03-24 03:53:32 -0400227 ftm->C7SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSB;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700228
Brian Silvermana3a172b2018-03-24 03:53:32 -0400229 (void)ftm->STATUS;
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700230 ftm->STATUS = 0x00;
231
232 ftm->COMBINE = FTM_COMBINE_DECAP3 | FTM_COMBINE_DECAPEN3 |
233 FTM_COMBINE_DECAP2 | FTM_COMBINE_DECAPEN2 |
234 FTM_COMBINE_DECAP1 | FTM_COMBINE_DECAPEN1 |
235 FTM_COMBINE_DECAP0 | FTM_COMBINE_DECAPEN0;
236
237 // 34.95ms max period before it starts wrapping and being weird.
238 ftm->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
239 FTM_SC_PS(4) /* Prescaler=32 */;
240
241 ftm->MODE &= ~FTM_MODE_WPDIS;
242}
243
Brian Silvermana3a172b2018-03-24 03:53:32 -0400244extern "C" void ftm0_isr() {
245 while (true) {
246 const uint32_t status = FTM0->STATUS;
247 if (status == 0) {
248 return;
249 }
250
251 if (status & (1 << 1)) {
252 const uint32_t start = FTM0->C0V;
253 const uint32_t end = FTM0->C1V;
254 pwm_input_widths[0] = (end - start) & 0xFFFF;
255 pwm_input_times[0] = millis();
256 }
257 if (status & (1 << 7)) {
258 const uint32_t start = FTM0->C6V;
259 const uint32_t end = FTM0->C7V;
260 pwm_input_widths[1] = (end - start) & 0xFFFF;
261 pwm_input_times[1] = millis();
262 }
263 if (status & (1 << 5)) {
264 const uint32_t start = FTM0->C4V;
265 const uint32_t end = FTM0->C5V;
266 pwm_input_widths[2] = (end - start) & 0xFFFF;
267 pwm_input_times[2] = millis();
268 }
269 if (status & (1 << 3)) {
270 const uint32_t start = FTM0->C2V;
271 const uint32_t end = FTM0->C3V;
272 pwm_input_widths[4] = (end - start) & 0xFFFF;
273 pwm_input_times[4] = millis();
274 }
275
276 FTM0->STATUS = 0;
277 }
278}
279
280extern "C" void ftm3_isr() {
281 while (true) {
282 const uint32_t status = FTM3->STATUS;
283 if (status == 0) {
284 return;
285 }
286
287 if (status & (1 << 3)) {
288 const uint32_t start = FTM3->C2V;
289 const uint32_t end = FTM3->C3V;
290 pwm_input_widths[3] = (end - start) & 0xFFFF;
291 pwm_input_times[3] = millis();
292 }
293
294 FTM3->STATUS = 0;
295 }
296}
297
298float ConvertEncoderChannel(uint16_t reading) {
299 // Theoretical values based on the datasheet are 931 and 2917.
300 // With these values, the magnitude ranges from 0.99-1.03, which works fine
301 // (the encoder's output appears to get less accurate in one quadrant for some
302 // reason, hence the variation).
303 static constexpr uint16_t kMin = 802, kMax = 3088;
304 if (reading < kMin) {
305 reading = kMin;
306 } else if (reading > kMax) {
307 reading = kMax;
308 }
309 return (static_cast<float>(2 * (reading - kMin)) /
310 static_cast<float>(kMax - kMin)) -
311 1.0f;
312}
313
314struct EncoderReading {
315 EncoderReading(const salsa::SimpleAdcReadings &adc_readings) {
316 const float sin = ConvertEncoderChannel(adc_readings.sin);
317 const float cos = ConvertEncoderChannel(adc_readings.cos);
318
319 const float magnitude = ::std::sqrt(sin * sin + cos * cos);
320 const float magnitude_error = ::std::abs(magnitude - 1.0f);
321 valid = magnitude_error < 0.15f;
322
323 angle = ::std::atan2(sin, cos);
324 }
325
326 // Angle in radians, in [-pi, pi].
327 float angle;
328
329 bool valid;
330};
331
332extern "C" void pit3_isr() {
333 PIT_TFLG3 = 1;
334
335 salsa::SimpleAdcReadings adc_readings;
336 {
337 DisableInterrupts disable_interrupts;
338 adc_readings = salsa::AdcReadSimple(disable_interrupts);
339 }
340
341 EncoderReading encoder(adc_readings);
342
343 printf("TODO(Austin): 1kHz loop %d %d %d %d %d ADC %" PRIu16 " %" PRIu16
344 " enc %d/1000 %s from %d\n",
345 (int)(convert_input_width(0) * 1000),
346 (int)(convert_input_width(1) * 1000),
347 (int)(convert_input_width(2) * 1000),
348 (int)(convert_input_width(3) * 1000),
349 (int)(convert_input_width(4) * 1000), adc_readings.sin,
350 adc_readings.cos, (int)(encoder.angle * 1000),
351 encoder.valid ? "T" : "f",
352 (int)(::std::sqrt(ConvertEncoderChannel(adc_readings.sin) *
353 ConvertEncoderChannel(adc_readings.sin) +
354 ConvertEncoderChannel(adc_readings.cos) *
355 ConvertEncoderChannel(adc_readings.cos)) *
356 1000));
357}
358
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700359} // namespace
360
361extern "C" {
362
363void *__stack_chk_guard = (void *)0x67111971;
364
365int _write(int /*file*/, char *ptr, int len) {
366 teensy::AcmTty *const tty = global_stdout.load(::std::memory_order_acquire);
367 if (tty != nullptr) {
368 return tty->Write(ptr, len);
369 }
370 return 0;
371}
372
373void __stack_chk_fail(void);
374
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700375} // extern "C"
376
377extern "C" int main(void) {
378 // for background about this startup delay, please see these conversations
379 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
380 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
381 delay(400);
382
383 // Set all interrupts to the second-lowest priority to start with.
384 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
385
386 // Now set priorities for all the ones we care about. They only have meaning
387 // relative to each other, which means centralizing them here makes it a lot
388 // more manageable.
389 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400390 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0xa);
391 NVIC_SET_SANE_PRIORITY(IRQ_FTM3, 0xa);
392 NVIC_SET_SANE_PRIORITY(IRQ_PIT_CH3, 0x5);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700393
394 // Builtin LED.
395 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 1;
396 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(1);
397 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
398
399 // Set up the CAN pins.
400 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
401 PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
402
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700403 // PWM_IN0
404 // FTM0_CH0
405 PORTC_PCR1 = PORT_PCR_MUX(4);
406
407 // PWM_IN1
408 // FTM0_CH6
409 PORTD_PCR6 = PORT_PCR_MUX(4);
410
411 // PWM_IN2
412 // FTM0_CH4
413 PORTD_PCR4 = PORT_PCR_MUX(4);
414
415 // PWM_IN3
416 // FTM3_CH2
417 PORTD_PCR2 = PORT_PCR_MUX(4);
418
419 // PWM_IN4
420 // FTM0_CH2
421 PORTC_PCR3 = PORT_PCR_MUX(4);
422
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700423 delay(100);
424
425 teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
426 usb_device.SetManufacturer("Seems Reasonable LLC");
427 usb_device.SetProduct("Simple Receiver Board");
428
429 teensy::AcmTty tty0(&usb_device);
430 global_stdout.store(&tty0, ::std::memory_order_release);
431 usb_device.Initialize();
432
Brian Silvermana3a172b2018-03-24 03:53:32 -0400433 SIM_SCGC6 |= SIM_SCGC6_PIT;
434 // Workaround for errata e7914.
435 (void)PIT_MCR;
436 PIT_MCR = 0;
437 PIT_LDVAL3 = (BUS_CLOCK_FREQUENCY / 1000) - 1;
438 PIT_TCTRL3 = PIT_TCTRL_TIE | PIT_TCTRL_TEN;
439
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700440 can_init(0, 1);
Brian Silvermana3a172b2018-03-24 03:53:32 -0400441 salsa::AdcInitSimple();
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700442 SetupPwmFtm(FTM0);
443 SetupPwmFtm(FTM3);
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700444
445 // Leave the LEDs on for a bit longer.
446 delay(300);
447 printf("Done starting up\n");
448
449 // Done starting up, now turn the LED off.
450 PERIPHERAL_BITBAND(GPIOC_PDOR, 5) = 0;
451
Brian Silvermana3a172b2018-03-24 03:53:32 -0400452 NVIC_ENABLE_IRQ(IRQ_FTM0);
453 NVIC_ENABLE_IRQ(IRQ_FTM3);
454 NVIC_ENABLE_IRQ(IRQ_PIT_CH3);
455
Brian Silverman4f8c6a72018-03-17 23:12:45 -0700456 DoReceiverTest2();
Brian Silverman54dd2fe2018-03-16 23:44:31 -0700457
458 return 0;
459}
460
461void __stack_chk_fail(void) {
462 while (true) {
463 GPIOC_PSOR = (1 << 5);
464 printf("Stack corruption detected\n");
465 delay(1000);
466 GPIOC_PCOR = (1 << 5);
467 delay(1000);
468 }
469}
470
471} // namespace motors
472} // namespace frc971