blob: 84b98b07fd1b4788e49f6c3ed3c56efa793ee943 [file] [log] [blame]
Brian Silverman3240e102019-02-16 18:24:24 -08001#include "aos/time/time.h"
2#include "motors/core/kinetis.h"
3#include "motors/core/time.h"
4#include "motors/peripheral/configuration.h"
Brian Silvermand7d01102019-02-24 16:11:21 -08005#include "motors/peripheral/spi.h"
Brian Silverman3240e102019-02-16 18:24:24 -08006#include "motors/peripheral/uart.h"
7#include "motors/print/print.h"
8#include "motors/util.h"
Brian Silvermand7d01102019-02-24 16:11:21 -08009#include "third_party/GSL/include/gsl/gsl"
10#include "y2019/jevois/cobs.h"
11#include "y2019/jevois/spi.h"
12#include "y2019/jevois/uart.h"
13
14using frc971::teensy::InterruptBufferedUart;
15using frc971::teensy::InterruptBufferedSpi;
16
17// All indices here refer to the ports as numbered on the PCB.
Brian Silverman3240e102019-02-16 18:24:24 -080018
19namespace frc971 {
20namespace jevois {
21namespace {
22
Brian Silvermand7d01102019-02-24 16:11:21 -080023// Holds all of our hardware UARTs. There is exactly one global instance for
24// interrupt handlers to access.
Brian Silverman3240e102019-02-16 18:24:24 -080025struct Uarts {
26 Uarts() {
27 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -080028 global_instance = this;
Brian Silverman3240e102019-02-16 18:24:24 -080029 }
30 ~Uarts() {
31 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -080032 global_instance = nullptr;
Brian Silverman3240e102019-02-16 18:24:24 -080033 }
Brian Silvermand7d01102019-02-24 16:11:21 -080034 Uarts(const Uarts &) = delete;
35 Uarts &operator=(const Uarts &) = delete;
Brian Silverman3240e102019-02-16 18:24:24 -080036
37 void Initialize(int baud_rate) {
38 cam0.Initialize(baud_rate);
39 cam1.Initialize(baud_rate);
40 cam2.Initialize(baud_rate);
41 cam3.Initialize(baud_rate);
42 cam4.Initialize(baud_rate);
43 }
44
Brian Silvermand7d01102019-02-24 16:11:21 -080045 InterruptBufferedUart cam0{&UART1, F_CPU};
46 InterruptBufferedUart cam1{&UART0, F_CPU};
47 InterruptBufferedUart cam2{&UART2, BUS_CLOCK_FREQUENCY};
48 InterruptBufferedUart cam3{&UART3, BUS_CLOCK_FREQUENCY};
49 InterruptBufferedUart cam4{&UART4, BUS_CLOCK_FREQUENCY};
Brian Silverman3240e102019-02-16 18:24:24 -080050
Brian Silvermand7d01102019-02-24 16:11:21 -080051 static Uarts *global_instance;
Brian Silverman3240e102019-02-16 18:24:24 -080052};
53
Brian Silvermand7d01102019-02-24 16:11:21 -080054Uarts *Uarts::global_instance = nullptr;
55
56// Manages the transmit buffer to a single camera.
57//
58// We have to add delays between sending each byte in order for the camera to
59// successfully receive them.
60struct TransmitBuffer {
61 TransmitBuffer(InterruptBufferedUart *camera_in) : camera(camera_in) {}
62 InterruptBufferedUart *const camera;
63
64 frc971::teensy::UartBuffer<1024> buffer;
65 aos::monotonic_clock::time_point last_send = aos::monotonic_clock::min_time;
66
67 // Sends a byte to the camera if it's time.
68 void Tick(aos::monotonic_clock::time_point now) {
69 if (buffer.empty()) {
70 return;
71 }
72 if (now < last_send + std::chrono::milliseconds(1)) {
73 return;
74 }
75 last_send = now;
76 camera->Write(std::array<char, 1>{{buffer.PopSingle()}});
77 }
78
79 // Queues up another packet to send, only if the previous one has finished.
80 void MaybeWritePacket(const CameraCalibration &calibration) {
81 if (!buffer.empty()) {
82 return;
83 }
84 const auto serialized = UartPackToCamera(calibration);
85 buffer.PushSingle(0);
86 if (buffer.PushSpan(serialized) == static_cast<int>(serialized.size())) {
87 buffer.PushSingle(0);
88 }
89 }
90};
91
92InterruptBufferedSpi *global_spi_instance = nullptr;
93
94// Manages queueing a transfer to send via SPI.
95class SpiQueue {
96 public:
97 SpiQueue() {
98 DisableInterrupts disable_interrupts;
99 global_instance = this;
100 }
101 ~SpiQueue() {
102 DisableInterrupts disable_interrupts;
103 global_instance = nullptr;
104 }
105 SpiQueue(const SpiQueue &) = delete;
106 SpiQueue &operator=(const SpiQueue &) = delete;
107
108 tl::optional<gsl::span<const char, spi_transfer_size()>> Tick() {
109 {
110 DisableInterrupts disable_interrupts;
111 if (waiting_for_enable_ || waiting_for_disable_) {
112 return tl::nullopt;
113 }
114 }
115 const auto now = aos::monotonic_clock::now();
116 if (TransferTimedOut(now)) {
117 printf("SPI timeout with %d left\n", static_cast<int>(to_receive_.size()));
118 WaitForNextTransfer();
119 return tl::nullopt;
120 }
121 {
122 DisableInterrupts disable_interrupts;
123 if (!PERIPHERAL_BITBAND(GPIOA_PDIR, 17) &&
124 cs_deassert_time_ == aos::monotonic_clock::max_time) {
125 cs_deassert_time_ = now;
126 }
127 }
128 if (DeassertHappened(now)) {
129 printf("CS deasserted with %d left\n", static_cast<int>(to_receive_.size()));
130 WaitForNextTransfer();
131 return tl::nullopt;
132 }
133 bool all_done;
134 {
135 DisableInterrupts disable_interrupts;
136 if (received_dummy_) {
137 to_receive_ = to_receive_.subspan(
138 global_spi_instance->Read(to_receive_, &disable_interrupts).size());
139 all_done = to_receive_.empty();
140 } else {
141 std::array<char, 1> dummy_data;
142 if (global_spi_instance->Read(dummy_data, &disable_interrupts).size() >=
143 1) {
144 received_dummy_ = true;
145 }
146 all_done = false;
147 }
148 }
149 if (all_done) {
150 WaitForNextTransfer();
151 return received_transfer_;
152 }
153 return tl::nullopt;
154 }
155
156 void HandleInterrupt() {
157 DisableInterrupts disable_interrupts;
158 if (waiting_for_disable_) {
159 if (!PERIPHERAL_BITBAND(GPIOA_PDIR, 17)) {
160 PORTA_PCR17 =
161 PORT_PCR_MUX(1) | PORT_PCR_IRQC(0xC) /* Interrupt when logic 1 */;
162 // Clear the interrupt flag now that we've reconfigured it.
163 PORTA_ISFR = 1 << 17;
164 waiting_for_disable_ = false;
165 } else {
166 // Clear the interrupt flag. It shouldn't trigger again immediately
167 // because the pin is still asserted.
168 PORTA_ISFR = 1 << 17;
169 }
170 return;
171 }
172 if (waiting_for_enable_) {
173 if (PERIPHERAL_BITBAND(GPIOA_PDIR, 17)) {
174 global_spi_instance->ClearQueues(disable_interrupts);
175 // Tell the SPI peripheral its CS is asserted.
176 PERIPHERAL_BITBAND(GPIOB_PDOR, 17) = 0;
177 // Disable interrupts on the enable pin. We'll re-enable once we finish
178 // the transfer.
179 PORTA_PCR17 = PORT_PCR_MUX(1);
180 // Clear the interrupt flag now that we've reconfigured it.
181 PORTA_ISFR = 1 << 17;
182 if (have_transfer_) {
183 global_spi_instance->Write(transfer_, &disable_interrupts);
184 have_transfer_ = false;
185 } else {
186 printf("Writing dummy SPI frame\n");
187 // If we don't have anything, just write 0s to avoid getting the
188 // hardware confused.
189 global_spi_instance->Write(SpiTransfer{}, &disable_interrupts);
190 }
191 // Queue up a dummy byte at the end. This won't actually be sent,
192 // because the first byte we do send will be garbage, but it will
193 // synchronize our queues so we receive all the useful data bytes.
194 global_spi_instance->Write(std::array<char, 1>(), &disable_interrupts);
195 waiting_for_enable_ = false;
196 receive_start_ = aos::monotonic_clock::now();
197 cs_deassert_time_ = aos::monotonic_clock::max_time;
198 // To make debugging easier.
199 received_transfer_.fill(0);
200 } else {
201 // Clear the interrupt flag. It shouldn't trigger again immediately
202 // because the pin is still asserted.
203 PORTA_ISFR = 1 << 17;
204 }
205 return;
206 }
207 // We shouldn't ever get here. Clear all the flags and hope they don't get
208 // re-asserted immediately.
209 PORTA_ISFR = UINT32_C(0xFFFFFFFF);
210 }
211
212 void UpdateTransfer(const SpiTransfer &transfer, const DisableInterrupts &) {
213 have_transfer_ = true;
214 transfer_ = transfer;
215 }
216
217 // Returns whether a transfer is currently queued. This will be true between a
218 // call to UpdateTransfer and that transfer actually being moved out to the
219 // hardware.
220 bool HaveTransfer(const DisableInterrupts &) const { return have_transfer_; }
221
222 static SpiQueue *global_instance;
223
224 private:
225 void WaitForNextTransfer() {
226 to_receive_ = received_transfer_;
227 received_dummy_ = false;
228 {
229 DisableInterrupts disable_interrupts;
230 waiting_for_enable_ = true;
231 waiting_for_disable_ = true;
232 PORTA_PCR17 =
233 PORT_PCR_MUX(1) | PORT_PCR_IRQC(0x8) /* Interrupt when logic 0 */;
234 // Clear the interrupt flag now that we've reconfigured it.
235 PORTA_ISFR = 1 << 17;
236 }
237 // Tell the SPI peripheral its CS is de-asserted.
238 PERIPHERAL_BITBAND(GPIOB_PDOR, 17) = 1;
239 }
240
241 bool TransferTimedOut(aos::monotonic_clock::time_point now) {
242 DisableInterrupts disable_interrupts;
243 // TODO: Revise this timeout.
244 return now - std::chrono::milliseconds(50) > receive_start_;
245 }
246
247 bool DeassertHappened(aos::monotonic_clock::time_point now) {
248 DisableInterrupts disable_interrupts;
249 return now - std::chrono::microseconds(50) > cs_deassert_time_;
250 }
251
252 bool waiting_for_enable_ = true;
253 bool waiting_for_disable_ = false;
254 bool have_transfer_ = false;
255 SpiTransfer transfer_;
256 bool received_dummy_ = false;
257 SpiTransfer received_transfer_;
258 gsl::span<char> to_receive_ = received_transfer_;
259 aos::monotonic_clock::time_point receive_start_;
260 aos::monotonic_clock::time_point cs_deassert_time_;
261};
262
263SpiQueue *SpiQueue::global_instance = nullptr;
264
265// All methods here must be fully synchronized by the caller.
266class FrameQueue {
267 public:
268 FrameQueue() = default;
269 FrameQueue(const FrameQueue &) = delete;
270 FrameQueue &operator=(const FrameQueue &) = delete;
271
Brian Silvermanc41fb862019-03-02 21:14:46 -0800272 void UpdateFrame(int camera, const CameraFrame &frame) {
Brian Silvermand7d01102019-02-24 16:11:21 -0800273 frames_[camera].targets = frame.targets;
274 frames_[camera].capture_time = aos::monotonic_clock::now() - frame.age;
Brian Silvermanc41fb862019-03-02 21:14:46 -0800275 frames_[camera].camera_index = camera;
Brian Silvermand7d01102019-02-24 16:11:21 -0800276 const aos::SizedArray<int, 3> old_last_frames = last_frames_;
277 last_frames_.clear();
278 for (int index : old_last_frames) {
279 if (index != camera) {
280 last_frames_.push_back(index);
281 }
282 }
283 }
284
285 // Creates and returns a transfer with all the current information.
286 //
287 // This does not actually record these frames as transferred until
288 // RemoveLatestFrames() is called.
289 SpiTransfer MakeTransfer();
290
291 // Records the frames represented in the result of the latest MakeTransfer()
292 // call as being transferred, so they will not be represented in subsequent
293 // MakeTransfer() calls.
294 void RemoveLatestFrames() {
295 for (int index : last_frames_) {
296 frames_[index].capture_time = aos::monotonic_clock::min_time;
297 }
298 last_frames_.clear();
299 }
300
301 private:
302 struct FrameData {
303 aos::SizedArray<Target, 3> targets;
304 aos::monotonic_clock::time_point capture_time =
305 aos::monotonic_clock::min_time;
Brian Silvermanc41fb862019-03-02 21:14:46 -0800306 int camera_index;
Brian Silvermand7d01102019-02-24 16:11:21 -0800307 };
308
309 std::array<FrameData, 5> frames_;
310 // The indices into frames_ which we returned in the last MakeTransfer() call.
311 aos::SizedArray<int, 3> last_frames_;
312};
313
314SpiTransfer FrameQueue::MakeTransfer() {
315 aos::SizedArray<int, 5> oldest_indices;
316 for (size_t i = 0; i < frames_.size(); ++i) {
317 if (frames_[i].capture_time != aos::monotonic_clock::min_time) {
318 oldest_indices.push_back(i);
319 }
320 }
321 std::sort(oldest_indices.begin(), oldest_indices.end(), [this](int a, int b) {
322 return frames_[a].capture_time < frames_[b].capture_time;
323 });
324
325 TeensyToRoborio message;
326 last_frames_.clear();
327 for (int i = 0; i < std::min<int>(oldest_indices.size(), 3); ++i) {
328 const int index = oldest_indices[i];
329 const FrameData &frame = frames_[index];
330 const auto age = aos::monotonic_clock::now() - frame.capture_time;
331 const auto rounded_age = aos::time::round<camera_duration>(age);
Brian Silvermanc41fb862019-03-02 21:14:46 -0800332 message.frames.push_back({frame.targets, rounded_age, frame.camera_index});
Brian Silvermand7d01102019-02-24 16:11:21 -0800333 last_frames_.push_back(index);
334 }
335 return SpiPackToRoborio(message);
336}
Brian Silverman3240e102019-02-16 18:24:24 -0800337
Brian Silverman2294f352019-03-02 16:31:18 -0800338// Manages turning the debug light on and off periodically.
339//
340// It blinks at 1Hz with a variable duty cycle.
341class DebugLight {
342 public:
343 static constexpr aos::monotonic_clock::duration period() {
344 return std::chrono::seconds(1);
345 }
346
347 void set_next_off_time(aos::monotonic_clock::duration next_off_time) {
348 next_off_time_ = next_off_time;
349 }
350
351 void Tick() {
352 const auto now = aos::monotonic_clock::now();
353 if (last_cycle_start_ == aos::monotonic_clock::min_time) {
354 last_cycle_start_ = now;
355 current_off_point_ = last_cycle_start_ + next_off_time_;
356 } else if (now > last_cycle_start_ + period()) {
357 last_cycle_start_ += period();
358 current_off_point_ = last_cycle_start_ + next_off_time_;
359 }
360 if (now > current_off_point_) {
361 GPIOC_PCOR = 1 << 5;
362 } else {
363 GPIOC_PSOR = 1 << 5;
364 }
365 }
366
367 private:
368 aos::monotonic_clock::time_point last_cycle_start_ =
369 aos::monotonic_clock::min_time;
370
371 aos::monotonic_clock::duration next_off_time_ = std::chrono::milliseconds(100);
372 aos::monotonic_clock::time_point current_off_point_ =
373 aos::monotonic_clock::min_time;
374};
375
Brian Silverman3240e102019-02-16 18:24:24 -0800376extern "C" {
377
378void *__stack_chk_guard = (void *)0x67111971;
379void __stack_chk_fail(void) {
380 while (true) {
381 GPIOC_PSOR = (1 << 5);
382 printf("Stack corruption detected\n");
383 delay(1000);
384 GPIOC_PCOR = (1 << 5);
385 delay(1000);
386 }
387}
388
389extern char *__brkval;
390extern uint32_t __bss_ram_start__[];
391extern uint32_t __heap_start__[];
392extern uint32_t __stack_end__[];
393
394void uart0_status_isr(void) {
395 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -0800396 Uarts::global_instance->cam1.HandleInterrupt(disable_interrupts);
Brian Silverman3240e102019-02-16 18:24:24 -0800397}
398
399void uart1_status_isr(void) {
400 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -0800401 Uarts::global_instance->cam0.HandleInterrupt(disable_interrupts);
Brian Silverman3240e102019-02-16 18:24:24 -0800402}
403
404void uart2_status_isr(void) {
405 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -0800406 Uarts::global_instance->cam2.HandleInterrupt(disable_interrupts);
Brian Silverman3240e102019-02-16 18:24:24 -0800407}
408
409void uart3_status_isr(void) {
410 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -0800411 Uarts::global_instance->cam3.HandleInterrupt(disable_interrupts);
Brian Silverman3240e102019-02-16 18:24:24 -0800412}
413
414void uart4_status_isr(void) {
415 DisableInterrupts disable_interrupts;
Brian Silvermand7d01102019-02-24 16:11:21 -0800416 Uarts::global_instance->cam4.HandleInterrupt(disable_interrupts);
417}
418
419void spi0_isr(void) {
420 DisableInterrupts disable_interrupts;
421 global_spi_instance->HandleInterrupt(disable_interrupts);
422}
423
424void porta_isr(void) {
425 SpiQueue::global_instance->HandleInterrupt();
Brian Silverman3240e102019-02-16 18:24:24 -0800426}
427
428} // extern "C"
429
430// A test program which echos characters back after adding a per-UART offset to
431// them (CAM0 adds 1, CAM1 adds 2, etc).
432__attribute__((unused)) void TestUarts() {
Brian Silvermand7d01102019-02-24 16:11:21 -0800433 Uarts *const uarts = Uarts::global_instance;
Brian Silverman3240e102019-02-16 18:24:24 -0800434 while (true) {
435 {
436 std::array<char, 10> buffer;
437 const auto data = uarts->cam0.Read(buffer);
438 for (int i = 0; i < data.size(); ++i) {
439 data[i] += 1;
440 }
441 uarts->cam0.Write(data);
442 }
443 {
444 std::array<char, 10> buffer;
445 const auto data = uarts->cam1.Read(buffer);
446 for (int i = 0; i < data.size(); ++i) {
447 data[i] += 2;
448 }
449 uarts->cam1.Write(data);
450 }
451 {
452 std::array<char, 10> buffer;
453 const auto data = uarts->cam2.Read(buffer);
454 for (int i = 0; i < data.size(); ++i) {
455 data[i] += 3;
456 }
457 uarts->cam2.Write(data);
458 }
459 {
460 std::array<char, 10> buffer;
461 const auto data = uarts->cam3.Read(buffer);
462 for (int i = 0; i < data.size(); ++i) {
463 data[i] += 4;
464 }
465 uarts->cam3.Write(data);
466 }
467 {
468 std::array<char, 10> buffer;
469 const auto data = uarts->cam4.Read(buffer);
470 for (int i = 0; i < data.size(); ++i) {
471 data[i] += 5;
472 }
473 uarts->cam4.Write(data);
474 }
475 }
476}
477
478// Tests all the I/O pins. Cycles through each one for 1 second. While active,
479// each output is turned on, and each input has its value printed.
480__attribute__((unused)) void TestIo() {
481 // Set SPI0 pins to GPIO.
482 // SPI_OUT
483 PERIPHERAL_BITBAND(GPIOC_PDDR, 6) = 1;
484 PORTC_PCR6 = PORT_PCR_DSE | PORT_PCR_MUX(1);
485 // SPI_CS
486 PERIPHERAL_BITBAND(GPIOD_PDDR, 0) = 0;
487 PORTD_PCR0 = PORT_PCR_DSE | PORT_PCR_MUX(1);
488 // SPI_IN
489 PERIPHERAL_BITBAND(GPIOC_PDDR, 7) = 0;
490 PORTC_PCR7 = PORT_PCR_DSE | PORT_PCR_MUX(1);
491 // SPI_SCK
492 PERIPHERAL_BITBAND(GPIOD_PDDR, 1) = 0;
493 PORTD_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(1);
494
495 // Set LED pins to GPIO.
496 PERIPHERAL_BITBAND(GPIOC_PDDR, 11) = 1;
497 PORTC_PCR11 = PORT_PCR_DSE | PORT_PCR_MUX(1);
498 PERIPHERAL_BITBAND(GPIOC_PDDR, 10) = 1;
499 PORTC_PCR10 = PORT_PCR_DSE | PORT_PCR_MUX(1);
500 PERIPHERAL_BITBAND(GPIOC_PDDR, 8) = 1;
501 PORTC_PCR8 = PORT_PCR_DSE | PORT_PCR_MUX(1);
502 PERIPHERAL_BITBAND(GPIOC_PDDR, 9) = 1;
503 PORTC_PCR9 = PORT_PCR_DSE | PORT_PCR_MUX(1);
504 PERIPHERAL_BITBAND(GPIOB_PDDR, 18) = 1;
505 PORTB_PCR18 = PORT_PCR_DSE | PORT_PCR_MUX(1);
506 PERIPHERAL_BITBAND(GPIOC_PDDR, 2) = 1;
507 PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(1);
508 PERIPHERAL_BITBAND(GPIOD_PDDR, 7) = 1;
509 PORTD_PCR7 = PORT_PCR_DSE | PORT_PCR_MUX(1);
510 PERIPHERAL_BITBAND(GPIOC_PDDR, 1) = 1;
511 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(1);
512 PERIPHERAL_BITBAND(GPIOB_PDDR, 19) = 1;
513 PORTB_PCR19 = PORT_PCR_DSE | PORT_PCR_MUX(1);
514 PERIPHERAL_BITBAND(GPIOD_PDDR, 5) = 1;
515 PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
516
517 auto next = aos::monotonic_clock::now();
518 static constexpr auto kTick = std::chrono::seconds(1);
519 while (true) {
520 printf("SPI_MISO\n");
521 PERIPHERAL_BITBAND(GPIOC_PDOR, 6) = 1;
522 while (aos::monotonic_clock::now() < next + kTick) {
523 }
524 PERIPHERAL_BITBAND(GPIOC_PDOR, 6) = 0;
525 next += kTick;
526
527 while (aos::monotonic_clock::now() < next + kTick) {
528 printf("SPI_CS %d\n", (int)PERIPHERAL_BITBAND(GPIOD_PDIR, 0));
529 }
530 next += kTick;
531
532 while (aos::monotonic_clock::now() < next + kTick) {
533 printf("SPI_MOSI %d\n", (int)PERIPHERAL_BITBAND(GPIOC_PDIR, 7));
534 }
535 next += kTick;
536
537 while (aos::monotonic_clock::now() < next + kTick) {
538 printf("SPI_CLK %d\n", (int)PERIPHERAL_BITBAND(GPIOD_PDIR, 1));
539 }
540 next += kTick;
541
542 printf("CAM0\n");
543 PERIPHERAL_BITBAND(GPIOC_PDOR, 11) = 1;
544 while (aos::monotonic_clock::now() < next + kTick) {
545 }
546 PERIPHERAL_BITBAND(GPIOC_PDOR, 11) = 0;
547 next += kTick;
548
549 printf("CAM1\n");
550 PERIPHERAL_BITBAND(GPIOC_PDOR, 10) = 1;
551 while (aos::monotonic_clock::now() < next + kTick) {
552 }
553 PERIPHERAL_BITBAND(GPIOC_PDOR, 10) = 0;
554 next += kTick;
555
556 printf("CAM2\n");
557 PERIPHERAL_BITBAND(GPIOC_PDOR, 8) = 1;
558 while (aos::monotonic_clock::now() < next + kTick) {
559 }
560 PERIPHERAL_BITBAND(GPIOC_PDOR, 8) = 0;
561 next += kTick;
562
563 printf("CAM3\n");
564 PERIPHERAL_BITBAND(GPIOC_PDOR, 9) = 1;
565 while (aos::monotonic_clock::now() < next + kTick) {
566 }
567 PERIPHERAL_BITBAND(GPIOC_PDOR, 9) = 0;
568 next += kTick;
569
570 printf("CAM4\n");
571 PERIPHERAL_BITBAND(GPIOB_PDOR, 18) = 1;
572 while (aos::monotonic_clock::now() < next + kTick) {
573 }
574 PERIPHERAL_BITBAND(GPIOB_PDOR, 18) = 0;
575 next += kTick;
576
577 printf("CAM5\n");
578 PERIPHERAL_BITBAND(GPIOC_PDOR, 2) = 1;
579 while (aos::monotonic_clock::now() < next + kTick) {
580 }
581 PERIPHERAL_BITBAND(GPIOC_PDOR, 2) = 0;
582 next += kTick;
583
584 printf("CAM6\n");
585 PERIPHERAL_BITBAND(GPIOD_PDOR, 7) = 1;
586 while (aos::monotonic_clock::now() < next + kTick) {
587 }
588 PERIPHERAL_BITBAND(GPIOD_PDOR, 7) = 0;
589 next += kTick;
590
591 printf("CAM7\n");
592 PERIPHERAL_BITBAND(GPIOC_PDOR, 1) = 1;
593 while (aos::monotonic_clock::now() < next + kTick) {
594 }
595 PERIPHERAL_BITBAND(GPIOC_PDOR, 1) = 0;
596 next += kTick;
597
598 printf("CAM8\n");
599 PERIPHERAL_BITBAND(GPIOB_PDOR, 19) = 1;
600 while (aos::monotonic_clock::now() < next + kTick) {
601 }
602 PERIPHERAL_BITBAND(GPIOB_PDOR, 19) = 0;
603 next += kTick;
604
605 printf("CAM9\n");
606 PERIPHERAL_BITBAND(GPIOD_PDOR, 5) = 1;
607 while (aos::monotonic_clock::now() < next + kTick) {
608 }
609 PERIPHERAL_BITBAND(GPIOD_PDOR, 5) = 0;
610 next += kTick;
611 }
612}
613
Brian Silvermand7d01102019-02-24 16:11:21 -0800614// Does the normal work of transferring data in all directions.
615//
616// https://community.nxp.com/thread/466937#comment-983881 is a post from NXP
617// claiming that it's impossible to queue up the first byte for the slave end of
618// an SPI connection properly. Instead, we just accept there will be a garbage
619// byte and the other end ignores it.
Brian Silverman83693e42019-03-02 15:45:52 -0800620__attribute__((unused)) void TransferData(
621 frc971::motors::PrintingImplementation *printing) {
Brian Silvermand7d01102019-02-24 16:11:21 -0800622 Uarts *const uarts = Uarts::global_instance;
623 std::array<CobsPacketizer<uart_to_teensy_size()>, 5> packetizers;
624 std::array<TransmitBuffer, 5> transmit_buffers{
625 {&uarts->cam0, &uarts->cam1, &uarts->cam2, &uarts->cam3, &uarts->cam4}};
626 FrameQueue frame_queue;
627 aos::monotonic_clock::time_point last_camera_send =
628 aos::monotonic_clock::min_time;
Brian Silverman83693e42019-03-02 15:45:52 -0800629 CameraCommand stdin_camera_command = CameraCommand::kNormal;
630 CameraCommand last_roborio_camera_command = CameraCommand::kNormal;
Brian Silverman2294f352019-03-02 16:31:18 -0800631 DebugLight debug_light;
Brian Silverman83693e42019-03-02 15:45:52 -0800632
Brian Silvermand7d01102019-02-24 16:11:21 -0800633 bool first = true;
634 while (true) {
Brian Silverman2294f352019-03-02 16:31:18 -0800635 debug_light.Tick();
636
Brian Silvermand7d01102019-02-24 16:11:21 -0800637 {
638 const auto received_transfer = SpiQueue::global_instance->Tick();
639 if (received_transfer) {
640 const auto unpacked = SpiUnpackToTeensy(*received_transfer);
Brian Silverman83693e42019-03-02 15:45:52 -0800641 if (unpacked) {
642 last_roborio_camera_command = unpacked->camera_command;
643 } else {
Brian Silvermand7d01102019-02-24 16:11:21 -0800644 printf("UART decode error\n");
645 }
646 }
647 }
648
649 {
650 std::array<char, 20> buffer;
651 packetizers[0].ParseData(uarts->cam0.Read(buffer));
652 packetizers[1].ParseData(uarts->cam1.Read(buffer));
653 packetizers[2].ParseData(uarts->cam2.Read(buffer));
654 packetizers[3].ParseData(uarts->cam3.Read(buffer));
655 packetizers[4].ParseData(uarts->cam4.Read(buffer));
656 }
657 for (size_t i = 0; i < packetizers.size(); ++i) {
658 if (!packetizers[i].received_packet().empty()) {
659 const auto decoded =
660 UartUnpackToTeensy(packetizers[i].received_packet());
661 packetizers[i].clear_received_packet();
662 if (decoded) {
Brian Silvermand7d01102019-02-24 16:11:21 -0800663 frame_queue.UpdateFrame(i, *decoded);
664 }
665 }
666 }
667 {
668 bool made_transfer = false;
669 if (!first) {
670 DisableInterrupts disable_interrupts;
671 made_transfer =
672 !SpiQueue::global_instance->HaveTransfer(disable_interrupts);
673 }
674 if (made_transfer) {
675 frame_queue.RemoveLatestFrames();
676 }
677 const auto transfer = frame_queue.MakeTransfer();
678 {
679 DisableInterrupts disable_interrupts;
680 SpiQueue::global_instance->UpdateTransfer(transfer, disable_interrupts);
681 }
682 }
683 {
684 const auto now = aos::monotonic_clock::now();
685 if (last_camera_send + std::chrono::milliseconds(1000) < now) {
686 last_camera_send = now;
687 CameraCalibration calibration{};
688 calibration.teensy_now = aos::monotonic_clock::now();
689 calibration.realtime_now = aos::realtime_clock::min_time;
Brian Silverman83693e42019-03-02 15:45:52 -0800690 if (last_roborio_camera_command != CameraCommand::kNormal) {
691 calibration.camera_command = last_roborio_camera_command;
692 } else {
693 calibration.camera_command = stdin_camera_command;
694 }
Brian Silverman2294f352019-03-02 16:31:18 -0800695 if (calibration.camera_command == CameraCommand::kUsb) {
696 debug_light.set_next_off_time(std::chrono::milliseconds(900));
697 } else if (calibration.camera_command ==
698 CameraCommand::kCameraPassthrough) {
699 debug_light.set_next_off_time(std::chrono::milliseconds(500));
700 } else {
701 debug_light.set_next_off_time(std::chrono::milliseconds(100));
702 }
Brian Silvermand7d01102019-02-24 16:11:21 -0800703 // TODO(Brian): Actually fill out the calibration field.
704 transmit_buffers[0].MaybeWritePacket(calibration);
705 transmit_buffers[1].MaybeWritePacket(calibration);
706 transmit_buffers[2].MaybeWritePacket(calibration);
707 transmit_buffers[3].MaybeWritePacket(calibration);
708 transmit_buffers[4].MaybeWritePacket(calibration);
709 }
710 for (TransmitBuffer &transmit_buffer : transmit_buffers) {
711 transmit_buffer.Tick(now);
712 }
713 }
714
Brian Silverman83693e42019-03-02 15:45:52 -0800715 {
716 const auto stdin_data = printing->ReadStdin();
717 if (!stdin_data.empty()) {
718 switch (stdin_data.back()) {
719 case 'p':
720 printf("Entering passthrough mode\n");
721 stdin_camera_command = CameraCommand::kCameraPassthrough;
722 break;
723 case 'u':
724 printf("Entering USB mode\n");
725 stdin_camera_command = CameraCommand::kUsb;
726 break;
727 case 'n':
728 printf("Entering normal mode\n");
729 stdin_camera_command = CameraCommand::kNormal;
730 break;
731 default:
732 printf("Unrecognized character\n");
733 break;
734 }
735 }
736 }
737
Brian Silvermand7d01102019-02-24 16:11:21 -0800738 first = false;
739 }
740}
741
Brian Silverman3240e102019-02-16 18:24:24 -0800742int Main() {
743 // for background about this startup delay, please see these conversations
744 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
745 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
746 delay(400);
747
748 // Set all interrupts to the second-lowest priority to start with.
749 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
750
751 // Now set priorities for all the ones we care about. They only have meaning
752 // relative to each other, which means centralizing them here makes it a lot
753 // more manageable.
Brian Silvermand7d01102019-02-24 16:11:21 -0800754 NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
755 NVIC_SET_SANE_PRIORITY(IRQ_UART0_STATUS, 0x3);
756 NVIC_SET_SANE_PRIORITY(IRQ_UART1_STATUS, 0x3);
757 NVIC_SET_SANE_PRIORITY(IRQ_UART2_STATUS, 0x3);
758 NVIC_SET_SANE_PRIORITY(IRQ_UART3_STATUS, 0x3);
759 NVIC_SET_SANE_PRIORITY(IRQ_UART4_STATUS, 0x3);
760 // This one is relatively sensitive to latencies. The buffer is ~4800 clock
761 // cycles long.
762 NVIC_SET_SANE_PRIORITY(IRQ_SPI0, 0x2);
763 NVIC_SET_SANE_PRIORITY(IRQ_PORTA, 0x3);
Brian Silverman3240e102019-02-16 18:24:24 -0800764
765 // Set the LED's pin to output mode.
766 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
767 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
768
769 frc971::motors::PrintingParameters printing_parameters;
770 printing_parameters.dedicated_usb = true;
771 const ::std::unique_ptr<frc971::motors::PrintingImplementation> printing =
772 CreatePrinting(printing_parameters);
773 printing->Initialize();
774
775 DMA.CR = M_DMA_EMLM;
776
Brian Silvermand7d01102019-02-24 16:11:21 -0800777 SIM_SCGC1 |= SIM_SCGC1_UART4;
Brian Silverman3240e102019-02-16 18:24:24 -0800778 SIM_SCGC4 |=
779 SIM_SCGC4_UART0 | SIM_SCGC4_UART1 | SIM_SCGC4_UART2 | SIM_SCGC4_UART3;
Brian Silvermand7d01102019-02-24 16:11:21 -0800780 SIM_SCGC6 |= SIM_SCGC6_SPI0;
Brian Silverman3240e102019-02-16 18:24:24 -0800781
782 // SPI0 goes to the roboRIO.
783 // SPI0_PCS0 is SPI_CS.
Brian Silvermand7d01102019-02-24 16:11:21 -0800784 PORTD_PCR0 = PORT_PCR_MUX(2);
Brian Silverman3240e102019-02-16 18:24:24 -0800785 // SPI0_SOUT is SPI_MISO.
786 PORTC_PCR6 = PORT_PCR_DSE | PORT_PCR_MUX(2);
787 // SPI0_SIN is SPI_MOSI.
788 PORTC_PCR7 = PORT_PCR_DSE | PORT_PCR_MUX(2);
789 // SPI0_SCK is SPI_CLK.
790 PORTD_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(2);
Brian Silvermand7d01102019-02-24 16:11:21 -0800791 // SPI_CS_DRIVE
792 PERIPHERAL_BITBAND(GPIOB_PDDR, 17) = 1;
793 PERIPHERAL_BITBAND(GPIOB_PDOR, 17) = 1;
794 PORTB_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(1);
795 // SPI_CS_IN
796 PERIPHERAL_BITBAND(GPIOA_PDDR, 17) = 0;
797 // Set the filter width.
798 PORTA_DFWR = 31;
799 // Enable the filter.
800 PERIPHERAL_BITBAND(PORTA_DFER, 17) = 1;
801 PORTA_PCR17 =
802 PORT_PCR_MUX(1) | PORT_PCR_IRQC(0xC) /* Interrupt when logic 1 */;
803 // Clear the interrupt flag now that we've reconfigured it.
804 PORTA_ISFR = 1 << 17;
Brian Silverman3240e102019-02-16 18:24:24 -0800805
806 // FTM0_CH0 is LED0 (7 in silkscreen, a beacon channel).
807 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
808 // FTM0_CH1 is LED1 (5 in silkscreen, a beacon channel).
809 PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
810 // FTM0_CH7 is LED2 (6 in silkscreen, a beacon channel).
811 PORTD_PCR7 = PORT_PCR_DSE | PORT_PCR_MUX(4);
812 // FTM0_CH5 is LED3 (9 in silkscreen, a vision camera).
813 PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(4);
814
815 // FTM2_CH1 is LED4 (8 in silkscreen, a vision camera).
816 PORTB_PCR19 = PORT_PCR_DSE | PORT_PCR_MUX(3);
817 // FTM2_CH0 is LED5 (for CAM4).
818 PORTB_PCR18 = PORT_PCR_DSE | PORT_PCR_MUX(3);
819
820 // FTM3_CH4 is LED6 (for CAM2).
821 PORTC_PCR8 = PORT_PCR_DSE | PORT_PCR_MUX(3);
822 // FTM3_CH5 is LED7 (for CAM3).
823 PORTC_PCR9 = PORT_PCR_DSE | PORT_PCR_MUX(3);
824 // FTM3_CH6 is LED8 (for CAM1).
825 PORTC_PCR10 = PORT_PCR_DSE | PORT_PCR_MUX(3);
826 // FTM3_CH7 is LED9 (for CAM0).
827 PORTC_PCR11 = PORT_PCR_DSE | PORT_PCR_MUX(3);
828
829 // This hardware has been deactivated, but keep this comment for now to
830 // document which pins it is on.
831#if 0
832 // This is ODROID_EN.
833 PERIPHERAL_BITBAND(GPIOC_PDDR, 0) = 1;
834 PERIPHERAL_BITBAND(GPIOC_PDOR, 0) = 0;
835 PORTC_PCR0 = PORT_PCR_DSE | PORT_PCR_MUX(1);
836 // This is CAM_EN.
837 PERIPHERAL_BITBAND(GPIOB_PDDR, 0) = 1;
838 PERIPHERAL_BITBAND(GPIOB_PDOR, 0) = 0;
839 PORTB_PCR0 = PORT_PCR_DSE | PORT_PCR_MUX(1);
840#endif
841 // This is 5V_PGOOD.
842 PERIPHERAL_BITBAND(GPIOD_PDDR, 6) = 0;
843 PORTD_PCR6 = PORT_PCR_MUX(1);
844
845 // These go to CAM1.
846 // UART0_RX (peripheral) is UART1_RX (schematic).
Brian Silvermand7d01102019-02-24 16:11:21 -0800847 PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(3) | PORT_PCR_PE /* Do a pull */ |
848 0 /* !PS to pull down */;
Brian Silverman3240e102019-02-16 18:24:24 -0800849 // UART0_TX (peripheral) is UART1_TX (schematic).
850 PORTA_PCR14 = PORT_PCR_DSE | PORT_PCR_MUX(3);
851
852 // These go to CAM0.
853 // UART1_RX (peripheral) is UART0_RX (schematic).
Brian Silvermand7d01102019-02-24 16:11:21 -0800854 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(3) | PORT_PCR_PE /* Do a pull */ |
855 0 /* !PS to pull down */;
Brian Silverman3240e102019-02-16 18:24:24 -0800856 // UART1_TX (peripheral) is UART0_TX (schematic).
857 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(3);
858
859 // These go to CAM2.
860 // UART2_RX
Brian Silvermand7d01102019-02-24 16:11:21 -0800861 PORTD_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(3) | PORT_PCR_PE /* Do a pull */ |
862 0 /* !PS to pull down */;
Brian Silverman3240e102019-02-16 18:24:24 -0800863 // UART2_TX
864 PORTD_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(3);
865
866 // These go to CAM3.
867 // UART3_RX
Brian Silvermand7d01102019-02-24 16:11:21 -0800868 PORTB_PCR10 = PORT_PCR_DSE | PORT_PCR_MUX(3) | PORT_PCR_PE /* Do a pull */ |
869 0 /* !PS to pull down */;
Brian Silverman3240e102019-02-16 18:24:24 -0800870 // UART3_TX
871 PORTB_PCR11 = PORT_PCR_DSE | PORT_PCR_MUX(3);
872
873 // These go to CAM4.
874 // UART4_RX
Brian Silvermand7d01102019-02-24 16:11:21 -0800875 PORTE_PCR25 = PORT_PCR_DSE | PORT_PCR_MUX(3) | PORT_PCR_PE /* Do a pull */ |
876 0 /* !PS to pull down */;
Brian Silverman3240e102019-02-16 18:24:24 -0800877 // UART4_TX
878 PORTE_PCR24 = PORT_PCR_DSE | PORT_PCR_MUX(3);
879
880 Uarts uarts;
Brian Silvermand7d01102019-02-24 16:11:21 -0800881 InterruptBufferedSpi spi0{&SPI0, BUS_CLOCK_FREQUENCY};
882 global_spi_instance = &spi0;
883 SpiQueue spi_queue;
Brian Silverman3240e102019-02-16 18:24:24 -0800884
885 // Give everything a chance to get going.
886 delay(100);
887
888 printf("Ram start: %p\n", __bss_ram_start__);
889 printf("Heap start: %p\n", __heap_start__);
890 printf("Heap end: %p\n", __brkval);
891 printf("Stack start: %p\n", __stack_end__);
892
893 uarts.Initialize(115200);
894 NVIC_ENABLE_IRQ(IRQ_UART0_STATUS);
895 NVIC_ENABLE_IRQ(IRQ_UART1_STATUS);
896 NVIC_ENABLE_IRQ(IRQ_UART2_STATUS);
897 NVIC_ENABLE_IRQ(IRQ_UART3_STATUS);
898 NVIC_ENABLE_IRQ(IRQ_UART4_STATUS);
Brian Silvermand7d01102019-02-24 16:11:21 -0800899 spi0.Initialize();
900 NVIC_ENABLE_IRQ(IRQ_SPI0);
901 NVIC_ENABLE_IRQ(IRQ_PORTA);
902
Brian Silverman83693e42019-03-02 15:45:52 -0800903 TransferData(printing.get());
Brian Silverman3240e102019-02-16 18:24:24 -0800904
905 while (true) {
906 }
907}
908
909extern "C" {
910
911int main(void) {
912 return Main();
913}
914
915} // extern "C"
916
917} // namespace
918} // namespace jevois
919} // namespace frc971