blob: c0bb76745d036fa59bb2a2acb5449051f6d3c081 [file] [log] [blame]
#include "motors/peripheral/spi.h"
#include <stdint.h>
#include "motors/core/time.h"
namespace frc971 {
namespace teensy {
Spi::~Spi() {
DisableTransmitInterrupt();
DisableReceiveInterrupt();
FlushInterruptRequests();
}
void Spi::Initialize() {
// Use all the defaults (including slave mode).
mcr_value_ =
V_SPI_PCSIS(1) /* CS0 is active low. It appears to ignore this though? */;
module_->MCR = mcr_value_;
module_->CTAR[0] = V_SPI_FMSZ(7) /* 8 bit frames */ |
M_SPI_CPOL /* Idle high clock */ |
M_SPI_CPHA /* Data changed on leading clock edge */;
}
void Spi::ClearQueues() {
module_->MCR = mcr_value_ | M_SPI_HALT;
const bool receive_overflow = module_->SR & M_SPI_RFOF;
module_->MCR = mcr_value_ | M_SPI_CLR_TXF | M_SPI_CLR_RXF | M_SPI_DIS_TXF |
M_SPI_DIS_RXF | M_SPI_HALT;
if (receive_overflow) {
(void)module_->POPR;
}
module_->SR = M_SPI_TFUF | M_SPI_TFFF | M_SPI_RFOF | M_SPI_RFDF;
module_->MCR = mcr_value_;
}
InterruptBufferedSpi::~InterruptBufferedSpi() {
spi_.DisableReceiveInterrupt();
spi_.FlushInterruptRequests();
}
void InterruptBufferedSpi::Initialize() {
spi_.Initialize();
transmit_buffer_.clear();
receive_buffer_.clear();
frames_to_receive_ = 0;
}
void InterruptBufferedSpi::ClearQueues(const DisableInterrupts &) {
spi_.ClearQueues();
transmit_buffer_.clear();
receive_buffer_.clear();
frames_to_receive_ = 0;
spi_.DisableTransmitInterrupt();
spi_.DisableReceiveInterrupt();
spi_.FlushInterruptRequests();
}
void InterruptBufferedSpi::Write(gsl::span<const char> data,
DisableInterrupts *disable_interrupts) {
frames_to_receive_ += data.size();
// Until we get all of the data queued, we'll call WriteFrames from our
// context, so don't enable the interrupt yet.
while (!data.empty()) {
const int bytes_written = transmit_buffer_.PushSpan(data);
data = data.subspan(bytes_written);
while (ReadAndWriteFrame(data.empty(), *disable_interrupts)) {
}
ReenableInterrupts{disable_interrupts};
}
// If there's still data queued, then we need to enable the interrupt so it
// can push it to the hardware.
if (!transmit_buffer_.empty()) {
spi_.EnableTransmitInterrupt();
}
if (frames_to_receive_ > 0) {
spi_.EnableReceiveInterrupt();
}
if (!transmit_buffer_.empty() || frames_to_receive_ > 0) {
spi_.FlushInterruptRequests();
}
}
gsl::span<char> InterruptBufferedSpi::Read(
gsl::span<char> buffer, DisableInterrupts *disable_interrupts) {
size_t bytes_read = 0;
{
const gsl::span<const char> read_data =
receive_buffer_.PopSpan(buffer.size());
std::copy(read_data.begin(), read_data.end(), buffer.begin());
bytes_read += read_data.size();
}
ReenableInterrupts{disable_interrupts};
{
const gsl::span<const char> read_data =
receive_buffer_.PopSpan(buffer.size() - bytes_read);
std::copy(read_data.begin(), read_data.end(),
buffer.subspan(bytes_read).begin());
bytes_read += read_data.size();
}
return buffer.first(bytes_read);
}
bool InterruptBufferedSpi::WriteFrame(bool disable_empty,
const DisableInterrupts &) {
if (transmit_buffer_.empty()) {
if (disable_empty) {
spi_.DisableTransmitInterrupt();
}
return false;
}
if (!spi_.SpaceAvailable()) {
return false;
}
spi_.WriteFrame(transmit_buffer_.PopSingle());
return true;
}
bool InterruptBufferedSpi::ReadFrame(const DisableInterrupts &) {
if (!spi_.DataAvailable()) {
return false;
}
const auto frame = spi_.ReadFrame();
--frames_to_receive_;
if (frames_to_receive_ <= 0) {
spi_.DisableReceiveInterrupt();
}
if (receive_buffer_.full()) {
return false;
}
receive_buffer_.PushSingle(frame);
return true;
}
} // namespace teensy
} // namespace frc971