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