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