blob: c5a1b649a103755445879b013e1dca5dcac9768e [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
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080010namespace frc971::teensy {
Brian Silverman333fc9e2019-02-24 15:09:17 -080011
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.
Austin Schuh7fe04492022-01-02 13:37:21 -080044 void EnableTransmitInterrupt() { rser_value_ |= M_SPI_TFFF_RE; }
45 void DisableTransmitInterrupt() { rser_value_ &= ~M_SPI_TFFF_RE; }
46 void EnableReceiveInterrupt() { rser_value_ |= M_SPI_RFDF_RE; }
47 void DisableReceiveInterrupt() { rser_value_ &= ~M_SPI_RFDF_RE; }
48 void FlushInterruptRequests() { module_->RSER = rser_value_; }
Brian Silverman333fc9e2019-02-24 15:09:17 -080049
50 private:
51 KINETISK_SPI_t *const module_;
52 const int module_clock_frequency_;
53
54 // What we put in RSER.
55 uint32_t rser_value_ = 0;
56
57 // What we put in MCR.
58 uint32_t mcr_value_ = 0;
59};
60
61// Interrupt-based buffered interface to a SPI peripheral.
62class InterruptBufferedSpi {
63 public:
64 InterruptBufferedSpi(KINETISK_SPI_t *module, int module_clock_frequency)
65 : spi_(module, module_clock_frequency) {}
66 ~InterruptBufferedSpi();
67
68 // Provides access to the underlying Spi wrapper for configuration. Don't do
69 // anything else with this object besides configure it.
70 Spi *spi() { return &spi_; }
71
72 void Initialize();
73
74 // Clears all of the queues, in both hardware and software.
75 //
76 // Note that this still leaves a to-be-transmitted byte queued.
77 void ClearQueues(const DisableInterrupts &);
78
79 // Queues up the given data for immediate writing. Blocks only if the queue
80 // fills up before all of data is enqueued.
Austin Schuh7fe04492022-01-02 13:37:21 -080081 void Write(absl::Span<const char> data,
82 DisableInterrupts *disable_interrupts);
Brian Silverman333fc9e2019-02-24 15:09:17 -080083
84 // Reads currently available data.
85 // Returns all the data which is currently available (possibly none);
86 // buffer is where to store the result. The return value will be a subspan of
87 // this.
Austin Schuh7fe04492022-01-02 13:37:21 -080088 absl::Span<char> Read(absl::Span<char> buffer,
89 DisableInterrupts *disable_interrupts);
Brian Silverman333fc9e2019-02-24 15:09:17 -080090
91 // Should be called as the body of the interrupt handler.
92 void HandleInterrupt(const DisableInterrupts &disable_interrupts) {
Austin Schuh7fe04492022-01-02 13:37:21 -080093 while (ReadAndWriteFrame(true, disable_interrupts)) {
94 }
Brian Silverman333fc9e2019-02-24 15:09:17 -080095 spi_.FlushInterruptRequests();
96 }
97
98 private:
99 bool WriteFrame(bool disable_empty, const DisableInterrupts &);
100 bool ReadFrame(const DisableInterrupts &);
101 bool ReadAndWriteFrame(bool disable_empty,
102 const DisableInterrupts &disable_interrupts) {
103 const bool read = ReadFrame(disable_interrupts);
104 const bool written = WriteFrame(disable_empty, disable_interrupts);
105 return read || written;
106 }
107
108 Spi spi_;
109 UartBuffer<1024> transmit_buffer_, receive_buffer_;
110 // The number of frames we should receive before stopping.
111 int frames_to_receive_ = 0;
112};
113
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800114} // namespace frc971::teensy
Brian Silverman333fc9e2019-02-24 15:09:17 -0800115
116#endif // MOTORS_PERIPHERAL_SPI_H_