blob: f865703799c1ef3db9c59bfd6fc880f76db2d7b9 [file] [log] [blame]
Brian Silverman333fc9e2019-02-24 15:09:17 -08001#ifndef MOTORS_PERIPHERAL_SPI_H_
2#define MOTORS_PERIPHERAL_SPI_H_
3
Austin Schuh7fe04492022-01-02 13:37:21 -08004#include "absl/types/span.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07005
Brian Silverman333fc9e2019-02-24 15:09:17 -08006#include "motors/core/kinetis.h"
7#include "motors/peripheral/uart_buffer.h"
8#include "motors/util.h"
Brian Silverman333fc9e2019-02-24 15:09:17 -08009
10namespace frc971 {
11namespace teensy {
12
13// Simple synchronous interface to a SPI peripheral.
14class Spi {
15 public:
16 Spi(KINETISK_SPI_t *module, int module_clock_frequency)
17 : module_(module), module_clock_frequency_(module_clock_frequency) {}
18 Spi(const Spi &) = delete;
19 ~Spi();
20 Spi &operator=(const Spi &) = delete;
21
22 // Currently hard-coded for slave mode with the parameters we want. In the
23 // future, we should add more setters to configure things in more detail.
24 void Initialize();
25
26 // Cleras all the hardware queues.
27 void ClearQueues();
28
29 bool SpaceAvailable() const { return module_->SR & M_SPI_TFFF; }
30 // Only call this if SpaceAvailable() has just returned true.
31 void WriteFrame(uint32_t frame_flags) {
32 module_->PUSHR = frame_flags;
33 module_->SR = M_SPI_TFFF;
34 }
35
36 bool DataAvailable() const { return module_->SR & M_SPI_RFDF; }
37 // Only call this if DataAvailable() has just returned true.
38 uint16_t ReadFrame() {
39 const uint16_t result = module_->POPR;
40 module_->SR = M_SPI_RFDF;
41 return result;
42 }
43
44 // Calling code must synchronize all of these.
Austin Schuh7fe04492022-01-02 13:37:21 -080045 void EnableTransmitInterrupt() { rser_value_ |= M_SPI_TFFF_RE; }
46 void DisableTransmitInterrupt() { rser_value_ &= ~M_SPI_TFFF_RE; }
47 void EnableReceiveInterrupt() { rser_value_ |= M_SPI_RFDF_RE; }
48 void DisableReceiveInterrupt() { rser_value_ &= ~M_SPI_RFDF_RE; }
49 void FlushInterruptRequests() { module_->RSER = rser_value_; }
Brian Silverman333fc9e2019-02-24 15:09:17 -080050
51 private:
52 KINETISK_SPI_t *const module_;
53 const int module_clock_frequency_;
54
55 // What we put in RSER.
56 uint32_t rser_value_ = 0;
57
58 // What we put in MCR.
59 uint32_t mcr_value_ = 0;
60};
61
62// Interrupt-based buffered interface to a SPI peripheral.
63class InterruptBufferedSpi {
64 public:
65 InterruptBufferedSpi(KINETISK_SPI_t *module, int module_clock_frequency)
66 : spi_(module, module_clock_frequency) {}
67 ~InterruptBufferedSpi();
68
69 // Provides access to the underlying Spi wrapper for configuration. Don't do
70 // anything else with this object besides configure it.
71 Spi *spi() { return &spi_; }
72
73 void Initialize();
74
75 // Clears all of the queues, in both hardware and software.
76 //
77 // Note that this still leaves a to-be-transmitted byte queued.
78 void ClearQueues(const DisableInterrupts &);
79
80 // Queues up the given data for immediate writing. Blocks only if the queue
81 // fills up before all of data is enqueued.
Austin Schuh7fe04492022-01-02 13:37:21 -080082 void Write(absl::Span<const char> data,
83 DisableInterrupts *disable_interrupts);
Brian Silverman333fc9e2019-02-24 15:09:17 -080084
85 // Reads currently available data.
86 // Returns all the data which is currently available (possibly none);
87 // buffer is where to store the result. The return value will be a subspan of
88 // this.
Austin Schuh7fe04492022-01-02 13:37:21 -080089 absl::Span<char> Read(absl::Span<char> buffer,
90 DisableInterrupts *disable_interrupts);
Brian Silverman333fc9e2019-02-24 15:09:17 -080091
92 // Should be called as the body of the interrupt handler.
93 void HandleInterrupt(const DisableInterrupts &disable_interrupts) {
Austin Schuh7fe04492022-01-02 13:37:21 -080094 while (ReadAndWriteFrame(true, disable_interrupts)) {
95 }
Brian Silverman333fc9e2019-02-24 15:09:17 -080096 spi_.FlushInterruptRequests();
97 }
98
99 private:
100 bool WriteFrame(bool disable_empty, const DisableInterrupts &);
101 bool ReadFrame(const DisableInterrupts &);
102 bool ReadAndWriteFrame(bool disable_empty,
103 const DisableInterrupts &disable_interrupts) {
104 const bool read = ReadFrame(disable_interrupts);
105 const bool written = WriteFrame(disable_empty, disable_interrupts);
106 return read || written;
107 }
108
109 Spi spi_;
110 UartBuffer<1024> transmit_buffer_, receive_buffer_;
111 // The number of frames we should receive before stopping.
112 int frames_to_receive_ = 0;
113};
114
115} // namespace teensy
116} // namespace frc971
117
118#endif // MOTORS_PERIPHERAL_SPI_H_