blob: c5a1b649a103755445879b013e1dca5dcac9768e [file] [log] [blame] [edit]
#ifndef MOTORS_PERIPHERAL_SPI_H_
#define MOTORS_PERIPHERAL_SPI_H_
#include "absl/types/span.h"
#include "motors/core/kinetis.h"
#include "motors/peripheral/uart_buffer.h"
#include "motors/util.h"
namespace frc971::teensy {
// Simple synchronous interface to a SPI peripheral.
class Spi {
public:
Spi(KINETISK_SPI_t *module, int module_clock_frequency)
: module_(module), module_clock_frequency_(module_clock_frequency) {}
Spi(const Spi &) = delete;
~Spi();
Spi &operator=(const Spi &) = delete;
// Currently hard-coded for slave mode with the parameters we want. In the
// future, we should add more setters to configure things in more detail.
void Initialize();
// Cleras all the hardware queues.
void ClearQueues();
bool SpaceAvailable() const { return module_->SR & M_SPI_TFFF; }
// Only call this if SpaceAvailable() has just returned true.
void WriteFrame(uint32_t frame_flags) {
module_->PUSHR = frame_flags;
module_->SR = M_SPI_TFFF;
}
bool DataAvailable() const { return module_->SR & M_SPI_RFDF; }
// Only call this if DataAvailable() has just returned true.
uint16_t ReadFrame() {
const uint16_t result = module_->POPR;
module_->SR = M_SPI_RFDF;
return result;
}
// Calling code must synchronize all of these.
void EnableTransmitInterrupt() { rser_value_ |= M_SPI_TFFF_RE; }
void DisableTransmitInterrupt() { rser_value_ &= ~M_SPI_TFFF_RE; }
void EnableReceiveInterrupt() { rser_value_ |= M_SPI_RFDF_RE; }
void DisableReceiveInterrupt() { rser_value_ &= ~M_SPI_RFDF_RE; }
void FlushInterruptRequests() { module_->RSER = rser_value_; }
private:
KINETISK_SPI_t *const module_;
const int module_clock_frequency_;
// What we put in RSER.
uint32_t rser_value_ = 0;
// What we put in MCR.
uint32_t mcr_value_ = 0;
};
// Interrupt-based buffered interface to a SPI peripheral.
class InterruptBufferedSpi {
public:
InterruptBufferedSpi(KINETISK_SPI_t *module, int module_clock_frequency)
: spi_(module, module_clock_frequency) {}
~InterruptBufferedSpi();
// Provides access to the underlying Spi wrapper for configuration. Don't do
// anything else with this object besides configure it.
Spi *spi() { return &spi_; }
void Initialize();
// Clears all of the queues, in both hardware and software.
//
// Note that this still leaves a to-be-transmitted byte queued.
void ClearQueues(const DisableInterrupts &);
// Queues up the given data for immediate writing. Blocks only if the queue
// fills up before all of data is enqueued.
void Write(absl::Span<const char> data,
DisableInterrupts *disable_interrupts);
// Reads currently available data.
// Returns all the data which is currently available (possibly none);
// buffer is where to store the result. The return value will be a subspan of
// this.
absl::Span<char> Read(absl::Span<char> buffer,
DisableInterrupts *disable_interrupts);
// Should be called as the body of the interrupt handler.
void HandleInterrupt(const DisableInterrupts &disable_interrupts) {
while (ReadAndWriteFrame(true, disable_interrupts)) {
}
spi_.FlushInterruptRequests();
}
private:
bool WriteFrame(bool disable_empty, const DisableInterrupts &);
bool ReadFrame(const DisableInterrupts &);
bool ReadAndWriteFrame(bool disable_empty,
const DisableInterrupts &disable_interrupts) {
const bool read = ReadFrame(disable_interrupts);
const bool written = WriteFrame(disable_empty, disable_interrupts);
return read || written;
}
Spi spi_;
UartBuffer<1024> transmit_buffer_, receive_buffer_;
// The number of frames we should receive before stopping.
int frames_to_receive_ = 0;
};
} // namespace frc971::teensy
#endif // MOTORS_PERIPHERAL_SPI_H_