elbear_arduino_bsp/libraries/SPI/src/SPI.cpp
KLASSENTS eb9b1aa0c9 v0.5.1
- в модулях Wire, SPI, Serial приведена в соответствие нумерация используемых экземпляров и периферии микроконтроллера.
- в функции analogWrite() перед запуском ШИМ проверяется, не запущен ли уже указанный канал.
- добавлена возможность переопределения функции main() в скетчах.
- при старте программы задается граница кэшируемой области SPIFI в соответствии с размером текущего исполняемого кода.
- исправление выявленных ошибок.
Co-authored-by: KLASSENTS <klassen@elron.tech>
Co-committed-by: KLASSENTS <klassen@elron.tech>
2025-05-30 12:24:08 +03:00

298 lines
8.0 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 SPI1(1);
#if SPI_COMMON_QTY > 1
SPIClass SPI0(0);
#endif
// default interface
SPIClass& SPI = DEFAULT_SPI;
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;
}