mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
652 lines
19 KiB
C++
652 lines
19 KiB
C++
/*----------------------------------------------------------------------------*/
|
|
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
/* the project. */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#include "hal/SPI.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <linux/spi/spidev.h>
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstring>
|
|
|
|
#include <wpi/mutex.h>
|
|
#include <wpi/raw_ostream.h>
|
|
|
|
#include "DigitalInternal.h"
|
|
#include "HALInitializer.h"
|
|
#include "hal/DIO.h"
|
|
#include "hal/HAL.h"
|
|
#include "hal/handles/HandlesInternal.h"
|
|
|
|
using namespace hal;
|
|
|
|
static int32_t m_spiCS0Handle{0};
|
|
static int32_t m_spiCS1Handle{0};
|
|
static int32_t m_spiCS2Handle{0};
|
|
static int32_t m_spiCS3Handle{0};
|
|
static int32_t m_spiMXPHandle{0};
|
|
|
|
static constexpr int32_t kSpiMaxHandles = 5;
|
|
|
|
// Indices 0-3 are for onboard CS0-CS2. Index 4 is for MXP.
|
|
static std::array<wpi::mutex, kSpiMaxHandles> spiHandleMutexes;
|
|
static std::array<wpi::mutex, kSpiMaxHandles> spiApiMutexes;
|
|
static std::array<wpi::mutex, kSpiMaxHandles> spiAccumulatorMutexes;
|
|
|
|
// MXP SPI does not count towards this
|
|
static std::atomic<int32_t> spiPortCount{0};
|
|
|
|
static HAL_DigitalHandle digitalHandles[9]{HAL_kInvalidHandle};
|
|
|
|
static wpi::mutex spiAutoMutex;
|
|
static int32_t spiAutoPort = kSpiMaxHandles;
|
|
static std::atomic_bool spiAutoRunning{false};
|
|
static std::unique_ptr<tDMAManager> spiAutoDMA;
|
|
|
|
static bool SPIInUseByAuto(HAL_SPIPort port) {
|
|
// SPI engine conflicts with any other chip selects on the same SPI device.
|
|
// There are two SPI devices: one for ports 0-3 (onboard), the other for port
|
|
// 4 (MXP).
|
|
if (!spiAutoRunning) return false;
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
return (spiAutoPort >= 0 && spiAutoPort <= 3 && port >= 0 && port <= 3) ||
|
|
(spiAutoPort == 4 && port == 4);
|
|
}
|
|
|
|
namespace hal {
|
|
namespace init {
|
|
void InitializeSPI() {}
|
|
} // namespace init
|
|
} // namespace hal
|
|
|
|
extern "C" {
|
|
|
|
static void CommonSPIPortInit(int32_t* status) {
|
|
// All false cases will set
|
|
if (spiPortCount.fetch_add(1) == 0) {
|
|
// Have not been initialized yet
|
|
initializeDigital(status);
|
|
if (*status != 0) return;
|
|
// MISO
|
|
if ((digitalHandles[3] = HAL_InitializeDIOPort(createPortHandleForSPI(29),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
std::printf("Failed to allocate DIO 29 (MISO)\n");
|
|
return;
|
|
}
|
|
// MOSI
|
|
if ((digitalHandles[4] = HAL_InitializeDIOPort(createPortHandleForSPI(30),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
std::printf("Failed to allocate DIO 30 (MOSI)\n");
|
|
HAL_FreeDIOPort(digitalHandles[3]); // free the first port allocated
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CommonSPIPortFree(void) {
|
|
if (spiPortCount.fetch_sub(1) == 1) {
|
|
// Clean up SPI Handles
|
|
HAL_FreeDIOPort(digitalHandles[3]);
|
|
HAL_FreeDIOPort(digitalHandles[4]);
|
|
}
|
|
}
|
|
|
|
void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) {
|
|
hal::init::CheckInit();
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
int handle;
|
|
if (HAL_GetSPIHandle(port) != 0) return;
|
|
switch (port) {
|
|
case HAL_SPI_kOnboardCS0:
|
|
CommonSPIPortInit(status);
|
|
if (*status != 0) return;
|
|
// CS0 is not a DIO port, so nothing to allocate
|
|
handle = open("/dev/spidev0.0", O_RDWR);
|
|
if (handle < 0) {
|
|
std::printf("Failed to open SPI port %d: %s\n", port,
|
|
std::strerror(errno));
|
|
CommonSPIPortFree();
|
|
return;
|
|
}
|
|
HAL_SetSPIHandle(HAL_SPI_kOnboardCS0, handle);
|
|
break;
|
|
case HAL_SPI_kOnboardCS1:
|
|
CommonSPIPortInit(status);
|
|
if (*status != 0) return;
|
|
// CS1, Allocate
|
|
if ((digitalHandles[0] = HAL_InitializeDIOPort(createPortHandleForSPI(26),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
std::printf("Failed to allocate DIO 26 (CS1)\n");
|
|
CommonSPIPortFree();
|
|
return;
|
|
}
|
|
handle = open("/dev/spidev0.1", O_RDWR);
|
|
if (handle < 0) {
|
|
std::printf("Failed to open SPI port %d: %s\n", port,
|
|
std::strerror(errno));
|
|
CommonSPIPortFree();
|
|
HAL_FreeDIOPort(digitalHandles[0]);
|
|
return;
|
|
}
|
|
HAL_SetSPIHandle(HAL_SPI_kOnboardCS1, handle);
|
|
break;
|
|
case HAL_SPI_kOnboardCS2:
|
|
CommonSPIPortInit(status);
|
|
if (*status != 0) return;
|
|
// CS2, Allocate
|
|
if ((digitalHandles[1] = HAL_InitializeDIOPort(createPortHandleForSPI(27),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
std::printf("Failed to allocate DIO 27 (CS2)\n");
|
|
CommonSPIPortFree();
|
|
return;
|
|
}
|
|
handle = open("/dev/spidev0.2", O_RDWR);
|
|
if (handle < 0) {
|
|
std::printf("Failed to open SPI port %d: %s\n", port,
|
|
std::strerror(errno));
|
|
CommonSPIPortFree();
|
|
HAL_FreeDIOPort(digitalHandles[1]);
|
|
return;
|
|
}
|
|
HAL_SetSPIHandle(HAL_SPI_kOnboardCS2, handle);
|
|
break;
|
|
case HAL_SPI_kOnboardCS3:
|
|
CommonSPIPortInit(status);
|
|
if (*status != 0) return;
|
|
// CS3, Allocate
|
|
if ((digitalHandles[2] = HAL_InitializeDIOPort(createPortHandleForSPI(28),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
std::printf("Failed to allocate DIO 28 (CS3)\n");
|
|
CommonSPIPortFree();
|
|
return;
|
|
}
|
|
handle = open("/dev/spidev0.3", O_RDWR);
|
|
if (handle < 0) {
|
|
std::printf("Failed to open SPI port %d: %s\n", port,
|
|
std::strerror(errno));
|
|
CommonSPIPortFree();
|
|
HAL_FreeDIOPort(digitalHandles[2]);
|
|
return;
|
|
}
|
|
HAL_SetSPIHandle(HAL_SPI_kOnboardCS3, handle);
|
|
break;
|
|
case HAL_SPI_kMXP:
|
|
initializeDigital(status);
|
|
if (*status != 0) return;
|
|
if ((digitalHandles[5] = HAL_InitializeDIOPort(createPortHandleForSPI(14),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
wpi::outs() << "Failed to allocate DIO 14\n";
|
|
return;
|
|
}
|
|
if ((digitalHandles[6] = HAL_InitializeDIOPort(createPortHandleForSPI(15),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
wpi::outs() << "Failed to allocate DIO 15\n";
|
|
HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
|
|
return;
|
|
}
|
|
if ((digitalHandles[7] = HAL_InitializeDIOPort(createPortHandleForSPI(16),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
wpi::outs() << "Failed to allocate DIO 16\n";
|
|
HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
|
|
HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
|
|
return;
|
|
}
|
|
if ((digitalHandles[8] = HAL_InitializeDIOPort(createPortHandleForSPI(17),
|
|
false, status)) ==
|
|
HAL_kInvalidHandle) {
|
|
wpi::outs() << "Failed to allocate DIO 17\n";
|
|
HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
|
|
HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
|
|
HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
|
|
return;
|
|
}
|
|
digitalSystem->writeEnableMXPSpecialFunction(
|
|
digitalSystem->readEnableMXPSpecialFunction(status) | 0x00F0, status);
|
|
handle = open("/dev/spidev1.0", O_RDWR);
|
|
if (handle < 0) {
|
|
std::printf("Failed to open SPI port %d: %s\n", port,
|
|
std::strerror(errno));
|
|
HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
|
|
HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
|
|
HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
|
|
HAL_FreeDIOPort(digitalHandles[8]); // free the fourth port allocated
|
|
return;
|
|
}
|
|
HAL_SetSPIHandle(HAL_SPI_kMXP, handle);
|
|
break;
|
|
default:
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32_t HAL_TransactionSPI(HAL_SPIPort port, const uint8_t* dataToSend,
|
|
uint8_t* dataReceived, int32_t size) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return -1;
|
|
}
|
|
|
|
if (SPIInUseByAuto(port)) return -1;
|
|
|
|
struct spi_ioc_transfer xfer;
|
|
std::memset(&xfer, 0, sizeof(xfer));
|
|
xfer.tx_buf = (__u64)dataToSend;
|
|
xfer.rx_buf = (__u64)dataReceived;
|
|
xfer.len = size;
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
|
|
}
|
|
|
|
int32_t HAL_WriteSPI(HAL_SPIPort port, const uint8_t* dataToSend,
|
|
int32_t sendSize) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return -1;
|
|
}
|
|
|
|
if (SPIInUseByAuto(port)) return -1;
|
|
|
|
struct spi_ioc_transfer xfer;
|
|
std::memset(&xfer, 0, sizeof(xfer));
|
|
xfer.tx_buf = (__u64)dataToSend;
|
|
xfer.len = sendSize;
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
|
|
}
|
|
|
|
int32_t HAL_ReadSPI(HAL_SPIPort port, uint8_t* buffer, int32_t count) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return -1;
|
|
}
|
|
|
|
if (SPIInUseByAuto(port)) return -1;
|
|
|
|
struct spi_ioc_transfer xfer;
|
|
std::memset(&xfer, 0, sizeof(xfer));
|
|
xfer.rx_buf = (__u64)buffer;
|
|
xfer.len = count;
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
|
|
}
|
|
|
|
void HAL_CloseSPI(HAL_SPIPort port) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return;
|
|
}
|
|
|
|
int32_t status = 0;
|
|
HAL_FreeSPIAuto(port, &status);
|
|
|
|
{
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
close(HAL_GetSPIHandle(port));
|
|
}
|
|
|
|
HAL_SetSPIHandle(port, 0);
|
|
if (port < 4) {
|
|
CommonSPIPortFree();
|
|
}
|
|
|
|
switch (port) {
|
|
// Case 0 does not need to do anything
|
|
case 1:
|
|
HAL_FreeDIOPort(digitalHandles[0]);
|
|
break;
|
|
case 2:
|
|
HAL_FreeDIOPort(digitalHandles[1]);
|
|
break;
|
|
case 3:
|
|
HAL_FreeDIOPort(digitalHandles[2]);
|
|
break;
|
|
case 4:
|
|
HAL_FreeDIOPort(digitalHandles[5]);
|
|
HAL_FreeDIOPort(digitalHandles[6]);
|
|
HAL_FreeDIOPort(digitalHandles[7]);
|
|
HAL_FreeDIOPort(digitalHandles[8]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HAL_SetSPISpeed(HAL_SPIPort port, int32_t speed) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MAX_SPEED_HZ, &speed);
|
|
}
|
|
|
|
void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
|
|
HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return;
|
|
}
|
|
|
|
uint8_t mode = 0;
|
|
mode |= (!msbFirst ? 8 : 0);
|
|
mode |= (clkIdleHigh ? 2 : 0);
|
|
mode |= (sampleOnTrailing ? 1 : 0);
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode);
|
|
}
|
|
|
|
void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
if (port < 4) {
|
|
spiSystem->writeChipSelectActiveHigh_Hdr(
|
|
spiSystem->readChipSelectActiveHigh_Hdr(status) | (1 << port), status);
|
|
} else {
|
|
spiSystem->writeChipSelectActiveHigh_MXP(1, status);
|
|
}
|
|
}
|
|
|
|
void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiApiMutexes[port]);
|
|
if (port < 4) {
|
|
spiSystem->writeChipSelectActiveHigh_Hdr(
|
|
spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1 << port), status);
|
|
} else {
|
|
spiSystem->writeChipSelectActiveHigh_MXP(0, status);
|
|
}
|
|
}
|
|
|
|
int32_t HAL_GetSPIHandle(HAL_SPIPort port) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return 0;
|
|
}
|
|
|
|
std::scoped_lock lock(spiHandleMutexes[port]);
|
|
switch (port) {
|
|
case 0:
|
|
return m_spiCS0Handle;
|
|
case 1:
|
|
return m_spiCS1Handle;
|
|
case 2:
|
|
return m_spiCS2Handle;
|
|
case 3:
|
|
return m_spiCS3Handle;
|
|
case 4:
|
|
return m_spiMXPHandle;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void HAL_SetSPIHandle(HAL_SPIPort port, int32_t handle) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiHandleMutexes[port]);
|
|
switch (port) {
|
|
case 0:
|
|
m_spiCS0Handle = handle;
|
|
break;
|
|
case 1:
|
|
m_spiCS1Handle = handle;
|
|
break;
|
|
case 2:
|
|
m_spiCS2Handle = handle;
|
|
break;
|
|
case 3:
|
|
m_spiCS3Handle = handle;
|
|
break;
|
|
case 4:
|
|
m_spiMXPHandle = handle;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (spiAutoPort != kSpiMaxHandles) {
|
|
*status = RESOURCE_IS_ALLOCATED;
|
|
return;
|
|
}
|
|
|
|
// remember the initialized port for other entry points
|
|
spiAutoPort = port;
|
|
|
|
// configure the correct chip select
|
|
if (port < 4) {
|
|
spiSystem->writeAutoSPI1Select(false, status);
|
|
spiSystem->writeAutoChipSelect(port, status);
|
|
} else {
|
|
spiSystem->writeAutoSPI1Select(true, status);
|
|
spiSystem->writeAutoChipSelect(0, status);
|
|
}
|
|
|
|
// configure DMA
|
|
tDMAChannelDescriptor desc;
|
|
spiSystem->getSystemInterface()->getDmaDescriptor(g_SpiAutoData_index, &desc);
|
|
spiAutoDMA = std::make_unique<tDMAManager>(desc.channel, bufferSize, status);
|
|
}
|
|
|
|
void HAL_FreeSPIAuto(HAL_SPIPort port, int32_t* status) {
|
|
if (port < 0 || port >= kSpiMaxHandles) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
if (spiAutoPort != port) return;
|
|
spiAutoPort = kSpiMaxHandles;
|
|
|
|
// disable by setting to internal clock and setting rate=0
|
|
spiSystem->writeAutoRate(0, status);
|
|
spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
|
|
|
|
// stop the DMA
|
|
spiAutoDMA->stop(status);
|
|
|
|
spiAutoDMA.reset(nullptr);
|
|
|
|
spiAutoRunning = false;
|
|
}
|
|
|
|
void HAL_StartSPIAutoRate(HAL_SPIPort port, double period, int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
spiAutoRunning = true;
|
|
|
|
// start the DMA
|
|
spiAutoDMA->start(status);
|
|
|
|
// auto rate is in microseconds
|
|
spiSystem->writeAutoRate(period * 1000000, status);
|
|
|
|
// disable the external clock
|
|
spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
|
|
}
|
|
|
|
void HAL_StartSPIAutoTrigger(HAL_SPIPort port, HAL_Handle digitalSourceHandle,
|
|
HAL_AnalogTriggerType analogTriggerType,
|
|
HAL_Bool triggerRising, HAL_Bool triggerFalling,
|
|
int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
spiAutoRunning = true;
|
|
|
|
// start the DMA
|
|
spiAutoDMA->start(status);
|
|
|
|
// get channel routing
|
|
bool routingAnalogTrigger = false;
|
|
uint8_t routingChannel = 0;
|
|
uint8_t routingModule = 0;
|
|
if (!remapDigitalSource(digitalSourceHandle, analogTriggerType,
|
|
routingChannel, routingModule,
|
|
routingAnalogTrigger)) {
|
|
*status = HAL_HANDLE_ERROR;
|
|
return;
|
|
}
|
|
|
|
// configure external trigger and enable it
|
|
tSPI::tAutoTriggerConfig config;
|
|
config.ExternalClock = 1;
|
|
config.FallingEdge = triggerFalling ? 1 : 0;
|
|
config.RisingEdge = triggerRising ? 1 : 0;
|
|
config.ExternalClockSource_AnalogTrigger = routingAnalogTrigger ? 1 : 0;
|
|
config.ExternalClockSource_Module = routingModule;
|
|
config.ExternalClockSource_Channel = routingChannel;
|
|
spiSystem->writeAutoTriggerConfig(config, status);
|
|
}
|
|
|
|
void HAL_StopSPIAuto(HAL_SPIPort port, int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
// disable by setting to internal clock and setting rate=0
|
|
spiSystem->writeAutoRate(0, status);
|
|
spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
|
|
|
|
// stop the DMA
|
|
spiAutoDMA->stop(status);
|
|
|
|
spiAutoRunning = false;
|
|
}
|
|
|
|
void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend,
|
|
int32_t dataSize, int32_t zeroSize,
|
|
int32_t* status) {
|
|
if (dataSize < 0 || dataSize > 32) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
if (zeroSize < 0 || zeroSize > 127) {
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
// set tx data registers
|
|
for (int32_t i = 0; i < dataSize; ++i)
|
|
spiSystem->writeAutoTx(i >> 2, i & 3, dataToSend[i], status);
|
|
|
|
// set byte counts
|
|
tSPI::tAutoByteCount config;
|
|
config.ZeroByteCount = static_cast<unsigned>(zeroSize) & 0x7f;
|
|
config.TxByteCount = static_cast<unsigned>(dataSize) & 0x1f;
|
|
spiSystem->writeAutoByteCount(config, status);
|
|
}
|
|
|
|
void HAL_ForceSPIAutoRead(HAL_SPIPort port, int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
spiSystem->strobeAutoForceOne(status);
|
|
}
|
|
|
|
int32_t HAL_ReadSPIAutoReceivedData(HAL_SPIPort port, uint32_t* buffer,
|
|
int32_t numToRead, double timeout,
|
|
int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return 0;
|
|
}
|
|
|
|
size_t numRemaining = 0;
|
|
// timeout is in ms
|
|
spiAutoDMA->read(buffer, numToRead, timeout * 1000, &numRemaining, status);
|
|
return numRemaining;
|
|
}
|
|
|
|
int32_t HAL_GetSPIAutoDroppedCount(HAL_SPIPort port, int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return 0;
|
|
}
|
|
|
|
return spiSystem->readTransferSkippedFullCount(status);
|
|
}
|
|
|
|
void HAL_ConfigureSPIAutoStall(HAL_SPIPort port, int32_t csToSclkTicks,
|
|
int32_t stallTicks, int32_t pow2BytesPerRead,
|
|
int32_t* status) {
|
|
std::scoped_lock lock(spiAutoMutex);
|
|
// FPGA only has one auto SPI engine
|
|
if (port != spiAutoPort) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
tSPI::tStallConfig stallConfig;
|
|
stallConfig.CsToSclkTicks = static_cast<uint8_t>(csToSclkTicks);
|
|
stallConfig.StallTicks = static_cast<uint16_t>(stallTicks);
|
|
stallConfig.Pow2BytesPerRead = static_cast<uint8_t>(pow2BytesPerRead);
|
|
spiSystem->writeStallConfig(stallConfig, status);
|
|
}
|
|
|
|
} // extern "C"
|