elbear_arduino_bsp/libraries/SPI/src/SPI.cpp
KlassenTS 1c8e06634c v0.5.0
- добавлена поддержка платы ELBEAR ACE-NANO;
- добавлена поддержка плат ELSOMIK OEM и SE;
- добавлена возможность работы в режиме отладки для всех плат, входящих в состав пакета. Доступно для версии ArduinoIDE 2 и выше;
- добавлена поддержка библиотеки FreeRTOS;
- добавлена поддержка библиотеки IRremote;
- добавлена поддержка библиотеки OneWire;
- добавлена поддержка аппаратного I2C0 для плат START-MIK32 и ELSOMIK. Для работы с ним доступен экземпляр класса Wire1;
- добавлена поддержка аппаратного SPI0 для всех плат, входящих в пакет. Для работы с ним доступен экземпляр класса SPI1;
- увеличено быстродействие функций digitalWrite, digitalRead;
- исправлены известные ошибки.
Co-authored-by: KlassenTS <klassen@elron.tech>
Co-committed-by: KlassenTS <klassen@elron.tech>
2025-04-28 07:06:08 +03:00

296 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"
SPIClass SPI(1);
#if SPI_COMMON_QTY > 1
SPIClass SPI1(0);
#endif
static uint8_t reverse_bits(uint8_t byte);
void SPIClass::updateSettings(uint32_t speedMaximum, uint8_t dataOrder, uint8_t dataMode)
{
// update config only if something has changed
if ((_speed != speedMaximum) || (_dataOrder != dataOrder) || (_dataMode != 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;
_speed = speedMaximum;
_dataOrder = dataOrder;
_dataMode = dataMode;
_newConfig = true;
}
}
void SPIClass::begin()
{
spi_onBegin(_spiNum);
// set constant parameters in spi struct
if (_spiNum == 0)
_hspi.Instance = SPI_0;
else
_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_0)
{
HAL_GPIO_PinConfig(GPIO_0, (HAL_PinsTypeDef)(GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2),
HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA);
}
else if (_hspi.Instance == SPI_1)
{
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(_spiNum);
_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)
{
// update SPI settings
updateSettings(settings.speedMaximum, settings.newDataOrder, settings.newDataMode);
// 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 needed
if(_spiInUse && ((!_isInited) || _newConfig))
{
// initialize spi with given settings
if (HAL_SPI_Init(&_hspi) != HAL_OK)
ErrorMsgHandler("SPI.beginTransaction(): initialization error");
else
{
_isInited = true;
_newConfig = false;
}
}
}
uint8_t SPIClass::transfer(uint8_t data)
{
uint8_t rxByte = 0;
if (_isInited)
{
// reverse bits if LSB mode needed
if (_dataOrder == 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 (_dataOrder == 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 (_dataOrder == 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 (_dataOrder == 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 (_dataOrder == 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 (_dataOrder == 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)
_dataOrder = 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);
_dataMode = 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;
_speed = 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;
}