elbear_arduino_bsp/libraries/SPI/src/SPI.cpp

295 lines
7.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "SPI.h"
#include "mik32_hal_spi.h"
SPI_HandleTypeDef hspi;
bool newConfig = false;
bool isInited = false;
uint32_t currentSpeed = 0;
int8_t currentDataOrder = MSBFIRST;
int8_t currentDataMode = -1;
static uint8_t reverse_bits(uint8_t byte);
// ------------------------------------------------------------------ //
void SPISettings::spiUpdateSettings(uint32_t speedMaximum, uint8_t dataOrder, uint8_t dataMode)
{
// update config only if something has changed
if ((currentSpeed != speedMaximum) || (currentDataOrder != dataOrder) ||
(currentDataMode != dataMode))
{
// Find the fastest clock that is less than or equal to the
// given clock rate. If nothing is slow enough - use the slowest.
// mik32v2 has the set of deviders, that can be calculate as:
// div = 2 << (0...7). Value in braсkets is a value for config register
uint8_t divRegVal = 0; // start from minimal divider (maximum speed)
while(divRegVal <= 7)
{
if ((F_CPU/(2 << divRegVal)) <= speedMaximum)
// find suitable divider
break;
divRegVal++;
}
// if break didn't call in cycle, it will be the greatest divRegVal (and divider)
// update params in struct
hspi.Init.CLKPhase = dataMode & 0b00000001;
hspi.Init.CLKPolarity = (dataMode & 0b00000010)>>1;
hspi.Init.BaudRateDiv = divRegVal;
currentSpeed = speedMaximum;
currentDataOrder = dataOrder;
currentDataMode = dataMode;
newConfig = true;
}
}
// ------------------------------------------------------------------ //
SPIClass SPI;
bool SPIClass::spiInUse = false;
uint8_t SPIClass::interruptMode = 0;
uint8_t SPIClass::interruptMask = 0;
void SPIClass::begin()
{
spi_onBegin();
// set constant parameters in spi struct
hspi.Instance = SPI_1;
hspi.Init.SPI_Mode = HAL_SPI_MODE_MASTER; // only master mode used
hspi.Init.ThresholdTX = 4;
hspi.Init.Decoder = SPI_DECODER_NONE;
hspi.Init.ManualCS = SPI_MANUALCS_ON;
hspi.Init.ChipSelect = SPI_CS_NONE;
// adjustable parameters default values as in SPISettings()
hspi.Init.BaudRateDiv = SPI_CLOCK_DIV8;
hspi.Init.CLKPhase = SPI_MODE0 & 0b00000001;
hspi.Init.CLKPolarity = (SPI_MODE0 & 0b00000010)>>1;
spiInUse = true;
}
void SPIClass::end()
{
if (spiInUse && isInited)
{
// turn off spi
HAL_SPI_Disable(&hspi);
// deinit spi gpio pins
if (hspi.Instance == SPI_1) // only spi1 is using in currunt version
{
HAL_GPIO_PinConfig(GPIO_1, (HAL_PinsTypeDef)(GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2),
HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA);
}
spi_onEnd();
spiInUse = false;
isInited = false;
interruptMode = 0;
interruptMask = 0;
}
}
void SPIClass::usingInterrupt(uint8_t interruptNumber)
{
if(interruptNumber < EXTERNAL_INTERRUPTS_QTY)
{
noInterrupts(); // prevent transactionBegin
interruptMask |= (1 << interruptNumber); // add new interrupt to mask
if (!interruptMode)
interruptMode = 1;
interrupts();
}
}
void SPIClass::notUsingInterrupt(uint8_t interruptNumber)
{
if(interruptNumber < EXTERNAL_INTERRUPTS_QTY)
{
noInterrupts(); // prevent transactionBegin
interruptMask &= ~(1<<interruptNumber); // delete interrupt from mask
if (!interruptMask)
interruptMode = 0;
interrupts();
}
}
void SPIClass::beginTransaction(SPISettings settings)
{
// disable interrupts in use if necessary
if (spiInUse && (interruptMode > 0))
{
for (uint8_t i = 0; i < EXTERNAL_INTERRUPTS_QTY; i++)
{
if (interruptMask & (1 << i))
// disable every interrupt by it's number
disableInterrupt(i);
}
}
// initialize spi bus or update config
if(spiInUse && ((!isInited) || newConfig))
{
// initialize spi with given settings
if (HAL_SPI_Init(&hspi) != HAL_OK)
ErrorMsgHandler("SPI.beginTransaction(): initialization error");
else
isInited = true;
}
}
uint8_t SPIClass::transfer(uint8_t data)
{
uint8_t rxByte = 0;
if (isInited)
{
// reverse bits if LSB mode needed
if (currentDataOrder == LSBFIRST)
data = reverse_bits(data);
// send and recieve data
HAL_StatusTypeDef SPI_Status = HAL_SPI_Exchange(&hspi, &data, &rxByte, 1, SPI_TIMEOUT_DEFAULT*2);
if (SPI_Status != HAL_OK)
HAL_SPI_ClearError(&hspi);
// reverse bits again if LSB mode needed
if (currentDataOrder == LSBFIRST)
rxByte = reverse_bits(rxByte);
}
return rxByte;
}
uint16_t SPIClass::transfer16(uint16_t data)
{
uint8_t buf[2];
uint16_t rxVal = 0;
if (isInited)
{
// prepare data for send
if (currentDataOrder == LSBFIRST)
{
// least significant byte is forward and reverse bits inside each byte
buf[0] = reverse_bits(data&0xFF);
buf[1] = reverse_bits((data>>8)&0xFF);
}
else
{
// most significant byte is forward
buf[0] = (data>>8)&0xFF;
buf[1] = data&0xFF;
}
// send and recieve data
HAL_StatusTypeDef SPI_Status = HAL_SPI_Exchange(&hspi, buf, buf, 2, SPI_TIMEOUT_DEFAULT*2);
if (SPI_Status != HAL_OK)
HAL_SPI_ClearError(&hspi);
// process the received data
if (currentDataOrder == LSBFIRST)
// reverse bits for LSB mode
rxVal = ( ((uint16_t)reverse_bits(buf[1]) ) << 8) | reverse_bits(buf[0]);
else
rxVal = ( ((uint16_t)buf[0]) << 8) | buf[1];
}
return rxVal;
}
void SPIClass::transfer(void *buf, size_t count)
{
if (count == 0)
return;
if (isInited)
{
uint8_t *p = (uint8_t *)buf;
// reverse bits in buffer if LSB mode needed
if (currentDataOrder == LSBFIRST)
{
for (uint32_t i = 0; i < count; i++)
*(p+i) = reverse_bits(*(p+i));
}
// send and recieve data using the same buffer
HAL_StatusTypeDef SPI_Status = HAL_SPI_Exchange(&hspi, (uint8_t*)buf, (uint8_t*)buf, count, SPI_TIMEOUT_DEFAULT*2);
if (SPI_Status != HAL_OK)
HAL_SPI_ClearError(&hspi);
// reverse bits if LSB mode needed
if (currentDataOrder == LSBFIRST)
{
p = (uint8_t *)buf; // return to buf beginning
for (uint32_t i = 0; i < count; i++)
*(p+i) = reverse_bits(*(p+i));
}
}
}
void SPIClass::endTransaction(void)
{
// enable interrupts in use
if (spiInUse && (interruptMode > 0))
{
for (uint8_t i = 0; i < EXTERNAL_INTERRUPTS_QTY; i++)
{
if (interruptMask & (1 << i))
// enable every interrupt in use by it's number
enableInterrupt(i);
}
}
}
// ------------------------------------ //
void SPIClass::setBitOrder(uint8_t bitOrder)
{
if (spiInUse)
currentDataOrder = bitOrder;
else
ErrorMsgHandler("SPI.setBitOrder():SPI.begin() need to be called first");
}
void SPIClass::setDataMode(uint8_t dataMode)
{
if (spiInUse)
{
hspi.Init.CLKPhase = (dataMode&0b00000001);
hspi.Init.CLKPolarity = (dataMode&0b00000010)>>1;
HAL_SPI_Set_Clock_Mode(&hspi);
currentDataMode = dataMode;
}
else
ErrorMsgHandler("SPI.setDataMode():SPI.begin() need to be called first");
}
void SPIClass::setClockDivider(uint8_t clockDiv)
{
if (spiInUse)
{
// if divider is valid
if ((clockDiv == SPI_CLOCK_DIV2) || (clockDiv == SPI_CLOCK_DIV4) ||
(clockDiv == SPI_CLOCK_DIV8) || (clockDiv == SPI_CLOCK_DIV16) ||
(clockDiv == SPI_CLOCK_DIV32) || (clockDiv == SPI_CLOCK_DIV64) ||
(clockDiv == SPI_CLOCK_DIV128) || (clockDiv == SPI_CLOCK_DIV256))
{
hspi.Init.BaudRateDiv = clockDiv;
currentSpeed = F_CPU >> (clockDiv+1);
HAL_SPI_Set_Clock_Divider(&hspi);
}
else
ErrorMsgHandler("SPI.setClockDivider(): Invalid clock devider");
}
else
ErrorMsgHandler("SPI.setClockDivider():SPI.begin() need to be called first");
}
static uint8_t reverse_bits(uint8_t byte)
{
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
return byte;
}