Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • avn/swd/skyward-boardcore
  • emilio.corigliano/skyward-boardcore
  • ettore.pane/skyward-boardcore
  • giulia.facchi/skyward-boardcore
  • valerio.flamminii/skyward-boardcore
  • nicolo.caruso/skyward-boardcore
6 results
Select Git revision
Show changes
Commits on Source (8)
Showing
with 948 additions and 1590 deletions
......@@ -187,9 +187,6 @@ sbs_target(test-can-loopback stm32f429zi_skyward_death_stack_x)
add_executable(test-can-protocol src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp)
sbs_target(test-can-protocol stm32f429zi_skyward_death_stack_x)
add_executable(test-dma-spi src/tests/drivers/dma/test-dma-spi.cpp)
sbs_target(test-dma-spi stm32f429zi_stm32f4discovery)
add_executable(test-dsgamma src/tests/drivers/test-dsgamma.cpp)
sbs_target(test-dsgamma stm32f429zi_stm32f4discovery)
......@@ -212,16 +209,7 @@ add_executable(test-pwm src/tests/drivers/timer/test-pwm.cpp)
sbs_target(test-pwm stm32f429zi_stm32f4discovery)
add_executable(test-spi src/tests/drivers/spi/test-spi.cpp)
sbs_target(test-spi stm32f429zi_stm32f4discovery)
add_executable(test-spi-as-slave src/tests/drivers/spi/test-spi-as-slave.cpp)
sbs_target(test-spi-as-slave stm32f429zi_stm32f4discovery)
add_executable(test-spi-signal-generator src/tests/drivers/spi/test-spi-signal-generator.cpp)
sbs_target(test-spi-signal-generator stm32f429zi_stm32f4discovery)
add_executable(test-spi-slave-bus src/tests/drivers/spi/test-spi-slave-bus.cpp)
sbs_target(test-spi-slave-bus stm32f429zi_stm32f4discovery)
sbs_target(test-spi stm32f407vg_stm32f4discovery)
add_executable(test-timer-utils src/tests/drivers/timer/test-timer-utils.cpp)
sbs_target(test-timer-utils stm32f429zi_stm32f4discovery)
......@@ -316,18 +304,12 @@ sbs_target(test-sx1278lora-tx stm32f429zi_skyward_groundstation_v2)
# Tests - Sensors #
#-----------------------------------------------------------------------------#
add_executable(test-ads1118 src/tests/sensors/ADS1118/test-ads1118.cpp)
add_executable(test-ads1118 src/tests/sensors/test-ads1118.cpp)
sbs_target(test-ads1118 stm32f407vg_stm32f4discovery)
add_executable(test-ads131m04 src/tests/sensors/ADS131M04/test-ads131m04.cpp)
add_executable(test-ads131m04 src/tests/sensors/test-ads131m04.cpp)
sbs_target(test-ads131m04 stm32f429zi_stm32f4discovery)
add_executable(test-ads131m04highfreq src/tests/sensors/ADS131M04/test-ads131m04highfreq.cpp)
sbs_target(test-ads131m04highfreq stm32f429zi_stm32f4discovery)
add_executable(test-ads131m04highfreq-with-logger src/tests/sensors/ADS131M04/test-ads131m04highfreq-with-logger.cpp)
sbs_target(test-ads131m04highfreq-with-logger stm32f429zi_stm32f4discovery)
add_executable(test-analog-pressure-sensors src/tests/sensors/analog/test-analog-pressure-sensors.cpp)
sbs_target(test-analog-pressure-sensors stm32f429zi_stm32f4discovery)
......
......@@ -81,7 +81,6 @@ foreach(OPT_BOARD ${BOARDS})
# Sensors
${SBS_BASE}/src/shared/sensors/ADS1118/ADS1118.cpp
${SBS_BASE}/src/shared/sensors/ADS131M04/ADS131M04.cpp
${SBS_BASE}/src/shared/sensors/ADS131M04/ADS131M04HighFreq.cpp
${SBS_BASE}/src/shared/sensors/BME280/BME280.cpp
${SBS_BASE}/src/shared/sensors/BMP280/BMP280.cpp
${SBS_BASE}/src/shared/sensors/BMX160/BMX160.cpp
......
......@@ -163,7 +163,9 @@ int main()
SPIBusConfig spi_config;
spi_config.clockDivider = SPI::ClockDivider::DIV_64;
spi_config.mode = SPI::Mode::MODE_0;
spi_config.bitOrder = SPI::BitOrder::MSB_FIRST;
spi_config.bitOrder = SPI::Order::MSB_FIRST;
spi_config.byteOrder = SPI::Order::MSB_FIRST;
spi_config.writeBit = SPI::WriteBit::INVERTED;
SPIBus bus(SX1278_SPI);
GpioPin cs = cs::getPin();
......
/* Copyright (c) 2021 Skyward Experimental Rocketry
* Author: Alberto Nidasio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <interfaces/arch_registers.h>
#include <stddef.h>
#include <utils/ClockUtils.h>
#ifndef USE_MOCK_PERIPHERALS
using SPIType = SPI_TypeDef;
#else
#include <utils/TestUtils/FakeSpiTypedef.h>
using SPIType = Boardcore::FakeSpiTypedef;
#endif
namespace Boardcore
{
/**
* @brief Driver for STM32 low level SPI peripheral.
*
* This driver applies to the whole STM32F4xx family.
*
* The serial peripheral interface (SPI) allows half/full-duplex, synchronous,
* serial communication with external devices. The interface can be configured
* as the master and in this case it provides the communication clock (SCK) to
* the external slave device. The peripheral is also capable of reliable
* communication using CRC checking.
*
* SPI main features:
* - Full-duplex synchronous transfers on three lines
* - 8- or 16-bit transfer frame format selection
* - Master or slave operation
* - 8 master mode baud rate prescaler (f_PCLK/2 max.)
* - Programmable clock polarity and phase
* - Programmable data order with MSB-first or LSB-first shifting
* - Hardware CRC feature for reliable communication
* - DMA capability
*/
class SPI
{
public:
enum class BitOrder : uint16_t
{
MSB_FIRST = 0,
LSB_FIRST = 0x80
};
/**
* @brief SPI Clock divider.
*
* SPI clock frequency will be equal to the SPI peripheral bus clock speed
* divided by the specified value.
*
* Eg: DIV_2 --> spi clock freq = f_PCLK / 2
*/
enum class ClockDivider : uint8_t
{
DIV_2 = 0x00,
DIV_4 = 0x08,
DIV_8 = 0x10,
DIV_16 = 0x18,
DIV_32 = 0x20,
DIV_64 = 0x28,
DIV_128 = 0x30,
DIV_256 = 0x38,
};
enum class Mode : uint8_t
{
MODE_0 = 0, ///< CPOL = 0, CPHA = 0
MODE_1 = 1, ///< CPOL = 0, CPHA = 1
MODE_2 = 2, ///< CPOL = 1, CPHA = 0
MODE_3 = 3 ///< CPOL = 1, CPHA = 1
};
/**
* @brief Automatically enables the timer peripheral clock.
*/
explicit SPI(SPIType *spi);
/**
* @brief Resets the registers and disables the peripheral clock.
*/
~SPI();
SPIType *getSpi();
/**
* @brief Resets the peripheral configuration.
*/
void reset();
/**
* @brief Enables the peripheral.
*/
void enable();
/**
* @brief Disables the peripheral.
*/
void disable();
void set8BitFrameFormat();
void set16BitFrameFormat();
void enableSoftwareSlaveManagement();
void disableSoftwareSlaveManagement();
void enableInternalSlaveSelection();
void disableInternalSlaveSelection();
void setBitOrder(BitOrder bitOrder);
void setClockDiver(ClockDivider divider);
void setSlaveConfiguration();
void setMasterConfiguration();
void setMode(Mode mode);
void enableTxDMARequest();
void disableTxDMARequest();
void enableRxDMARequest();
void disableRxDMARequest();
void waitPeripheral();
// Read, write and transfer operations in master mode
/**
* @brief Reads a single byte from the bus.
*
* @return Byte read from the bus.
*/
uint8_t read();
/**
* @brief Reads a single half word from the bus.
*
* @return Half word read from the bus.
*/
uint16_t read16();
/**
* @brief Reads multiple bytes from the bus
*
* @param data Buffer to be filled with received data.
* @param size Size of the buffer in bytes.
*/
void read(uint8_t *data, size_t nBytes);
/**
* @brief Reads multiple half words from the bus
*
* @param data Buffer to be filled with received data.
* @param size Size of the buffer in bytes.
*/
void read(uint16_t *data, size_t nBytes);
/**
* @brief Writes a single byte to the bus.
*
* @param data Byte to write.
*/
void write(uint8_t data);
/**
* @brief Writes a single half word to the bus.
*
* @param data Half word to write.
*/
void write(uint16_t data);
/**
* @brief Writes multiple bytes to the bus.
*
* @param data Buffer containing data to write.
* @param size Size of the buffer in bytes.
*/
void write(const uint8_t *data, size_t nBytes);
/**
* @brief Writes multiple half words to the bus.
*
* @param data Buffer containing data to write.
* @param size Size of the buffer in bytes.
*/
void write(const uint16_t *data, size_t nBytes);
/**
* @brief Full duplex transmission of one byte on the bus.
*
* @param data Byte to write.
* @return Byte read from the bus.
*/
uint8_t transfer(uint8_t data);
/**
* @brief Full duplex transmission of one half word on the bus.
*
* @param data Half word to write.
* @return Half word read from the bus.
*/
uint16_t transfer(uint16_t data);
/**
* @brief Full duplex transmission of multiple bytes on the bus.
*
* @param data Buffer containing data to trasfer.
* @param size Size of the buffer in bytes.
*/
void transfer(uint8_t *data, size_t nBytes);
/**
* @brief Full duplex transmission of multiple half words on the bus.
*
* @param data Buffer containing data to trasfer.
* @param size Size of the buffer in bytes.
*/
void transfer(uint16_t *data, size_t nBytes);
private:
SPIType *spi;
};
inline SPI::SPI(SPIType *spi) : spi(spi)
{
ClockUtils::enablePeripheralClock(spi);
}
inline SPI::~SPI() { ClockUtils::disablePeripheralClock(spi); }
inline SPIType *SPI::getSpi() { return spi; }
inline void SPI::reset()
{
spi->CR1 = 0;
spi->CR2 = 0;
spi->DR = 0;
spi->RXCRCR = 0;
spi->TXCRCR = 0;
}
inline void SPI::enable() { spi->CR1 |= SPI_CR1_SPE; }
inline void SPI::disable() { spi->CR1 &= ~SPI_CR1_SPE; }
inline void SPI::set8BitFrameFormat() { spi->CR1 &= ~SPI_CR1_DFF; }
inline void SPI::set16BitFrameFormat() { spi->CR1 |= SPI_CR1_DFF; }
inline void SPI::enableSoftwareSlaveManagement() { spi->CR1 |= SPI_CR1_SSM; }
inline void SPI::disableSoftwareSlaveManagement() { spi->CR1 &= ~SPI_CR1_SSM; }
inline void SPI::enableInternalSlaveSelection() { spi->CR1 |= SPI_CR1_SSI; }
inline void SPI::disableInternalSlaveSelection() { spi->CR1 &= ~SPI_CR1_SSI; }
inline void SPI::setBitOrder(BitOrder bitOrder)
{
// First clear the configuration
spi->CR1 &= ~SPI_CR1_LSBFIRST;
// Set the new value
spi->CR1 |= static_cast<uint32_t>(bitOrder);
}
inline void SPI::setClockDiver(ClockDivider divider)
{
// First clear the configuration
spi->CR1 &= ~SPI_CR1_BR;
// Set the new value
spi->CR1 |= static_cast<uint32_t>(divider);
}
inline void SPI::setSlaveConfiguration() { spi->CR1 &= ~SPI_CR1_MSTR; }
inline void SPI::setMasterConfiguration() { spi->CR1 |= SPI_CR1_MSTR; }
inline void SPI::setMode(Mode mode)
{
// First clear the configuration
spi->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);
// Set the new value
spi->CR1 |= static_cast<uint32_t>(mode);
}
inline void SPI::enableTxDMARequest() { spi->CR2 |= SPI_CR2_TXDMAEN; }
inline void SPI::disableTxDMARequest() { spi->CR2 &= ~SPI_CR2_TXDMAEN; }
inline void SPI::enableRxDMARequest() { spi->CR2 |= SPI_CR2_RXDMAEN; }
inline void SPI::disableRxDMARequest() { spi->CR2 &= ~SPI_CR2_RXDMAEN; }
inline void SPI::waitPeripheral()
{
while ((spi->SR & SPI_SR_TXE) == 0)
;
while ((spi->SR & SPI_SR_BSY) > 0)
;
}
// Read, write and transfer operations in master mode
inline uint8_t SPI::read() { return transfer(static_cast<uint8_t>(0)); }
inline uint16_t SPI::read16() { return transfer(static_cast<uint16_t>(0)); }
inline void SPI::read(uint8_t *data, size_t nBytes)
{
// Reset the data
for (size_t i = 0; i < nBytes; i++)
data[i] = 0;
// Read the data
transfer(data, nBytes);
}
inline void SPI::read(uint16_t *data, size_t nBytes)
{
// Reset the data
for (size_t i = 0; i < nBytes / 2; i++)
data[i] = 0;
// Read the data
transfer(data, nBytes);
}
inline void SPI::write(uint8_t data)
{
// Write the data item in the transmit buffer
spi->DR = data;
// Wait until Tx buffer is empty and until the peripheral is still busy
while ((spi->SR & SPI_SR_TXE) == 0)
;
while (spi->SR & SPI_SR_BSY)
;
// Ensures the Rx buffer is empty
(void)spi->DR;
}
inline void SPI::write(uint16_t data)
{
// Set 16 bit frame format
set16BitFrameFormat();
// Write the data item in the Tx buffer
spi->DR = data;
// Wait until Tx buffer is empty and until the peripheral is still busy
while ((spi->SR & SPI_SR_TXE) == 0)
;
while (spi->SR & SPI_SR_BSY)
;
// Go back to 8 bit frame format
set8BitFrameFormat();
// Ensures the Rx buffer is empty
(void)spi->DR;
}
inline void SPI::write(const uint8_t *data, size_t nBytes)
{
// Write the first data item in the Tx buffer
spi->DR = data[0];
// Wait for TXE=1 and write the next data item
for (size_t i = 1; i < nBytes; i++)
{
// Wait until Tx buffer is empty
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the next data item
spi->DR = data[i];
}
// Wait until Tx buffer is empty and until the peripheral is still busy
while ((spi->SR & SPI_SR_TXE) == 0)
;
while (spi->SR & SPI_SR_BSY)
;
// Ensures the Rx buffer is empty
(void)spi->DR;
}
inline void SPI::write(const uint16_t *data, size_t nBytes)
{
// Set 16 bit frame format
set16BitFrameFormat();
// Write the first data item in the Tx buffer
spi->DR = data[0];
// Wait for TXE=1 and write the next data item
for (size_t i = 1; i < nBytes / 2; i++)
{
// Wait until Tx buffer is empty
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the next data item
spi->DR = data[i];
}
// Wait until Tx buffer is empty and until the peripheral is still busy
while ((spi->SR & SPI_SR_TXE) == 0)
;
while (spi->SR & SPI_SR_BSY)
;
// Go back to 8 bit frame format
set8BitFrameFormat();
// Ensures the Rx buffer is empty
(void)spi->DR;
}
inline uint8_t SPI::transfer(uint8_t data)
{
// Write the data item to transmit
spi->DR = static_cast<uint8_t>(data);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
return static_cast<uint8_t>(spi->DR);
}
inline uint16_t SPI::transfer(uint16_t data)
{
// Set 16 bit frame format
set16BitFrameFormat();
// Write the data item to transmit
spi->DR = static_cast<uint16_t>(data);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Go back to 8 bit frame format
set8BitFrameFormat();
// Read the received data item
return static_cast<uint16_t>(spi->DR);
}
inline void SPI::transfer(uint8_t *data, size_t nBytes)
{
miosix::FastInterruptDisableLock lock;
// Clear the RX buffer
(void)spi->DR;
// Write the first data item to transmitted
spi->DR = data[0];
for (size_t i = 1; i < nBytes; i++)
{
// Wait until the previous data item has been transmitted
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the next data item
spi->DR = static_cast<uint8_t>(data[i]);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
data[i - 1] = static_cast<uint8_t>(spi->DR);
}
// Wait until the last data item is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the last received data item
data[nBytes - 1] = static_cast<uint8_t>(spi->DR);
// Wait until TXE=1 and then wait until BSY=0 before concluding
while ((spi->SR & SPI_SR_TXE) == 0)
;
while (spi->SR & SPI_SR_BSY)
;
}
inline void SPI::transfer(uint16_t *data, size_t nBytes)
{
miosix::FastInterruptDisableLock lock;
// Clear the RX buffer
(void)spi->DR;
// Set 16 bit frame format
set16BitFrameFormat();
// Write the first data item to transmitted
spi->DR = static_cast<uint16_t>(data[0]);
for (size_t i = 1; i < nBytes / 2; i++)
{
// Wait until the previous data item has been transmitted
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the next data item
spi->DR = static_cast<uint16_t>(data[i]);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
data[i - 1] = static_cast<uint16_t>(spi->DR);
}
// Wait until the last data item is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the last received data item
data[nBytes / 2 - 1] = static_cast<uint16_t>(spi->DR);
// Wait until TXE=1 and then wait until BSY=0 before concluding
while ((spi->SR & SPI_SR_TXE) == 0)
;
while (spi->SR & SPI_SR_BSY)
;
// Go back to 8 bit frame format
set8BitFrameFormat();
}
} // namespace Boardcore
......@@ -23,6 +23,7 @@
#pragma once
#include <interfaces/delays.h>
#include <utils/ClockUtils.h>
#include "SPIBusInterface.h"
......@@ -37,8 +38,23 @@ namespace Boardcore
{
/**
* @brief Main implementation of SPIBusInterface used for accessing the SPI
* peripheral in master mode
* @brief Driver for STM32 low level SPI peripheral.
*
* This driver applies to the whole STM32F4xx family.
*
* The serial peripheral interface (SPI) allows half/full-duplex, synchronous,
* serial communication with external devices. The interface can be configured
* as the master and in this case it provides the communication clock (SCK) to
* the external slave device. The peripheral is also capable of reliable
* communication using CRC checking.
*
* Supported SPI main features:
* - Full-duplex synchronous transfers on three lines
* - 8 or 16-bit transfer frame formats
* - Master operation
* - 8 master mode baud rate prescaler values (f_PCLK/2 max.)
* - Programmable clock polarity and phase
* - Programmable data order (MSBit-first or LSBit-first)
*/
class SPIBus : public SPIBusInterface
{
......@@ -51,11 +67,63 @@ public:
SPIBus(SPIBus&&) = delete;
SPIBus& operator=(SPIBus&&) = delete;
/**
* @brief Retrieve the pointer to the peripheral currently used.
*/
SPIType* getSpi();
/**
* @brief Resets the peripheral configuration.
*/
void reset();
/**
* @brief Enables the peripheral.
*/
void enable();
/**
* @brief Disables the peripheral.
*/
void disable();
void set8BitFrameFormat();
void set16BitFrameFormat();
void enableSoftwareSlaveManagement();
void disableSoftwareSlaveManagement();
void enableInternalSlaveSelection();
void disableInternalSlaveSelection();
void setBitOrder(SPI::Order bitOrder);
void setClockDiver(SPI::ClockDivider divider);
void setSlaveConfiguration();
void setMasterConfiguration();
void setMode(SPI::Mode mode);
void enableTxDMARequest();
void disableTxDMARequest();
void enableRxDMARequest();
void disableRxDMARequest();
void waitPeripheral();
/**
* @brief Configures and enables the bus with the provided configuration.
*
* Since this implementation is not syncronized, if configure() is called on
* an already in use bus nothing will be done.
* Since this implementation is not synchronized, if configure() is called
* on an already in use bus nothing will be done.
*
* Use SyncedSPIBus if you need to synchronize access to the bus.
*/
......@@ -64,29 +132,43 @@ public:
/**
* @brief See SPIBusInterface::select().
*/
void select(GpioType& cs) override;
void select(GpioType cs) override;
/**
* @brief See SPIBusInterface::deselect().
*/
void deselect(GpioType& cs) override;
void deselect(GpioType cs) override;
// Read, write and transfer operations
/**
* @brief Reads a single byte from the bus.
* @brief Reads 8 bits from the bus.
*
* @return Byte read from the bus.
*/
uint8_t read() override;
/**
* @brief Reads a single half word from the bus.
* @brief Reads 16 bits from the bus.
*
* @return Half word read from the bus.
*/
uint16_t read16() override;
/**
* @brief Reads 24 bits from the bus.
*
* @return Bytes read from the bus (MSB of the uint32_t value will be 0).
*/
uint32_t read24() override;
/**
* @brief Reads 32 bits from the bus.
*
* @return Word read from the bus.
*/
uint32_t read32() override;
/**
* @brief Reads multiple bytes from the bus
*
......@@ -101,21 +183,35 @@ public:
* @param data Buffer to be filled with received data.
* @param size Size of the buffer.
*/
void read(uint16_t* data, size_t size) override;
void read16(uint16_t* data, size_t size) override;
/**
* @brief Writes a single byte to the bus.
* @brief Writes 8 bits to the bus.
*
* @param data Byte to write.
*/
void write(uint8_t data) override;
/**
* @brief Writes a single half word to the bus.
* @brief Writes 16 bits to the bus.
*
* @param data Half word to write.
*/
void write(uint16_t data) override;
void write16(uint16_t data) override;
/**
* @brief Writes 24 bits to the bus.
*
* @param data Bytes to write (the MSB of the uint32_t is not used).
*/
void write24(uint32_t data) override;
/**
* @brief Writes 32 bits to the bus.
*
* @param data Word to write.
*/
void write32(uint32_t data) override;
/**
* @brief Writes multiple bytes to the bus.
......@@ -123,7 +219,7 @@ public:
* @param data Buffer containing data to write.
* @param size Size of the buffer.
*/
void write(uint8_t* data, size_t size) override;
void write(const uint8_t* data, size_t size) override;
/**
* @brief Writes multiple half words to the bus.
......@@ -131,10 +227,10 @@ public:
* @param data Buffer containing data to write.
* @param size Size of the buffer.
*/
void write(uint16_t* data, size_t size) override;
void write16(const uint16_t* data, size_t size) override;
/**
* @brief Full duplex transmission of one byte on the bus.
* @brief Full duplex transmission of 8 bits on the bus.
*
* @param data Byte to write.
* @return Byte read from the bus.
......@@ -142,12 +238,28 @@ public:
uint8_t transfer(uint8_t data) override;
/**
* @brief Full duplex transmission of one half word on the bus.
* @brief Full duplex transmission of 16 bits on the bus.
*
* @param data Half word to write.
* @return Half word read from the bus.
*/
uint16_t transfer(uint16_t data) override;
uint16_t transfer16(uint16_t data) override;
/**
* @brief Full duplex transmission of 24 bits on the bus.
*
* @param data Bytes to write (the MSB of the uint32_t is not used).
* @return Bytes read from the bus (the MSB of the uint32_t will be 0).
*/
uint32_t transfer24(uint32_t data) override;
/**
* @brief Full duplex transmission of 32 bits on the bus.
*
* @param data Word to write.
* @return Half word read from the bus.
*/
uint32_t transfer32(uint32_t data) override;
/**
* @brief Full duplex transmission of multiple bytes on the bus.
......@@ -163,15 +275,98 @@ public:
* @param data Buffer containing data to trasfer.
* @param size Size of the buffer.
*/
void transfer(uint16_t* data, size_t size) override;
void transfer16(uint16_t* data, size_t size) override;
private:
SPI spi;
SPIType* spi;
SPIBusConfig config{};
bool firstConfigApplied = false;
};
inline SPIBus::SPIBus(SPIType* spi) : spi(spi) {}
inline SPIBus::SPIBus(SPIType* spi) : spi(spi)
{
ClockUtils::enablePeripheralClock(spi);
}
inline SPIType* SPIBus::getSpi() { return spi; }
inline void SPIBus::reset()
{
spi->CR1 = 0;
spi->CR2 = 0;
spi->DR = 0;
spi->RXCRCR = 0;
spi->TXCRCR = 0;
}
inline void SPIBus::enable() { spi->CR1 |= SPI_CR1_SPE; }
inline void SPIBus::disable() { spi->CR1 &= ~SPI_CR1_SPE; }
inline void SPIBus::set8BitFrameFormat() { spi->CR1 &= ~SPI_CR1_DFF; }
inline void SPIBus::set16BitFrameFormat() { spi->CR1 |= SPI_CR1_DFF; }
inline void SPIBus::enableSoftwareSlaveManagement() { spi->CR1 |= SPI_CR1_SSM; }
inline void SPIBus::disableSoftwareSlaveManagement()
{
spi->CR1 &= ~SPI_CR1_SSM;
}
inline void SPIBus::enableInternalSlaveSelection() { spi->CR1 |= SPI_CR1_SSI; }
inline void SPIBus::disableInternalSlaveSelection()
{
spi->CR1 &= ~SPI_CR1_SSI;
}
inline void SPIBus::setBitOrder(SPI::Order bitOrder)
{
// First clear the configuration
spi->CR1 &= ~SPI_CR1_LSBFIRST;
// Set the new value
spi->CR1 |= static_cast<uint32_t>(bitOrder);
}
inline void SPIBus::setClockDiver(SPI::ClockDivider divider)
{
// First clear the configuration
spi->CR1 &= ~SPI_CR1_BR;
// Set the new value
spi->CR1 |= static_cast<uint32_t>(divider);
}
inline void SPIBus::setSlaveConfiguration() { spi->CR1 &= ~SPI_CR1_MSTR; }
inline void SPIBus::setMasterConfiguration() { spi->CR1 |= SPI_CR1_MSTR; }
inline void SPIBus::setMode(SPI::Mode mode)
{
// First clear the configuration
spi->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);
// Set the new value
spi->CR1 |= static_cast<uint32_t>(mode);
}
inline void SPIBus::enableTxDMARequest() { spi->CR2 |= SPI_CR2_TXDMAEN; }
inline void SPIBus::disableTxDMARequest() { spi->CR2 &= ~SPI_CR2_TXDMAEN; }
inline void SPIBus::enableRxDMARequest() { spi->CR2 |= SPI_CR2_RXDMAEN; }
inline void SPIBus::disableRxDMARequest() { spi->CR2 &= ~SPI_CR2_RXDMAEN; }
inline void SPIBus::waitPeripheral()
{
while ((spi->SR & SPI_SR_TXE) == 0)
;
while ((spi->SR & SPI_SR_BSY) > 0)
;
}
inline void SPIBus::configure(SPIBusConfig newConfig)
{
......@@ -183,81 +378,216 @@ inline void SPIBus::configure(SPIBusConfig newConfig)
firstConfigApplied = true;
// Wait until the peripheral is done before changing configuration
spi.waitPeripheral();
waitPeripheral();
// Disable the peripheral
spi.disable();
disable();
// Configure clock polarity and phase
spi.setMode(config.mode);
setMode(config.mode);
// Configure clock frequency
spi.setClockDiver(config.clockDivider);
setClockDiver(config.clockDivider);
// Configure bit order
spi.setBitOrder(config.bitOrder);
setBitOrder(config.bitOrder);
// Configure chip select and master mode
spi.enableSoftwareSlaveManagement();
spi.enableInternalSlaveSelection();
spi.setMasterConfiguration();
enableSoftwareSlaveManagement();
enableInternalSlaveSelection();
setMasterConfiguration();
// Enable the peripheral
spi.enable();
enable();
}
}
inline void SPIBus::select(GpioType& cs)
inline void SPIBus::select(GpioType cs)
{
cs.low();
if (config.csSetupTimeUs > 0)
{
miosix::delayUs(config.csSetupTimeUs);
}
}
inline void SPIBus::deselect(GpioType& cs)
inline void SPIBus::deselect(GpioType cs)
{
if (config.csHoldTimeUs > 0)
{
miosix::delayUs(config.csHoldTimeUs);
}
cs.high();
}
// Read, write and transfer operations
inline uint8_t SPIBus::read() { return transfer(0); }
inline uint16_t SPIBus::read16() { return transfer16(0); }
inline uint32_t SPIBus::read24() { return transfer24(0); }
inline uint32_t SPIBus::read32() { return transfer32(0); }
inline void SPIBus::read(uint8_t* data, size_t nBytes)
{
for (size_t i = 0; i < nBytes; i++)
data[i] = read();
}
inline void SPIBus::read16(uint16_t* data, size_t nBytes)
{
// Set 16 bit frame format
set16BitFrameFormat();
for (size_t i = 0; i < nBytes / 2; i++)
{
// Wait until the peripheral is ready to transmit
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the data item to transmit
spi->DR = 0;
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
data[i] = static_cast<uint16_t>(spi->DR);
}
inline uint8_t SPIBus::read() { return spi.read(); }
// Go back to 8 bit frame format
set8BitFrameFormat();
}
inline uint16_t SPIBus::read16() { return spi.read16(); }
inline void SPIBus::write(uint8_t data) { transfer(data); }
inline void SPIBus::read(uint8_t* data, size_t size) { spi.read(data, size); }
inline void SPIBus::write16(uint16_t data) { transfer16(data); }
inline void SPIBus::read(uint16_t* data, size_t size) { spi.read(data, size); }
inline void SPIBus::write24(uint32_t data) { transfer24(data); }
inline void SPIBus::write(uint8_t data) { spi.write(data); }
inline void SPIBus::write32(uint32_t data) { transfer32(data); }
inline void SPIBus::write(uint16_t data) { spi.write(data); }
inline void SPIBus::write(const uint8_t* data, size_t nBytes)
{
for (size_t i = 0; i < nBytes; i++)
transfer(data[i]);
}
inline void SPIBus::write(uint8_t* data, size_t size) { spi.write(data, size); }
inline void SPIBus::write16(const uint16_t* data, size_t nBytes)
{
// Set 16 bit frame format
set16BitFrameFormat();
inline void SPIBus::write(uint16_t* data, size_t size)
for (size_t i = 0; i < nBytes / 2; i++)
{
spi.write(data, size);
// Wait until the peripheral is ready to transmit
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the data item to transmit
spi->DR = static_cast<uint16_t>(data[i]);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
(void)spi->DR;
}
inline uint8_t SPIBus::transfer(uint8_t data) { return spi.transfer(data); }
// Go back to 8 bit frame format
set8BitFrameFormat();
}
inline uint16_t SPIBus::transfer(uint16_t data) { return spi.transfer(data); }
inline uint8_t SPIBus::transfer(uint8_t data)
{
// Wait until the peripheral is ready to transmit
while ((spi->SR & SPI_SR_TXE) == 0)
;
inline void SPIBus::transfer(uint8_t* data, size_t size)
// Write the data item to transmit
spi->DR = static_cast<uint8_t>(data);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
return static_cast<uint8_t>(spi->DR);
}
inline uint16_t SPIBus::transfer16(uint16_t data)
{
spi.transfer(data, size);
// Set 16 bit frame format
set16BitFrameFormat();
// Wait until the peripheral is ready to transmit
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the data item to transmit
spi->DR = static_cast<uint16_t>(data);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Go back to 8 bit frame format
set8BitFrameFormat();
// Read the received data item
return static_cast<uint16_t>(spi->DR);
}
inline void SPIBus::transfer(uint16_t* data, size_t size)
inline uint32_t SPIBus::transfer24(uint32_t data)
{
spi.transfer(data, size);
uint32_t res = static_cast<uint32_t>(transfer16(data >> 8)) << 8;
res |= transfer(data);
return res;
}
inline uint32_t SPIBus::transfer32(uint32_t data)
{
uint32_t res = static_cast<uint32_t>(transfer16(data >> 16)) << 16;
res |= transfer16(data);
return res;
}
inline void SPIBus::transfer(uint8_t* data, size_t nBytes)
{
for (size_t i = 0; i < nBytes; i++)
data[i] = transfer(data[i]);
}
inline void SPIBus::transfer16(uint16_t* data, size_t nBytes)
{
// Set 16 bit frame format
set16BitFrameFormat();
for (size_t i = 0; i < nBytes / 2; i++)
{
// Wait until the peripheral is ready to transmit
while ((spi->SR & SPI_SR_TXE) == 0)
;
// Write the data item to transmit
spi->DR = static_cast<uint16_t>(data[i]);
// Wait until data is received
while ((spi->SR & SPI_SR_RXNE) == 0)
;
// Read the received data item
data[i] = static_cast<uint16_t>(spi->DR);
}
// Go back to 8 bit frame format
set8BitFrameFormat();
}
} // namespace Boardcore
......@@ -23,8 +23,9 @@
#pragma once
#include <interfaces-impl/gpio_impl.h>
#include <stddef.h>
#include "SPI.h"
#include "SPIDefs.h"
#ifndef USE_MOCK_PERIPHERALS
using GpioType = miosix::GpioPin;
......@@ -49,8 +50,34 @@ struct SPIBusConfig
///< Clock polarity and phase configuration
SPI::Mode mode;
///< MSB or LSB first
SPI::BitOrder bitOrder;
///< MSBit or LSBit first
SPI::Order bitOrder;
/**
* @brief MSByte or LSByte first
*
* This parameter is used when reading and writing registers 16 bit wide or
* more.
*
* A device features MSByte first ordering if the most significant byte is
* at the lowest address. Example of a 24bit register:
* Address: 0x06 0x07 0x08
* value: MSB MID LSB
*
* Conversely, an LSByte first ordering starts with the lowest significant
* byte first.
*
* Also, in every device used since now, in multiple registers accesses, the
* device always increments the address. So the user has always to provide
* the lowest address.
*
* @warning This driver does not support devices which decrements registers
* address during multiple registers accesses.
*/
SPI::Order byteOrder;
///< Write bit behaviour, default high when reading
SPI::WriteBit writeBit;
///< How long to wait before starting a tranmission after CS is set (us)
unsigned int csSetupTimeUs;
......@@ -60,9 +87,12 @@ struct SPIBusConfig
SPIBusConfig(SPI::ClockDivider clockDivider = SPI::ClockDivider::DIV_256,
SPI::Mode mode = SPI::Mode::MODE_0,
SPI::BitOrder bitOrder = SPI::BitOrder::MSB_FIRST,
SPI::Order bitOrder = SPI::Order::MSB_FIRST,
SPI::Order byteOrder = SPI::Order::MSB_FIRST,
SPI::WriteBit writeBit = SPI::WriteBit::NORMAL,
unsigned int csSetupTimeUs = 0, unsigned int csHoldTimeUs = 0)
: clockDivider(clockDivider), mode(mode), bitOrder(bitOrder),
byteOrder(byteOrder), writeBit(writeBit),
csSetupTimeUs(csSetupTimeUs), csHoldTimeUs(csHoldTimeUs)
{
}
......@@ -109,31 +139,45 @@ public:
*
* @param cs Chip select pin for the slave.
*/
virtual void select(GpioType& cs) = 0;
virtual void select(GpioType cs) = 0;
/**
* @brief Deselects the slave.
*
* @param cs Chip select pin for the slave.
*/
virtual void deselect(GpioType& cs) = 0;
virtual void deselect(GpioType cs) = 0;
// Read, write and transfer operations
/**
* @brief Reads a single byte from the bus.
* @brief Reads 8 bits from the bus.
*
* @return Byte read from the bus.
*/
virtual uint8_t read() = 0;
/**
* @brief Reads a single half word from the bus.
* @brief Reads 16 bits from the bus.
*
* @return Half word read from the bus.
*/
virtual uint16_t read16() = 0;
/**
* @brief Reads 24 bits from the bus.
*
* @return Bytes read from the bus (MSB of the uint32_t value will be 0).
*/
virtual uint32_t read24() = 0;
/**
* @brief Reads 32 bits from the bus.
*
* @return Word read from the bus.
*/
virtual uint32_t read32() = 0;
/**
* @brief Reads multiple bytes from the bus
*
......@@ -148,21 +192,35 @@ public:
* @param data Buffer to be filled with received data.
* @param size Size of the buffer.
*/
virtual void read(uint16_t* data, size_t size) = 0;
virtual void read16(uint16_t* data, size_t size) = 0;
/**
* @brief Writes a single byte to the bus.
* @brief Writes 8 bits to the bus.
*
* @param data Byte to write.
*/
virtual void write(uint8_t data) = 0;
/**
* @brief Writes a single half word to the bus.
* @brief Writes 16 bits to the bus.
*
* @param data Half word to write.
*/
virtual void write(uint16_t data) = 0;
virtual void write16(uint16_t data) = 0;
/**
* @brief Writes 24 bits to the bus.
*
* @param data Bytes to write (the MSB of the uint32_t is not used).
*/
virtual void write24(uint32_t data) = 0;
/**
* @brief Writes 32 bits to the bus.
*
* @param data Word to write.
*/
virtual void write32(uint32_t data) = 0;
/**
* @brief Writes multiple bytes to the bus.
......@@ -170,7 +228,7 @@ public:
* @param data Buffer containing data to write.
* @param size Size of the buffer.
*/
virtual void write(uint8_t* data, size_t size) = 0;
virtual void write(const uint8_t* data, size_t size) = 0;
/**
* @brief Writes multiple half words to the bus.
......@@ -178,10 +236,10 @@ public:
* @param data Buffer containing data to write.
* @param size Size of the buffer.
*/
virtual void write(uint16_t* data, size_t size) = 0;
virtual void write16(const uint16_t* data, size_t size) = 0;
/**
* @brief Full duplex transmission of one byte on the bus.
* @brief Full duplex transmission of 8 bits on the bus.
*
* @param data Byte to write.
* @return Byte read from the bus.
......@@ -189,17 +247,33 @@ public:
virtual uint8_t transfer(uint8_t data) = 0;
/**
* @brief Full duplex transmission of one half word on the bus.
* @brief Full duplex transmission of 16 bits on the bus.
*
* @param data Half word to write.
* @return Half word read from the bus.
*/
virtual uint16_t transfer(uint16_t data) = 0;
virtual uint16_t transfer16(uint16_t data) = 0;
/**
* @brief Full duplex transmission of 24 bits on the bus.
*
* @param data Bytes to write (the MSB of the uint32_t is not used).
* @return Bytes read from the bus (the MSB of the uint32_t will be 0).
*/
virtual uint32_t transfer24(uint32_t data) = 0;
/**
* @brief Full duplex transmission of 32 bits on the bus.
*
* @param data Word to write.
* @return Half word read from the bus.
*/
virtual uint32_t transfer32(uint32_t data) = 0;
/**
* @brief Full duplex transmission of multiple bytes on the bus.
*
* @param data Buffer containing data to trasfer.
* @param data Buffer containing data to transfer.
* @param size Size of the buffer.
*/
virtual void transfer(uint8_t* data, size_t size) = 0;
......@@ -207,10 +281,10 @@ public:
/**
* @brief Full duplex transmission of multiple half words on the bus.
*
* @param data Buffer containing data to trasfer.
* @param data Buffer containing data to transfer.
* @param size Size of the buffer.
*/
virtual void transfer(uint16_t* data, size_t size) = 0;
virtual void transfer16(uint16_t* data, size_t size) = 0;
};
/**
......@@ -223,7 +297,7 @@ struct SPISlave
///< with the slave.
GpioType cs; ///< Chip select pin
SPISlave(SPIBusInterface& bus, GpioType cs, SPIBusConfig config)
SPISlave(SPIBusInterface& bus, GpioType cs, SPIBusConfig config = {})
: bus(bus), config(config), cs(cs)
{
}
......
......@@ -20,61 +20,83 @@
* THE SOFTWARE.
*/
#include <drivers/spi/SPISignalGenerator.h>
#include <miosix.h>
#pragma once
using namespace miosix;
using namespace Boardcore;
using namespace TimerUtils;
#include <interfaces/arch_registers.h>
#include <stdint.h>
namespace Boardcore
{
/**
* Pin list:
* PB4 - TIM3_CH1 - CS signal
* PE5 - TIM9_CH1 - SCK signal
* @brief Driver for STM32 low level SPI peripheral.
*
* This driver applies to the whole STM32F4xx family.
*
* We'll use two timers to generate clock and chip select signals for the SPI
* peripheral.
* The serial peripheral interface (SPI) allows half/full-duplex, synchronous,
* serial communication with external devices. The interface can be configured
* as the master and in this case it provides the communication clock (SCK) to
* the external slave device. The peripheral is also capable of reliable
* communication using CRC checking.
*
* SPI main features:
* - Full-duplex synchronous transfers on three lines
* - 8- or 16-bit transfer frame format selection
* - Master or slave operation
* - 8 master mode baud rate prescaler (f_PCLK/2 max.)
* - Programmable clock polarity and phase
* - Programmable data order with MSB-first or LSB-first shifting
* - Hardware CRC feature for reliable communication
* - DMA capability
*/
GpioPin csPin = GpioPin(GPIOA_BASE, 11);
GpioPin sckPin = GpioPin(GPIOB_BASE, 1);
void setupGPIOs();
int main()
namespace SPI
{
setupGPIOs();
SPISignalGenerator spiSignalGenerator{2,
1000,
1000000,
SPI::Mode::MODE_0,
TimerUtils::Channel::CHANNEL_1,
TimerUtils::Channel::CHANNEL_4,
TimerUtils::Channel::CHANNEL_4};
spiSignalGenerator.configure();
Thread::sleep(1000);
spiSignalGenerator.generateSingleTransaction(8);
Thread::sleep(1000);
spiSignalGenerator.configure();
spiSignalGenerator.enable();
Thread::sleep(5 * 1000);
enum class Order : uint16_t
{
MSB_FIRST = 0,
LSB_FIRST = 0x80
};
spiSignalGenerator.disable();
/**
* @brief SPI Clock divider.
*
* SPI clock frequency will be equal to the SPI peripheral bus clock speed
* divided by the specified value.
*
* Eg: DIV_2 --> spi clock freq = f_PCLK / 2
*/
enum class ClockDivider : uint8_t
{
DIV_2 = 0x00,
DIV_4 = 0x08,
DIV_8 = 0x10,
DIV_16 = 0x18,
DIV_32 = 0x20,
DIV_64 = 0x28,
DIV_128 = 0x30,
DIV_256 = 0x38,
};
while (true)
Thread::sleep(10000);
}
enum class Mode : uint8_t
{
///< CPOL = 0, CPHA = 0 -> Clock low when idle, sample on first edge
MODE_0 = 0,
///< CPOL = 0, CPHA = 1 -> Clock low when idle, sample on second edge
MODE_1 = 1,
///< CPOL = 1, CPHA = 0 -> Clock high when idle, sample on first edge
MODE_2 = 2,
///< CPOL = 1, CPHA = 1 -> Clock high when idle, sample on second edge
MODE_3 = 3
};
void setupGPIOs()
enum class WriteBit
{
csPin.mode(Mode::ALTERNATE);
csPin.alternateFunction(1);
NORMAL, ///< Normal write bit settings (0 for write, 1 for reads)
INVERTED, ///< Inverted write bit settings (1 for write, 0 for reads)
DISABLED, ///< Do not set write bit in any way
};
} // namespace SPI
sckPin.mode(Mode::ALTERNATE);
sckPin.alternateFunction(2);
}
} // namespace Boardcore
......@@ -22,7 +22,19 @@
#pragma once
/**
* This header file is inteded to provide all the necessary files needed to use
* the SPI driver.
*
* The driver is divided into 3 levels:
* - Low: SPIBus is the actual driver that talks directly to the STM32
* pheripheral
* - Middle: SPIBusInterface is a common interface that defines which operations
* the implementation has to privde
* - High: SPITransaction is a RAII abstraction that manages slave selection
* through the chip select signal
*/
#include "SPIBus.h"
#include "SPIBusInterface.h"
#include "SPISlaveBus.h"
#include "SPITransaction.h"
/* Copyright (c) 2021 Skyward Experimental Rocketry
* Author: Alberto Nidasio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <drivers/timer/GeneralPurposeTimer.h>
#include <drivers/timer/TimerUtils.h>
#include "SPIBusInterface.h"
namespace Boardcore
{
/**
* @brief Generates SPI clock and chip select signal through two timers chained
* together.
*
* To configure which timer to use, change the following parameters:
* - chainChannel: Specify which channel of the master timer is used to chain
* the slave timer;
* - csChannel: Specify which channel of the master timer to use for CS signal
* generation;
* - sckChannel: Specify which channel of the slave timer to use for SCK signal
* generation.
*
* The chainChannel and CSChannel should be different. If they are equal the CS
* signal generated by the master timer will be reversed (high when active).
*
* GPIO must be already configured.
*
* This driver does not support the complementary timers channels.
*/
class SPISignalGenerator
{
public:
/**
* @brief Create a SPISignalGenerator object. This does not configure the
* timers.
*
* @param nBytes Number of bytes for the sck signal.
* @param transactionFrequency Frequency of the SPI transactions.
* @param spiFrequency SPI clock signal frequency.
* @param spiMode SPI mode, this affects the clock polarity and phase.
* @param chainChannel Master timer channel used to chain the slave timer.
* @param csChannel Master timer channel used for CS signal generation.
* @param sckChannel Slave timer channel used for SCK signal generation.
* @param masterTimer Master timer which generates the CS signal.
* @param slaveTimer Slave timer which generates the SCK signal.
* @param slaveTriggerSource Trigger source form the slave timer. This
* depends on the timers combination.
*/
SPISignalGenerator(
size_t nBytes, int transactionFrequency, int spiFrequency = 1e6,
SPI::Mode spiMode = SPI::Mode::MODE_0,
TimerUtils::Channel chainChannel = TimerUtils::Channel::CHANNEL_2,
TimerUtils::Channel csChannel = TimerUtils::Channel::CHANNEL_2,
TimerUtils::Channel sckChannel = TimerUtils::Channel::CHANNEL_4,
TIM_TypeDef *masterTimer = TIM1, TIM_TypeDef *slaveTimer = TIM3,
TimerUtils::TriggerSource slaveTriggerSource =
TimerUtils::TriggerSource::ITR0);
/**
* @brief Sets up the two timers.
*/
void configure();
/**
* @brief Enables SPI signal generation.
*/
void enable();
/**
* @brief Disables SPI signal generation.
*/
void disable();
/**
* @brief Generates a single spi transaction with the specified number of
* bytes.
*
* Configures the timers to generate the specified number of bytes, enables
* master timer one one-pulse mode and enables them. The master timer will
* be disabled automatically, but the slave timer will not.
*
* @param nBytes Size of the transaction in bytes.
*/
void generateSingleTransaction(size_t nBytes);
private:
int nBytes; ///< SPI Clock pulses (divided by 8).
int transactionFrequency; ///< Frequency of the transactions are generated.
int spiFrequency; ///< SPI Clock frequency.
SPI::Mode spiMode;
TimerUtils::Channel chainChannel;
TimerUtils::Channel csChannel;
TimerUtils::Channel sckChannel;
GP16bitTimer masterTimer; ///< Master timer for CS generation.
GP16bitTimer slaveTimer; ///< Slave timer for SCK generation.
TimerUtils::TriggerSource slaveTriggerSource;
};
inline SPISignalGenerator::SPISignalGenerator(
size_t nBytes, int transactionFrequency, int spiFrequency,
SPI::Mode spiMode, TimerUtils::Channel chainChannel,
TimerUtils::Channel csChannel, TimerUtils::Channel sckChannel,
TIM_TypeDef *masterTimer, TIM_TypeDef *slaveTimer,
TimerUtils::TriggerSource slaveTriggerSource)
: nBytes(nBytes), transactionFrequency(transactionFrequency),
spiFrequency(spiFrequency), spiMode(spiMode), chainChannel(chainChannel),
csChannel(csChannel), sckChannel(sckChannel), masterTimer(masterTimer),
slaveTimer(slaveTimer), slaveTriggerSource(slaveTriggerSource)
{
}
inline void SPISignalGenerator::configure()
{
// Configure master timer
{
masterTimer.reset();
// Ensures that the CS output will be high until the timers are enabled
// masterTimer.setCounter(UINT16_MAX);
// Connect the specified channel to the trigger output
switch (chainChannel)
{
case TimerUtils::Channel::CHANNEL_1:
masterTimer.setMasterMode(
TimerUtils::MasterMode::OC1REF_OUTPUT);
break;
case TimerUtils::Channel::CHANNEL_2:
masterTimer.setMasterMode(
TimerUtils::MasterMode::OC2REF_OUTPUT);
break;
case TimerUtils::Channel::CHANNEL_3:
masterTimer.setMasterMode(
TimerUtils::MasterMode::OC3REF_OUTPUT);
break;
case TimerUtils::Channel::CHANNEL_4:
masterTimer.setMasterMode(
TimerUtils::MasterMode::OC4REF_OUTPUT);
break;
}
// Set the prescaler and auto realod value
uint16_t autoReloadRegister = spiFrequency * 4 / transactionFrequency;
masterTimer.setPrescaler(TimerUtils::computePrescalerValue(
masterTimer.getTimer(), spiFrequency * 4));
masterTimer.setAutoReloadRegister(autoReloadRegister);
// Set channels capture/compare register
uint16_t ccRegister = nBytes * 8 * 4;
ccRegister = autoReloadRegister - ccRegister + 1;
// Set chain channel
masterTimer.setCaptureCompareRegister(chainChannel, ccRegister);
masterTimer.setOutputCompareMode(
chainChannel, TimerUtils::OutputCompareMode::PWM_MODE_2);
// Set CS channel if different
if (chainChannel != csChannel)
{
masterTimer.setCaptureCompareRegister(csChannel, ccRegister);
masterTimer.setOutputCompareMode(
csChannel, TimerUtils::OutputCompareMode::PWM_MODE_1);
// Enable CS capture/compare output
masterTimer.enableCaptureCompareOutput(csChannel);
}
else
{
// If chain channel and cs channel are the same, the cs output
// must be from the complementary output
masterTimer.setCaptureCompareComplementaryPolarity(
chainChannel, TimerUtils::OutputComparePolarity::ACTIVE_LOW);
masterTimer.enableCaptureCompareComplementaryOutput(chainChannel);
}
// Update the registers
masterTimer.generateUpdate();
}
// Configure slave timer
{
slaveTimer.reset();
// Set slaveTimer in gated mode
slaveTimer.setSlaveMode(TimerUtils::SlaveMode::GATED_MODE);
// Set ITR1 as internal trigger source
slaveTimer.setTriggerSource(slaveTriggerSource);
// Set the prescaler and auto realod value
slaveTimer.setPrescaler(TimerUtils::computePrescalerValue(
slaveTimer.getTimer(), spiFrequency * 4));
slaveTimer.setAutoReloadRegister(1);
// Set SCK capture/compare register
slaveTimer.setCaptureCompareRegister(sckChannel, 1);
// Set channel 1 to toggle mode
slaveTimer.setOutputCompareMode(sckChannel,
TimerUtils::OutputCompareMode::TOGGLE);
if (spiMode >= SPI::Mode::MODE_2)
{
slaveTimer.setCaptureComparePolarity(
sckChannel, TimerUtils::OutputComparePolarity::ACTIVE_LOW);
}
// Enable capture/compare output
slaveTimer.enableCaptureCompareOutput(sckChannel);
// Update the register
slaveTimer.generateUpdate();
}
}
inline void SPISignalGenerator::enable()
{
slaveTimer.enable();
masterTimer.enable();
}
inline void SPISignalGenerator::disable()
{
masterTimer.disable();
slaveTimer.disable();
}
inline void SPISignalGenerator::generateSingleTransaction(size_t nBytes)
{
// Change the number of bytes to generate
size_t backupNBytes = this->nBytes;
this->nBytes = nBytes;
// Change the period to generate a transaction right away
int backupTransactionFrequency = transactionFrequency;
transactionFrequency = spiFrequency * 4 / (nBytes * 8 * 4);
// Configure the timers
configure();
// Enable one pulse mode
masterTimer.enableOnePulseMode();
// Reset nBytes
this->nBytes = backupNBytes;
transactionFrequency = backupTransactionFrequency;
// Start the signal generation
enable();
}
} // namespace Boardcore
/* Copyright (c) 2021 Skyward Experimental Rocketry
* Author: Alberto Nidasio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include "SPIBusInterface.h"
#include "SPISignalGenerator.h"
namespace Boardcore
{
/**
* @brief This implementation of SPIBusInterface uses the spi peripheral in
* slave mode and the spi signal generator to act as an spi master.
*/
class SPISlaveBus : public SPIBusInterface
{
public:
SPISlaveBus(SPIType* spi, SPISignalGenerator signalGenerator);
///< Delete copy/move contructors/operators.
SPISlaveBus(const SPISlaveBus&) = delete;
SPISlaveBus& operator=(const SPISlaveBus&) = delete;
SPISlaveBus(SPISlaveBus&&) = delete;
SPISlaveBus& operator=(SPISlaveBus&&) = delete;
/**
* @brief Configures and enables the bus with the provided configuration.
*
* Since this implementation is not syncronized, if configure() is called on
* an already in use bus nothing will be done.
*
* Use SyncedSPIBus if you need to synchronize access to the bus.
*/
void configure(SPIBusConfig config) override;
/**
* @brief See SPIBusInterface::select().
*/
void select(GpioType& cs) override;
/**
* @brief See SPIBusInterface::deselect().
*/
void deselect(GpioType& cs) override;
// Read, write and transfer operations
/**
* @brief Reads a single byte from the bus.
*
* @return Byte read from the bus.
*/
uint8_t read() override;
/**
* @brief Reads a single half word from the bus.
*
* @return Half word read from the bus.
*/
uint16_t read16() override;
/**
* @brief Reads multiple bytes from the bus
*
* @param data Buffer to be filled with received data.
* @param size Size of the buffer in bytes.
*/
void read(uint8_t* data, size_t size) override;
/**
* @brief Reads multiple half words from the bus
*
* @param data Buffer to be filled with received data.
* @param size Size of the buffer in bytes.
*/
void read(uint16_t* data, size_t size) override;
/**
* @brief Writes a single byte to the bus.
*
* @param data Byte to write.
*/
void write(uint8_t data) override;
/**
* @brief Writes a single half word to the bus.
*
* @param data Half word to write.
*/
void write(uint16_t data) override;
/**
* @brief Writes multiple bytes to the bus.
*
* @param data Buffer containing data to write.
* @param size Size of the buffer in bytes.
*/
void write(uint8_t* data, size_t size) override;
/**
* @brief Writes multiple half words to the bus.
*
* @param data Buffer containing data to write.
* @param size Size of the buffer in bytes.
*/
void write(uint16_t* data, size_t size) override;
/**
* @brief Full duplex transmission of one byte on the bus.
*
* @param data Byte to write.
* @return Byte read from the bus.
*/
uint8_t transfer(uint8_t data) override;
/**
* @brief Full duplex transmission of one half word on the bus.
*
* @param data Half word to write.
* @return Half word read from the bus.
*/
uint16_t transfer(uint16_t data) override;
/**
* @brief Full duplex transmission of multiple bytes on the bus.
*
* @param data Buffer containing data to trasfer.
* @param size Size of the buffer in bytes.
*/
void transfer(uint8_t* data, size_t size) override;
/**
* @brief Full duplex transmission of multiple half words on the bus.
*
* @param data Buffer containing data to trasfer.
* @param size Size of the buffer in bytes.
*/
void transfer(uint16_t* data, size_t size) override;
private:
SPI spi;
SPISignalGenerator signalGenerator;
SPIBusConfig config{};
};
inline SPISlaveBus::SPISlaveBus(SPIType* spi,
SPISignalGenerator signalGenerator)
: spi(spi), signalGenerator(signalGenerator)
{
}
inline void SPISlaveBus::configure(SPIBusConfig newConfig)
{
// Save the new configuration
config = newConfig;
// Wait until the peripheral is done before changing configuration
spi.waitPeripheral();
// Disable the peripheral
spi.disable();
// Configure clock polarity and phase
spi.setMode(config.mode);
// Configure bit order
spi.setBitOrder(config.bitOrder);
// Enable the peripheral
spi.enable();
}
inline void SPISlaveBus::select(GpioType& cs) {}
inline void SPISlaveBus::deselect(GpioType& cs) {}
// Read, write and transfer operations
inline uint8_t SPISlaveBus::read()
{
signalGenerator.generateSingleTransaction(1);
return spi.read();
}
inline uint16_t SPISlaveBus::read16()
{
signalGenerator.generateSingleTransaction(2);
return spi.read16();
}
inline void SPISlaveBus::read(uint8_t* data, size_t size)
{
signalGenerator.generateSingleTransaction(size);
spi.read(data, size);
}
inline void SPISlaveBus::read(uint16_t* data, size_t size)
{
signalGenerator.generateSingleTransaction(size);
spi.read(data, size);
}
inline void SPISlaveBus::write(uint8_t data)
{
signalGenerator.generateSingleTransaction(1);
spi.write(data);
}
inline void SPISlaveBus::write(uint16_t data)
{
signalGenerator.generateSingleTransaction(2);
spi.write(data);
}
inline void SPISlaveBus::write(uint8_t* data, size_t size)
{
signalGenerator.generateSingleTransaction(size);
spi.write(data, size);
}
inline void SPISlaveBus::write(uint16_t* data, size_t size)
{
signalGenerator.generateSingleTransaction(size);
spi.write(data, size);
}
inline uint8_t SPISlaveBus::transfer(uint8_t data)
{
signalGenerator.generateSingleTransaction(1);
return spi.transfer(data);
}
inline uint16_t SPISlaveBus::transfer(uint16_t data)
{
signalGenerator.generateSingleTransaction(2);
return spi.transfer(data);
}
inline void SPISlaveBus::transfer(uint8_t* data, size_t size)
{
signalGenerator.generateSingleTransaction(size);
spi.transfer(data, size);
}
inline void SPISlaveBus::transfer(uint16_t* data, size_t size)
{
signalGenerator.generateSingleTransaction(size);
spi.transfer(data, size);
}
} // namespace Boardcore
......@@ -22,158 +22,287 @@
#include "SPITransaction.h"
#include <interfaces/endianness.h>
namespace Boardcore
{
SPITransaction::SPITransaction(SPISlave slave, WriteBit writeBit)
: SPITransaction(slave.bus, slave.cs, slave.config, writeBit)
SPITransaction::SPITransaction(const SPISlave& slave) : slave(slave)
{
slave.bus.configure(slave.config);
}
SPITransaction::SPITransaction(SPIBusInterface& bus, GpioType cs,
SPIBusConfig config, WriteBit writeBit)
: bus(bus), writeBit(writeBit), cs(cs)
{
bus.configure(config);
}
SPIBusInterface& SPITransaction::getBus() { return bus; }
SPIBusInterface& SPITransaction::getBus() { return slave.bus; }
// Read, write and transfer operations in master mode
uint8_t SPITransaction::read()
{
bus.select(cs);
uint8_t data = bus.read();
bus.deselect(cs);
slave.bus.select(slave.cs);
uint8_t data = slave.bus.read();
slave.bus.deselect(slave.cs);
return data;
}
uint16_t SPITransaction::read16()
{
bus.select(cs);
uint16_t data = bus.read16();
bus.deselect(cs);
slave.bus.select(slave.cs);
uint16_t data = slave.bus.read16();
slave.bus.deselect(slave.cs);
return data;
}
uint32_t SPITransaction::read24()
{
slave.bus.select(slave.cs);
uint32_t data = slave.bus.read24();
slave.bus.deselect(slave.cs);
return data;
}
uint32_t SPITransaction::read32()
{
slave.bus.select(slave.cs);
uint32_t data = slave.bus.read32();
slave.bus.deselect(slave.cs);
return data;
}
void SPITransaction::read(uint8_t* data, size_t size)
{
bus.select(cs);
bus.read(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.read(data, size);
slave.bus.deselect(slave.cs);
}
void SPITransaction::read(uint16_t* data, size_t size)
void SPITransaction::read16(uint16_t* data, size_t size)
{
bus.select(cs);
bus.read(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.read16(data, size);
slave.bus.deselect(slave.cs);
}
void SPITransaction::write(uint8_t data)
{
bus.select(cs);
bus.write(data);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::write16(uint16_t data)
{
slave.bus.select(slave.cs);
slave.bus.write16(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::write24(uint32_t data)
{
slave.bus.select(slave.cs);
slave.bus.write24(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::write(uint16_t data)
void SPITransaction::write32(uint32_t data)
{
bus.select(cs);
bus.write(data);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write32(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::write(uint8_t* data, size_t size)
{
bus.select(cs);
bus.write(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write(data, size);
slave.bus.deselect(slave.cs);
}
void SPITransaction::write(uint16_t* data, size_t size)
void SPITransaction::write16(uint16_t* data, size_t size)
{
bus.select(cs);
bus.write(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write16(data, size);
slave.bus.deselect(slave.cs);
}
uint8_t SPITransaction::transfer(uint8_t data)
{
bus.select(cs);
data = bus.transfer(data);
bus.deselect(cs);
slave.bus.select(slave.cs);
data = slave.bus.transfer(data);
slave.bus.deselect(slave.cs);
return data;
}
uint16_t SPITransaction::transfer16(uint16_t data)
{
slave.bus.select(slave.cs);
data = slave.bus.transfer16(data);
slave.bus.deselect(slave.cs);
return data;
}
uint32_t SPITransaction::transfer24(uint32_t data)
{
slave.bus.select(slave.cs);
data = slave.bus.transfer24(data);
slave.bus.deselect(slave.cs);
return data;
}
uint16_t SPITransaction::transfer(uint16_t data)
uint32_t SPITransaction::transfer32(uint32_t data)
{
bus.select(cs);
data = bus.transfer(data);
bus.deselect(cs);
slave.bus.select(slave.cs);
data = slave.bus.transfer32(data);
slave.bus.deselect(slave.cs);
return data;
}
void SPITransaction::transfer(uint8_t* data, size_t size)
{
bus.select(cs);
bus.transfer(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.transfer(data, size);
slave.bus.deselect(slave.cs);
}
void SPITransaction::transfer(uint16_t* data, size_t size)
void SPITransaction::transfer16(uint16_t* data, size_t size)
{
bus.select(cs);
bus.transfer(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.transfer16(data, size);
slave.bus.deselect(slave.cs);
}
// Read, write and transfer operations with registers
uint8_t SPITransaction::readRegister(uint8_t reg)
{
if (writeBit == WriteBit::NORMAL)
if (slave.config.writeBit == SPI::WriteBit::NORMAL)
reg |= 0x80;
slave.bus.select(slave.cs);
slave.bus.write(reg);
uint8_t data = slave.bus.read();
slave.bus.deselect(slave.cs);
return data;
}
uint16_t SPITransaction::readRegister16(uint8_t reg)
{
if (slave.config.writeBit == SPI::WriteBit::NORMAL)
reg |= 0x80;
slave.bus.select(slave.cs);
slave.bus.write(reg);
uint16_t data = slave.bus.read16();
slave.bus.deselect(slave.cs);
if (slave.config.byteOrder == SPI::Order::LSB_FIRST)
data = swapBytes16(data);
return data;
}
uint32_t SPITransaction::readRegister24(uint8_t reg)
{
if (slave.config.writeBit == SPI::WriteBit::NORMAL)
reg |= 0x80;
slave.bus.select(slave.cs);
slave.bus.write(reg);
uint32_t data = slave.bus.read24();
slave.bus.deselect(slave.cs);
if (slave.config.byteOrder == SPI::Order::LSB_FIRST)
data = swapBytes32(data) >> 8;
return data;
}
uint32_t SPITransaction::readRegister32(uint8_t reg)
{
if (slave.config.writeBit == SPI::WriteBit::NORMAL)
reg |= 0x80;
bus.select(cs);
bus.write(reg);
uint8_t data = bus.read();
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write(reg);
uint32_t data = slave.bus.read32();
slave.bus.deselect(slave.cs);
if (slave.config.byteOrder == SPI::Order::LSB_FIRST)
data = swapBytes32(data) >> 8;
return data;
}
void SPITransaction::readRegisters(uint8_t reg, uint8_t* data, size_t size)
{
if (writeBit == WriteBit::NORMAL)
if (slave.config.writeBit == SPI::WriteBit::NORMAL)
reg |= 0x80;
bus.select(cs);
bus.write(reg);
bus.read(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write(reg);
slave.bus.read(data, size);
slave.bus.deselect(slave.cs);
}
void SPITransaction::writeRegister(uint8_t reg, uint8_t data)
{
if (writeBit == WriteBit::INVERTED)
if (slave.config.writeBit == SPI::WriteBit::INVERTED)
reg |= 0x80;
slave.bus.select(slave.cs);
slave.bus.write(reg);
slave.bus.write(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::writeRegister16(uint8_t reg, uint16_t data)
{
if (slave.config.writeBit == SPI::WriteBit::INVERTED)
reg |= 0x80;
slave.bus.select(slave.cs);
slave.bus.write(reg);
slave.bus.write16(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::writeRegister24(uint8_t reg, uint32_t data)
{
if (slave.config.writeBit == SPI::WriteBit::INVERTED)
reg |= 0x80;
slave.bus.select(slave.cs);
slave.bus.write(reg);
slave.bus.write24(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::writeRegister32(uint8_t reg, uint32_t data)
{
if (slave.config.writeBit == SPI::WriteBit::INVERTED)
reg |= 0x80;
bus.select(cs);
bus.write(reg);
bus.write(data);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write(reg);
slave.bus.write32(data);
slave.bus.deselect(slave.cs);
}
void SPITransaction::writeRegisters(uint8_t reg, uint8_t* data, size_t size)
{
if (writeBit == WriteBit::INVERTED)
if (slave.config.writeBit == SPI::WriteBit::INVERTED)
reg |= 0x80;
bus.select(cs);
bus.write(reg);
bus.write(data, size);
bus.deselect(cs);
slave.bus.select(slave.cs);
slave.bus.write(reg);
slave.bus.write(data, size);
slave.bus.deselect(slave.cs);
}
} // namespace Boardcore
......@@ -56,34 +56,25 @@ namespace Boardcore
class SPITransaction
{
public:
enum class WriteBit
{
NORMAL, ///< Normal write bit settings (0 for write, 1 for reads)
INVERTED, ///< Inverted write bit settings (1 for write, 0 for reads)
DISABLED, ///< Do not set write bit in any way
};
/**
* @brief Instantiates a new SPITransaction, configuring the bus with the
* provided parameters.
*
* @param slave Slave to communicate with.
*/
explicit SPITransaction(SPISlave slave,
WriteBit writeBit = WriteBit::NORMAL);
explicit SPITransaction(const SPISlave &slave);
/**
* @brief Instantiates a new SPITransaction, configuring the bus with the
* provided parameters.
*
* @param bus Bus to communicate on.
* @param cs Chip select of the slave to communicate to.
* @param config Configuration of the bus for the selected slave.
*/
SPITransaction(SPIBusInterface &bus, GpioType cs, SPIBusConfig config,
WriteBit writeBit = WriteBit::NORMAL);
// /**
// * @brief Instantiates a new SPITransaction, configuring the bus with the
// * provided parameters.
// *
// * @param bus Bus to communicate on.
// * @param cs Chip select of the slave to communicate to.
// * @param config Configuration of the bus for the selected slave.
// */
// SPITransaction(SPIBusInterface &bus, GpioType cs, SPIBusConfig config);
///< Delete copy/move contructors/operators.
///< Delete copy/move constructors/operators.
SPITransaction(const SPITransaction &) = delete;
SPITransaction &operator=(const SPITransaction &) = delete;
SPITransaction(SPITransaction &&) = delete;
......@@ -112,6 +103,20 @@ public:
*/
uint16_t read16();
/**
* @brief Reads 24 bits from the bus.
*
* @return Bytes read from the bus (MSB of the uint32_t value will be 0).
*/
virtual uint32_t read24();
/**
* @brief Reads 32 bits from the bus.
*
* @return Word read from the bus.
*/
virtual uint32_t read32();
/**
* @brief Reads multiple bytes from the bus
*
......@@ -126,7 +131,7 @@ public:
* @param data Buffer to be filled with received data.
* @param size Size of the buffer in bytes.
*/
void read(uint16_t *data, size_t size);
void read16(uint16_t *data, size_t size);
/**
* @brief Writes a single byte to the bus.
......@@ -140,7 +145,21 @@ public:
*
* @param data Half word to write.
*/
void write(uint16_t data);
void write16(uint16_t data);
/**
* @brief Writes 24 bits to the bus.
*
* @param data Bytes to write (the MSB of the uint32_t is not used).
*/
virtual void write24(uint32_t data);
/**
* @brief Writes 32 bits to the bus.
*
* @param data Word to write.
*/
virtual void write32(uint32_t data);
/**
* @brief Writes multiple bytes to the bus.
......@@ -156,7 +175,7 @@ public:
* @param data Buffer containing data to write.
* @param size Size of the buffer in bytes.
*/
void write(uint16_t *data, size_t size);
void write16(uint16_t *data, size_t size);
/**
* @brief Full duplex transmission of one byte on the bus.
......@@ -172,12 +191,28 @@ public:
* @param data Half word to write.
* @return Half word read from the bus.
*/
uint16_t transfer(uint16_t data);
uint16_t transfer16(uint16_t data);
/**
* @brief Full duplex transmission of 24 bits on the bus.
*
* @param data Bytes to write (the MSB of the uint32_t is not used).
* @return Bytes read from the bus (the MSB of the uint32_t will be 0).
*/
virtual uint32_t transfer24(uint32_t data);
/**
* @brief Full duplex transmission of 32 bits on the bus.
*
* @param data Word to write.
* @return Half word read from the bus.
*/
virtual uint32_t transfer32(uint32_t data);
/**
* @brief Full duplex transmission of multiple bytes on the bus.
*
* @param data Buffer containing data to trasfer.
* @param data Buffer containing data to transfer.
* @param size Size of the buffer in bytes.
*/
void transfer(uint8_t *data, size_t size);
......@@ -185,20 +220,41 @@ public:
/**
* @brief Full duplex transmission of multiple half words on the bus.
*
* @param data Buffer containing data to trasfer.
* @param data Buffer containing data to transfer.
* @param size Size of the buffer in bytes.
*/
void transfer(uint16_t *data, size_t size);
void transfer16(uint16_t *data, size_t size);
// Read, write and transfer operations with registers
/**
* @brief Reads the specified register.
* @brief Reads an 8 bit register.
*
* @return Byte read from the register.
*/
uint8_t readRegister(uint8_t reg);
/**
* @brief Reads a 16 bit register.
*
* @return Byte read from the register.
*/
uint16_t readRegister16(uint8_t reg);
/**
* @brief Reads a 24 bit register.
*
* @return Byte read from the register.
*/
uint32_t readRegister24(uint8_t reg);
/**
* @brief Reads a 32 bit register.
*
* @return Byte read from the register.
*/
uint32_t readRegister32(uint8_t reg);
/**
* @brief Reads multiple bytes starting from the specified register.
*
......@@ -208,13 +264,37 @@ public:
void readRegisters(uint8_t reg, uint8_t *data, size_t size);
/**
* @brief Writes a single byte register.
* @brief Writes an 8 bit register.
*
* @param reg Register address.
* @param data Byte to write.
*/
void writeRegister(uint8_t reg, uint8_t data);
/**
* @brief Writes a 16 bit register.
*
* @param reg Register address.
* @param data Byte to write.
*/
void writeRegister16(uint8_t reg, uint16_t data);
/**
* @brief Writes a 24 bit register.
*
* @param reg Register address.
* @param data Byte to write.
*/
void writeRegister24(uint8_t reg, uint32_t data);
/**
* @brief Writes a 32 bit register.
*
* @param reg Register address.
* @param data Byte to write.
*/
void writeRegister32(uint8_t reg, uint32_t data);
/**
* @brief Writes multiple bytes starting from the specified register.
*
......@@ -225,9 +305,7 @@ public:
void writeRegisters(uint8_t reg, uint8_t *data, size_t size);
private:
SPIBusInterface &bus;
WriteBit writeBit;
GpioType cs;
const SPISlave &slave;
};
} // namespace Boardcore
......@@ -71,7 +71,7 @@ SX1278Fsk::Error SX1278Fsk::init(const Config &config)
bool SX1278Fsk::checkVersion()
{
Lock guard(*this);
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
return spi.readRegister(REG_VERSION) == 0x12;
}
......@@ -108,7 +108,7 @@ SX1278Fsk::Error SX1278Fsk::configure(const Config &config)
// Setup generic parameters
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_RX_CONFIG,
RegRxConfig::AFC_AUTO_ON | RegRxConfig::AGC_AUTO_ON |
......@@ -155,7 +155,7 @@ ssize_t SX1278Fsk::receive(uint8_t *pkt, size_t max_len)
last_rx_rssi = getRssi();
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
len = spi.readRegister(REG_FIFO);
if (len > max_len)
return -1;
......@@ -186,7 +186,7 @@ bool SX1278Fsk::send(uint8_t *pkt, size_t len)
waitForIrq(guard_mode, RegIrqFlags::TX_READY);
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_FIFO, static_cast<uint8_t>(len));
spi.writeRegisters(REG_FIFO, pkt, len);
......@@ -226,7 +226,7 @@ void SX1278Fsk::rateLimitTx()
void SX1278Fsk::imageCalibrate()
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_IMAGE_CAL, REG_IMAGE_CAL_DEFAULT | (1 << 6));
// Wait for calibration complete by polling on running register
......@@ -262,7 +262,7 @@ DioMask SX1278Fsk::getDioMaskFromIrqFlags(IrqFlags flags, Mode mode,
ISX1278::IrqFlags SX1278Fsk::getIrqFlags()
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
uint8_t flags_msb = spi.readRegister(REG_IRQ_FLAGS_1);
uint8_t flags_lsb = spi.readRegister(REG_IRQ_FLAGS_2);
......@@ -279,7 +279,7 @@ void SX1278Fsk::resetIrqFlags(IrqFlags flags)
if (flags != 0)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_IRQ_FLAGS_1, flags >> 8);
spi.writeRegister(REG_IRQ_FLAGS_2, flags);
}
......@@ -287,14 +287,14 @@ void SX1278Fsk::resetIrqFlags(IrqFlags flags)
float SX1278Fsk::getRssi()
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
return static_cast<float>(spi.readRegister(REG_RSSI_VALUE)) * -0.5f;
}
float SX1278Fsk::getFei()
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
// Order of read is important!!
uint8_t fei_msb = spi.readRegister(REG_FEI_MSB);
......@@ -308,13 +308,13 @@ float SX1278Fsk::getFei()
void SX1278Fsk::setMode(Mode mode)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_OP_MODE, REG_OP_MODE_DEFAULT | mode);
}
void SX1278Fsk::setMapping(SX1278::DioMapping mapping)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_DIO_MAPPING_1, mapping.raw >> 8);
spi.writeRegister(REG_DIO_MAPPING_2, mapping.raw);
}
......@@ -324,7 +324,7 @@ void SX1278Fsk::setBitrate(int bitrate)
uint16_t val = FXOSC / bitrate;
// Update values
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_BITRATE_MSB, val >> 8);
spi.writeRegister(REG_BITRATE_LSB, val);
}
......@@ -332,7 +332,7 @@ void SX1278Fsk::setBitrate(int bitrate)
void SX1278Fsk::setFreqDev(int freq_dev)
{
uint16_t val = freq_dev / FSTEP;
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_FDEV_MSB, (val >> 8) & 0x3f);
spi.writeRegister(REG_FDEV_LSB, val);
}
......@@ -342,7 +342,7 @@ void SX1278Fsk::setFreqRF(int freq_rf)
uint32_t val = freq_rf / FSTEP;
// Update values
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_FRF_MSB, val >> 16);
spi.writeRegister(REG_FRF_MID, val >> 8);
spi.writeRegister(REG_FRF_LSB, val);
......@@ -350,7 +350,7 @@ void SX1278Fsk::setFreqRF(int freq_rf)
void SX1278Fsk::setOcp(int ocp)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
if (ocp == 0)
{
spi.writeRegister(REG_OCP, 0);
......@@ -369,7 +369,7 @@ void SX1278Fsk::setOcp(int ocp)
void SX1278Fsk::setSyncWord(uint8_t value[], int size)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_SYNC_CONFIG, REG_SYNC_CONFIG_DEFAULT | size);
for (int i = 0; i < size; i++)
......@@ -380,19 +380,19 @@ void SX1278Fsk::setSyncWord(uint8_t value[], int size)
void SX1278Fsk::setRxBw(RxBw rx_bw)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_RX_BW, static_cast<uint8_t>(rx_bw));
}
void SX1278Fsk::setAfcBw(RxBw afc_bw)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_AFC_BW, static_cast<uint8_t>(afc_bw));
}
void SX1278Fsk::setPreambleLen(int len)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_PREAMBLE_MSB, len >> 8);
spi.writeRegister(REG_PREAMBLE_LSB, len);
}
......@@ -404,7 +404,7 @@ void SX1278Fsk::setPa(int power, bool pa_boost)
const uint8_t MAX_POWER = 0b111;
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
if (!pa_boost)
{
......@@ -435,7 +435,7 @@ void SX1278Fsk::setPa(int power, bool pa_boost)
void SX1278Fsk::setShaping(Shaping shaping)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_PA_RAMP, static_cast<uint8_t>(shaping) | 0x09);
}
......@@ -505,7 +505,7 @@ void SX1278Fsk::debugDumpRegisters()
RegDef{"REG_DIO_MAPPING_2", REG_DIO_MAPPING_2},
RegDef{"REG_VERSION", REG_VERSION}, RegDef{NULL, 0}};
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
int i = 0;
while (defs[i].name)
......
......@@ -149,7 +149,7 @@ SX1278Lora::Error SX1278Lora::init(const Config &config)
bool SX1278Lora::checkVersion()
{
Lock guard(*this);
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
return spi.readRegister(REG_VERSION) == 0x12;
}
......@@ -188,7 +188,7 @@ SX1278Lora::Error SX1278Lora::configure(const Config &config)
ErrataRegistersValues::calculate(bw, freq_rf);
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
// Setup FIFO sections
spi.writeRegister(REG_FIFO_TX_BASE_ADDR, FIFO_TX_BASE_ADDR);
......@@ -277,7 +277,7 @@ ssize_t SX1278Lora::receive(uint8_t *pkt, size_t max_len)
{
Lock guard(*this);
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
do
{
......@@ -305,7 +305,7 @@ bool SX1278Lora::send(uint8_t *pkt, size_t len)
Lock guard(*this);
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_PAYLOAD_LENGTH, len);
writeFifo(FIFO_TX_BASE_ADDR, pkt, len);
......@@ -327,7 +327,7 @@ float SX1278Lora::getLastRxRssi()
float rssi;
{
Lock guard(*this);
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
rssi =
static_cast<float>(spi.readRegister(REG_PKT_RSSI_VALUE)) - 164.0f;
}
......@@ -343,7 +343,7 @@ float SX1278Lora::getLastRxRssi()
float SX1278Lora::getLastRxSnr()
{
Lock guard(*this);
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
return static_cast<float>(
static_cast<int8_t>(spi.readRegister(REG_PKT_SNR_VALUE))) /
4.0f;
......@@ -351,14 +351,14 @@ float SX1278Lora::getLastRxSnr()
void SX1278Lora::readFifo(uint8_t addr, uint8_t *dst, uint8_t size)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_FIFO_ADDR_PTR, addr);
spi.readRegisters(REG_FIFO, dst, size);
}
void SX1278Lora::writeFifo(uint8_t addr, uint8_t *src, uint8_t size)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_FIFO_ADDR_PTR, addr);
spi.writeRegisters(REG_FIFO, src, size);
}
......@@ -393,20 +393,20 @@ SX1278::DioMask SX1278Lora::getDioMaskFromIrqFlags(IrqFlags flags, Mode _mode,
ISX1278::IrqFlags SX1278Lora::getIrqFlags()
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
return spi.readRegister(REG_IRQ_FLAGS);
}
void SX1278Lora::resetIrqFlags(IrqFlags flags)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
// Register is write clear
spi.writeRegister(REG_IRQ_FLAGS, flags);
}
void SX1278Lora::setMode(ISX1278::Mode mode)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(
REG_OP_MODE,
......@@ -415,7 +415,7 @@ void SX1278Lora::setMode(ISX1278::Mode mode)
void SX1278Lora::setMapping(SX1278::DioMapping mapping)
{
SPITransaction spi(slave, SPITransaction::WriteBit::INVERTED);
SPITransaction spi(slave);
spi.writeRegister(REG_DIO_MAPPING_1, mapping.raw >> 8);
spi.writeRegister(REG_DIO_MAPPING_2, mapping.raw);
}
......
......@@ -63,8 +63,7 @@ public:
uint8_t odr = OutputDataRate::ODR_100_HZ,
uint8_t bdu = BlockDataUpdate::UPDATE_AFTER_READ_MODE,
uint8_t fullScale = FullScale::FULL_SCALE_2G)
: spiSlave(bus, chipSelect, {}), odr(odr), bdu(bdu),
fullScale(fullScale)
: spiSlave(bus, chipSelect), odr(odr), bdu(bdu), fullScale(fullScale)
{
spiSlave.config.clockDivider =
SPI::ClockDivider::DIV_64; // used to set the spi baud rate
......
......@@ -51,7 +51,7 @@ bool MAX31855::checkConnection()
{
SPITransaction spi{slave};
spi.read(sample, sizeof(sample));
spi.read16(sample, sizeof(sample));
}
// Bits D0, D1 and D2 go high if thermocouple is open or shorted either to
......@@ -90,7 +90,7 @@ TemperatureData MAX31855::readInternalTemperature()
{
SPITransaction spi{slave};
spi.read(sample, sizeof(sample));
spi.read16(sample, sizeof(sample));
}
TemperatureData result{};
......
......@@ -32,11 +32,13 @@ MS5803::MS5803(SPIBusInterface& spiBus, miosix::GpioPin cs,
SPIBusConfig spiConfig, uint16_t temperatureDivider)
: spiSlave(spiBus, cs, spiConfig), temperatureDivider(temperatureDivider)
{
// Ensure that the write bit is disabled
spiSlave.config.writeBit = SPI::WriteBit::DISABLED;
}
bool MS5803::init()
{
SPITransaction transaction{spiSlave, SPITransaction::WriteBit::DISABLED};
SPITransaction transaction{spiSlave};
// Read calibration data
calibrationData.sens = readReg(transaction, REG_PROM_SENS_MASK);
......@@ -59,7 +61,7 @@ bool MS5803::selfTest() { return true; }
MS5803Data MS5803::sampleImpl()
{
SPITransaction transaction{spiSlave, SPITransaction::WriteBit::DISABLED};
SPITransaction transaction{spiSlave};
uint8_t buffer[3];
......
......@@ -98,7 +98,7 @@ private:
uint16_t readReg(SPITransaction& spi, uint8_t reg);
const SPISlave spiSlave;
SPISlave spiSlave;
MS5803CalibrationData calibrationData;
uint16_t temperatureDivider;
......
......@@ -49,8 +49,9 @@ UBXGPSSpi::UBXGPSSpi(SPIBusInterface& spiBus, miosix::GpioPin spiCs,
SPIBusConfig UBXGPSSpi::getDefaultSPIConfig()
{
return SPIBusConfig{SPI::ClockDivider::DIV_16, SPI::Mode::MODE_0,
SPI::BitOrder::MSB_FIRST, 10, 10};
SPIBusConfig config;
config.clockDivider = SPI::ClockDivider::DIV_16;
return config;
}
uint8_t UBXGPSSpi::getSampleRate() { return sampleRate; }
......
/* Copyright (c) 2021 Skyward Experimental Rocketry
* Author: Alberto Nidasio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <miosix.h>
using namespace miosix;
static bool dmaComplete = false;
static bool dmaError = false;
static uint8_t txData[] = {0xA8, 0x00};
static uint8_t rxData[2] = {0};
GpioPin csPin = GpioPin(GPIOC_BASE, 1);
GpioPin sckPin = GpioPin(GPIOF_BASE, 7);
GpioPin misoPin = GpioPin(GPIOF_BASE, 8);
GpioPin mosiPin = GpioPin(GPIOF_BASE, 9);
int main()
{
// SPI INIT
{
csPin.mode(Mode::OUTPUT);
csPin.high();
sckPin.mode(Mode::ALTERNATE);
sckPin.alternateFunction(5);
mosiPin.mode(Mode::ALTERNATE);
mosiPin.alternateFunction(5);
misoPin.mode(Mode::ALTERNATE);
misoPin.alternateFunction(5);
// Enable SPI clock for SPI5 interface
RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
// Clear configuration for SPI interface
SPI5->CR1 = 0;
SPI5->CR2 = 0;
// 1: Set BR[2:0] bits to define the baud rate
SPI5->CR1 |= SPI_CR1_BR;
// 2: Setup clock phase and polarity (MODE1: CPOL = 1, CPHA = 1)
SPI5->CR1 |= SPI_CR1_CPOL | SPI_CR1_CPHA;
// 7: Set MSTR to set master mode
SPI5->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM | SPI_CR1_MSTR;
// 8: Enable DMA for tx
SPI5->CR2 |= SPI_CR2_TXDMAEN;
SPI5->CR2 |= SPI_CR2_RXDMAEN;
// Enable SPI
SPI5->CR1 |= SPI_CR1_SPE;
printf("SPI interface setup completed...\n");
}
// 0: Enable DMA2 clock
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
// DMA2 Stream4 channel 3 for SPI5 TX
{
// 1: Disable the stream
// Disable stream
DMA2_Stream4->CR &= ~DMA_SxCR_EN;
// Wait for the stream to be disabled
while (DMA2_Stream4->CR & DMA_SxCR_EN)
;
// Reset DMA configuration
DMA2_Stream4->CR = 0;
// Ensures all the status bits are cleared by explicitly clearing them
DMA2->LIFCR = 0x0F7D0F7D; // Mask excluding reserved bits
DMA2->HIFCR = 0x0F7D0F7D;
// 2: Set the peripheral address
DMA2_Stream4->PAR = (uint32_t) & (SPI5->DR);
// 3: Set the memory address
DMA2_Stream4->M0AR = (uint32_t)txData;
// 4: Configure the total number of data items
DMA2_Stream4->NDTR = 2;
// 5: Select the DMA channel
DMA2_Stream4->CR |= DMA_SxCR_CHSEL_1; // Channel 2 for SPI5 Tx
// 7: Configure the stream priority to very high
DMA2_Stream4->CR |= DMA_SxCR_PL;
// 9: Other configuration
// Data transfer memory-to-peripheral
DMA2_Stream4->CR |= DMA_SxCR_DIR_0;
// Address increment mode
DMA2_Stream4->CR |= DMA_SxCR_MINC;
// Interrupts (transfer complete)
DMA2_Stream4->CR |= DMA_SxCR_TCIE;
// Register interrupt
NVIC_EnableIRQ(DMA2_Stream4_IRQn);
NVIC_SetPriority(DMA2_Stream4_IRQn, 15);
}
// DMA2 Stream3 channel 3 for SPI5 RX
{
// 1: Disable the stream
// Disable stream
DMA2_Stream3->CR &= ~DMA_SxCR_EN;
// Wait for the stream to be disabled
while (DMA2_Stream3->CR & DMA_SxCR_EN)
;
// Reset DMA configuration
DMA2_Stream3->CR = 0;
// Ensures all the status bits are cleared by explicitly clearing them
DMA1->LIFCR = 0x0F7D0F7D; // Mask excluding reserved bits
DMA1->HIFCR = 0x0F7D0F7D;
// 2: Set the peripheral address
DMA2_Stream3->PAR = (uint32_t) & (SPI5->DR);
// 3: Set the memory address
DMA2_Stream3->M0AR = (uint32_t)rxData;
// 4: Configure the total number of data items
DMA2_Stream3->NDTR = 2;
// 5: Select the DMA channel
DMA2_Stream3->CR |= DMA_SxCR_CHSEL_1; // Channel 2 for SPI5 Rx
// 7: Configure the stream priority to very high
DMA2_Stream3->CR |= DMA_SxCR_PL;
// 9: Other configuration
// Address increment mode
DMA2_Stream3->CR |= DMA_SxCR_MINC;
}
// Transaction 1
while (true)
{
DMA2->LIFCR = 0x0F7D0F7D;
DMA2->HIFCR = 0x0F7D0F7D;
csPin.low();
// Enable DMA to start serving requests from SPI interface
DMA2_Stream3->CR |= DMA_SxCR_EN;
DMA2_Stream4->CR |= DMA_SxCR_EN;
delayUs(1);
// Wait for completion
while ((SPI5->SR & SPI_SR_TXE) == 0)
;
while (SPI5->SR & SPI_SR_BSY)
;
csPin.high();
if (dmaError)
{
dmaError = false;
printf("DMA transfer error!\n");
}
if (dmaComplete)
{
dmaError = false;
printf("DMA transfer completed!\n");
}
printf("Rx data: 0x%02X%02X\n", rxData[0], rxData[1]);
rxData[0] = 0;
rxData[1] = 0;
delayMs(1000);
}
}
void __attribute__((naked)) DMA2_Stream4_IRQHandler()
{
saveContext();
asm volatile("bl _Z27DMA2_Stream4_IRQHandlerImplv");
restoreContext();
}
void __attribute__((used)) DMA2_Stream4_IRQHandlerImpl()
{
if (DMA2->HISR & DMA_HISR_TCIF4)
{
dmaComplete = true;
// Clear the interrupt
SET_BIT(DMA2->HIFCR, DMA_HIFCR_CTCIF4);
}
if (DMA2->HISR & DMA_HISR_TEIF4)
{
dmaError = true;
// Clear the interrupt
SET_BIT(DMA2->HIFCR, DMA_HIFCR_CTEIF4);
}
}