blob: c0bb76745d036fa59bb2a2acb5449051f6d3c081 [file] [log] [blame]
Brian Silverman333fc9e2019-02-24 15:09:17 -08001#include "motors/peripheral/spi.h"
2
3#include <stdint.h>
4
5#include "motors/core/time.h"
6
7namespace frc971 {
8namespace teensy {
9
10Spi::~Spi() {
11 DisableTransmitInterrupt();
12 DisableReceiveInterrupt();
13 FlushInterruptRequests();
14}
15
16void Spi::Initialize() {
17 // Use all the defaults (including slave mode).
18 mcr_value_ =
19 V_SPI_PCSIS(1) /* CS0 is active low. It appears to ignore this though? */;
20 module_->MCR = mcr_value_;
21 module_->CTAR[0] = V_SPI_FMSZ(7) /* 8 bit frames */ |
22 M_SPI_CPOL /* Idle high clock */ |
23 M_SPI_CPHA /* Data changed on leading clock edge */;
24}
25
26void Spi::ClearQueues() {
27 module_->MCR = mcr_value_ | M_SPI_HALT;
28 const bool receive_overflow = module_->SR & M_SPI_RFOF;
29 module_->MCR = mcr_value_ | M_SPI_CLR_TXF | M_SPI_CLR_RXF | M_SPI_DIS_TXF |
30 M_SPI_DIS_RXF | M_SPI_HALT;
31 if (receive_overflow) {
32 (void)module_->POPR;
33 }
34 module_->SR = M_SPI_TFUF | M_SPI_TFFF | M_SPI_RFOF | M_SPI_RFDF;
35 module_->MCR = mcr_value_;
36}
37
38InterruptBufferedSpi::~InterruptBufferedSpi() {
39 spi_.DisableReceiveInterrupt();
40 spi_.FlushInterruptRequests();
41}
42
43void InterruptBufferedSpi::Initialize() {
44 spi_.Initialize();
45 transmit_buffer_.clear();
46 receive_buffer_.clear();
47 frames_to_receive_ = 0;
48}
49
50void InterruptBufferedSpi::ClearQueues(const DisableInterrupts &) {
51 spi_.ClearQueues();
52 transmit_buffer_.clear();
53 receive_buffer_.clear();
54 frames_to_receive_ = 0;
55 spi_.DisableTransmitInterrupt();
56 spi_.DisableReceiveInterrupt();
57 spi_.FlushInterruptRequests();
58}
59
60void InterruptBufferedSpi::Write(gsl::span<const char> data,
61 DisableInterrupts *disable_interrupts) {
62 frames_to_receive_ += data.size();
63 // Until we get all of the data queued, we'll call WriteFrames from our
64 // context, so don't enable the interrupt yet.
65 while (!data.empty()) {
66 const int bytes_written = transmit_buffer_.PushSpan(data);
67 data = data.subspan(bytes_written);
68 while (ReadAndWriteFrame(data.empty(), *disable_interrupts)) {
69 }
70 ReenableInterrupts{disable_interrupts};
71 }
72 // If there's still data queued, then we need to enable the interrupt so it
73 // can push it to the hardware.
74 if (!transmit_buffer_.empty()) {
75 spi_.EnableTransmitInterrupt();
76 }
77 if (frames_to_receive_ > 0) {
78 spi_.EnableReceiveInterrupt();
79 }
80 if (!transmit_buffer_.empty() || frames_to_receive_ > 0) {
81 spi_.FlushInterruptRequests();
82 }
83}
84
85gsl::span<char> InterruptBufferedSpi::Read(
86 gsl::span<char> buffer, DisableInterrupts *disable_interrupts) {
87 size_t bytes_read = 0;
88 {
89 const gsl::span<const char> read_data =
90 receive_buffer_.PopSpan(buffer.size());
91 std::copy(read_data.begin(), read_data.end(), buffer.begin());
92 bytes_read += read_data.size();
93 }
94
95 ReenableInterrupts{disable_interrupts};
96
97 {
98 const gsl::span<const char> read_data =
99 receive_buffer_.PopSpan(buffer.size() - bytes_read);
100 std::copy(read_data.begin(), read_data.end(),
101 buffer.subspan(bytes_read).begin());
102 bytes_read += read_data.size();
103 }
104
105 return buffer.first(bytes_read);
106}
107
108bool InterruptBufferedSpi::WriteFrame(bool disable_empty,
109 const DisableInterrupts &) {
110 if (transmit_buffer_.empty()) {
111 if (disable_empty) {
112 spi_.DisableTransmitInterrupt();
113 }
114 return false;
115 }
116 if (!spi_.SpaceAvailable()) {
117 return false;
118 }
119 spi_.WriteFrame(transmit_buffer_.PopSingle());
120 return true;
121}
122
123bool InterruptBufferedSpi::ReadFrame(const DisableInterrupts &) {
124 if (!spi_.DataAvailable()) {
125 return false;
126 }
127 const auto frame = spi_.ReadFrame();
128 --frames_to_receive_;
129 if (frames_to_receive_ <= 0) {
130 spi_.DisableReceiveInterrupt();
131 }
132 if (receive_buffer_.full()) {
133 return false;
134 }
135 receive_buffer_.PushSingle(frame);
136 return true;
137}
138
139} // namespace teensy
140} // namespace frc971