Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /***********************************************************************************
- * @file STM32SPI_SerialPeripheralInterface.c
- ***********************************************************************************
- * _ _____ ____ ____ _____
- * | |/ /_ _/ ___|| _ \| ____|
- * | ' / | |\___ \| |_) | _|
- * | . \ | | ___) | __/| |___
- * |_|\_\___|____/|_| |_____|
- *
- ***********************************************************************************
- * Copyright (c) 2024 KISPE Space Systems Ltd.
- *
- * www.kispe.co.uk/projectlicenses/RA2202001001
- ***********************************************************************************
- * Created on: 10-Apr-2024 10:54:13
- * Implementation of the Class STM32SPI_SerialPeripheralInterface
- * @author: Charlie Gallie
- ***********************************************************************************/
- #include "STM32SPI_SerialPeripheralInterface.h"
- #include "STM32GPIO_GeneralPurposeInputOutput.h"
- #include "STM32DMA_DirectMemoryAccess.h"
- #include "STM32DMAMUX_DirectMemoryAccessMultiplexer.h"
- #include "ISISRD_ISISRadioDownlink.h"
- #include "EH_ExceptionHandler.h"
- // TODO: Remove this include once we're no longer using HAL
- #include "stm32h7xx.h"
- /**
- * This object currently supports transmission on SPI via DMA (RAM->SPI). Once
- * transmission is complete, a function is called to allow the calling code to
- * react to the completed transmission.
- * This code also supports a chip select signal from the SPI controller.
- */
- /* PRIVATE ATTRIBUTES */
- /** TODO: Remove once no longer using HAL
- * Associates an index from 0 to 15, inclusive, to the DMA enumeration used within HAL
- */
- IRQn_Type ateSTM32SPI_DMAIRQs[] = {
- DMA1_Stream0_IRQn, DMA1_Stream1_IRQn, DMA1_Stream2_IRQn, DMA1_Stream3_IRQn,
- DMA1_Stream4_IRQn, DMA1_Stream5_IRQn, DMA1_Stream6_IRQn, DMA1_Stream7_IRQn,
- DMA2_Stream0_IRQn, DMA2_Stream1_IRQn, DMA2_Stream2_IRQn, DMA2_Stream3_IRQn,
- DMA2_Stream4_IRQn, DMA2_Stream5_IRQn, DMA2_Stream6_IRQn, DMA2_Stream7_IRQn
- };
- /**
- * An array representing which DMA channel the SPI port uses
- * SPI 1 = index 0, SPI 2 = index 1...
- * This value is set when the peripheral is being initialised
- */
- static tUINT32 aiSTM32SPI_SPIChannels[6] = { 0U };
- /**
- * STM32H7 device contains all these hardware registers for the complete operation
- * of the SPI protocol capabilities
- */
- typedef struct tsSTM32SPI_REGISTERS
- {
- tUINT32 SPI_CR1;
- tUINT32 SPI_CR2;
- tUINT32 SPI_CFG1;
- tUINT32 SPI_CFG2;
- tUINT32 SPI_IER;
- tUINT32 SPI_SR;
- tUINT32 SPI_IFCR;
- tUINT32 SPI_Reserved_0;
- tUINT32 SPI_TXDR;
- tUINT32 SPI_Reserved_1[3U];
- tUINT32 SPI_RXDR;
- tUINT32 SPI_Reserved_2[3U];
- tUINT32 SPI_CRCPOLY;
- tUINT32 SPI_TXCRC;
- tUINT32 SPI_RXCRC;
- tUINT32 SPI_UDRDR;
- tUINT32 SPI_I2SCFGR;
- } tsSTM32SPI_REGISTERS;
- /**
- * Number of SPI Ports available
- */
- #define STM32SPI_NUM_PORTS (6U)
- /**
- * The number of idle clock cycles which there are between frames.
- */
- #define STM32SPI_IDLE_CLOCKS_BETWEEN_FRAMES (5U)
- /**
- * The number of idle clock cycles between the slave select going active and
- * inactive to allow time for the slave to react to the change.
- */
- #define STM32SPI_IDLE_CLOCKS_BETWEEN_SS_TRANSITION (5U)
- /**
- * An array containing the DMA completion callback for each SPI peripheral. Index
- * 0 refers to SPI 1, index 1 = SPI 2 and so on.
- */
- static tFUNC_PTR atSTM32SPI_DMACallbacks[6U] = { 0U };
- /**
- * The base address of the DMA 1 registers
- */
- static tUINT32 iSTM32SPI_DMA1BaseAddress = STM32DMA_DMA1_REGISTERS_BASE_ADDRESS;
- /**
- * The base address of the DMA 2 registers
- */
- static tUINT32 iSTM32SPI_DMA2BaseAddress = STM32DMA_DMA2_REGISTERS_BASE_ADDRESS;
- /**
- * The base address of the DMA MUX 1 registers
- */
- static tUINT32 iSTM32SPI_DMAMUX1BaseAddress = STM32DMAMUX_DMAMUX1_REGISTERS_BASE_ADDRESS;
- /**
- * The base address of the DMA MUX 2 registers
- */
- static tUINT32 iSTM32SPI_DMAMUX2BaseAddress = STM32DMAMUX_DMAMUX2_REGISTERS_BASE_ADDRESS;
- /**
- * An array containing the error counters for each SPI peripheral. Each index
- * corresponds with the SPI peripheral value, minus 1. That means index 0 = SPI 1,
- * index 1 = SPI 2...
- */
- static tsSTM32SPI_ERROR_COUNTERS atsSTM32SPI_ErrorCounters[6] = { 0U };
- /**
- * These are flags to specify whether an SPI peripheral is "busy"
- * If the value of the flag is 0, then the peripheral is deemed to be available for use
- * If the value is non-zero, then the peripheral is busy. These are stored as U32s so that the operations are atomic
- */
- static tUINT32 aiSTM32SPI_PeripheralBusyFlags[6] = { 0U };
- /* PRIVATE FUNCTIONS */
- #ifndef UNIT_TEST
- static teFUNC_STATUS STM32SPI_ISRHandler(teSTM32SPI_PORTS teSPIPort, tUINT32 iSPINumber);
- #endif /* UNIT_TEST */
- teFUNC_STATUS STM32SPI_DMACallback(__attribute__((unused)) tUINT32 iDmaChannel, __attribute__((unused)) tsSTM32DMA_ISR_FLAGS tsDMAFlags)
- {
- // This goes unused because it's all handled within the interrupt for the SPI peripheral
- return BT_SUCCESS;
- }
- /**
- * This initialises a given SPI peripheral ready for communication.
- * This driver implementation currently only supports transmission, therefore slave mode and other
- * slave specific functionality is an invalid configuration.
- * The teSTM32SPI_CONFIGURATION must be set with the peripheral configuration prior to calling this
- * function.
- * Note: SPI 6 is currently unsupported due to it using BDMA.
- * @throws STM32SPI_BAD_PARAM if the given configuration is invalid
- * @throws STM32SPI_INVALID_CALLBACK if the provided callback is 0
- * @throws STM32SPI_DMA_ERROR if the initialisation of DMA failed
- * @returns SUCCESS unless there is an exception
- * @param ptsConfig: tsSTM32SPI_CONFIGURATION*: the spi operation configuration structure
- * @param fpTransmitCompleteCallback: tFUNC_PTR: This function is called when the DMA transmission has
- * completed.
- * @re-entrant: False
- */
- teFUNC_STATUS STM32SPI_InitialisePeripheral(const tsSTM32SPI_CONFIGURATION* ptsConfig, tFUNC_PTR fpTransmitCompleteCallback)
- {
- // Ensure the callback pointer is not zero
- EH_ASSERT((tUINT32)fpTransmitCompleteCallback != 0U, STM32SPI_INVALID_CALLBACK, "An invalid callback was provided - Callback must be non-zero");
- // Ensure the DMA stream is within the valid range
- EH_ASSERT(ptsConfig->iDMAStream <= 15U, STM32SPI_BAD_PARAM, "The DMA stream must be within the range 0 to 15, inclusive");
- // Ensure the operating mode is set to master
- EH_ASSERT(ptsConfig->teOperatingMode == SPI_MASTER, STM32SPI_BAD_PARAM, "Only Master SPI mode is supported");
- // Get a pointer to the registers for the SPI peripheral
- volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)((tUINT32)ptsConfig->tePortNumber);
- // Set the callbacks and enables the peripheral clock for the specific SPI peripheral
- // TODO: Replace the HAL related things with RCC driver calls once implemented
- IRQn_Type teSPIIRQ = HardFault_IRQn;
- // The request input to the DMA MUX for the specific SPI peripheral
- tUINT32 iMUXRequestInput = 0U;
- // The index of the SPI peripheral
- tUINT32 iPeripheralIndex = 0U;
- switch (ptsConfig->tePortNumber)
- {
- case SPI_PORT1:
- __HAL_RCC_SPI1_CLK_ENABLE();
- iPeripheralIndex = 0U;
- teSPIIRQ = SPI1_IRQn;
- iMUXRequestInput = 38U;
- break;
- case SPI_PORT2:
- __HAL_RCC_SPI2_CLK_ENABLE();
- iPeripheralIndex = 1U;
- teSPIIRQ = SPI2_IRQn;
- iMUXRequestInput = 40U;
- break;
- case SPI_PORT3:
- __HAL_RCC_SPI3_CLK_ENABLE();
- iPeripheralIndex = 2U;
- teSPIIRQ = SPI3_IRQn;
- iMUXRequestInput = 62U;
- break;
- case SPI_PORT4:
- __HAL_RCC_SPI4_CLK_ENABLE();
- iPeripheralIndex = 3U;
- teSPIIRQ = SPI4_IRQn;
- iMUXRequestInput = 84U;
- break;
- case SPI_PORT5:
- __HAL_RCC_SPI5_CLK_ENABLE();
- iPeripheralIndex = 4U;
- teSPIIRQ = SPI5_IRQn;
- iMUXRequestInput = 86U;
- break;
- case SPI_PORT6:
- // SPI 6 is not supported because it requires BDMA
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "SPI 6 is not currently supported.");
- break;
- default:
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown SPI peripheral enumeration value was provided");
- break;
- }
- // Set the DMA callback function for the peripheral
- atSTM32SPI_DMACallbacks[iPeripheralIndex] = fpTransmitCompleteCallback;
- // Set the DMA port for the peripheral
- aiSTM32SPI_SPIChannels[iPeripheralIndex] = ptsConfig->iDMAStream;
- // Enable the SPI IRQ function
- // TODO: Don't use HAL
- HAL_NVIC_SetPriority(teSPIIRQ, 7, 7);
- HAL_NVIC_EnableIRQ(teSPIIRQ);
- // Get the DMA & DMAMUX base addresses and IRQ for the specific DMA stream chosen
- tUINT32 iDMABaseAddress = 0U;
- tUINT32 iDMAMUXBaseAddress = 0U;
- IRQn_Type teDMAIRQ = ateSTM32SPI_DMAIRQs[ptsConfig->iDMAStream];
- switch (ptsConfig->iDMAStream)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- iDMABaseAddress = iSTM32SPI_DMA1BaseAddress;
- iDMAMUXBaseAddress = iSTM32SPI_DMAMUX1BaseAddress;
- break;
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- iDMABaseAddress = iSTM32SPI_DMA2BaseAddress;
- iDMAMUXBaseAddress = iSTM32SPI_DMAMUX1BaseAddress;
- break;
- default:
- // Should be unreachable
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An invalid DMA stream was chosen");
- break;
- }
- teFUNC_STATUS teFuncStatus = BT_FAIL;
- // Configure the DMA channel
- teFuncStatus = STM32DMA_ConfigureChannel(iDMABaseAddress, ptsConfig->iDMAStream, (tUINT32)&(ptsSPIRegisters->SPI_TXDR), 0U, 2, STM32SPI_DMACallback);
- EH_ASSERT(teFuncStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to configure a channel for DMA");
- // Attach DMA MUX to channel
- teFuncStatus = STM32DMAMUX_AttachInputToChannel(iDMAMUXBaseAddress, ptsConfig->iDMAStream, iMUXRequestInput);
- EH_ASSERT(teFuncStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to attach DMAMUX");
- HAL_NVIC_SetPriority(teDMAIRQ, 7, 7);
- HAL_NVIC_EnableIRQ(teDMAIRQ);
- // SPE = 0
- // Disable the peripheral before working with it
- ptsSPIRegisters->SPI_CR1 &= ~(0x1U);
- // MBR = 0
- // Clear the value at MBR
- ptsSPIRegisters->SPI_CFG1 &= ~(0x70000000U);
- // MBR = config baudrate
- // Set the divider of the SPI baudrate
- ptsSPIRegisters->SPI_CFG1 |= (((tUINT32)ptsConfig->teBaudrate) << 28U);
- // CRCEN = 0
- // Disable CRC
- ptsSPIRegisters->SPI_CFG1 &= ~(0x400000U);
- // CRCSIZE = default
- // CRCSIZE must be left at default when CRCEN = 0
- // RXDMAEN = 0
- // Disable Rx DMA stream
- ptsSPIRegisters->SPI_CFG1 &= ~(0x4000U);
- // UDRDET = default
- // UDRDET is only necessary in slave mode
- // UDRCFG = default
- // UDRCFG is only necessary in slave mode
- // FTHLV = 0b0000
- // Set the FIFO threshold level to 1-data
- ptsSPIRegisters->SPI_CFG1 &= ~(0x1E0U);
- // DSIZE = 0b00111
- // Set the number of bits in an SPI frame to 8-bits
- ptsSPIRegisters->SPI_CFG1 |= (0x7U);
- // AFCNTR = 0
- // The SPI peripheral will not have control of the peripheral when SPE=0
- // You may decide for this to be 1 if you plan to change the SPI configuration without disrupting the IO lines
- ptsSPIRegisters->SPI_CFG2 &= ~(0x80000000U);
- // SSOM = 0
- // This will leave the slave select line at the active level between frames instead of temporarily deactivating the line
- // The alternative is the slave select line becomes inactive between frames for the duration of the MIDI register
- ptsSPIRegisters->SPI_CFG2 &= ~(0x40000000U);
- // Set whether the hardware controls SS or not
- if (ptsConfig->bChipSelectInHardware)
- {
- // SSOE = 1
- // Enable SS output
- ptsSPIRegisters->SPI_CFG2 |= (0x20000000U);
- // SSM = 0
- // SS hardware management
- ptsSPIRegisters->SPI_CFG2 &= ~(0x4000000U);
- }
- else
- {
- // SSOE = 0
- // Disable SS output
- ptsSPIRegisters->SPI_CFG2 &= ~(0x20000000U);
- // SSM = 1
- // SS software management
- ptsSPIRegisters->SPI_CFG2 |= (0x4000000U);
- // SSI = 1
- // Set SSI value to 1
- ptsSPIRegisters->SPI_CR1 |= (0x1000U);
- }
- // Set the register for the chip selects active level
- if (ptsConfig->teChipSelectActiveLevel == SPI_CHIP_SELECT_ACTIVE_HIGH)
- {
- // SSIOP = 1
- // SS active level is high
- ptsSPIRegisters->SPI_CFG2 |= (0x10000000U);
- }
- else if (ptsConfig->teChipSelectActiveLevel == SPI_CHIP_SELECT_ACTIVE_LOW)
- {
- // SSIOP = 0
- // SS active level is low
- ptsSPIRegisters->SPI_CFG2 &= ~(0x10000000U);
- }
- else
- {
- // Throw because we don't know the value given
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown chip select active level enumeration value was provided");
- }
- // Set the idle state of the clock line
- if (ptsConfig->teClockPolarity == SPI_CLOCK_IDLE_HIGH)
- {
- // CPOL = 1
- // The idle state of the clock is high
- ptsSPIRegisters->SPI_CFG2 |= (0x2000000U);
- }
- else if (ptsConfig->teClockPolarity == SPI_CLOCK_IDLE_LOW)
- {
- // CPOL = 0
- // The idle state of the clock is low
- ptsSPIRegisters->SPI_CFG2 &= ~(0x2000000U);
- }
- else
- {
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown clock polarity enumeration value was provided");
- }
- // Set which clock transition the data should be captured on
- if (ptsConfig->teClockPhase == SPI_CLOCK_SAMPLE_ON_FIRST_TRANSITION)
- {
- // CPHA = 0
- // The first transition of the clock is the data capture
- ptsSPIRegisters->SPI_CFG2 &= ~(0x1000000U);
- }
- else if (ptsConfig->teClockPhase == SPI_CLOCK_SAMPLE_ON_SECOND_TRANSITION)
- {
- // CPHA = 1
- // The second transition of the clock is the data capture
- ptsSPIRegisters->SPI_CFG2 |= (0x1000000U);
- }
- else
- {
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown clock transition enumeration value was provided");
- }
- // Clear LSBFRST before setting it
- ptsSPIRegisters->SPI_CFG2 &= ~(0x800000U);
- // LSBFRST = teBitOrder
- // Set the bit order for transmission
- ptsSPIRegisters->SPI_CFG2 |= ((tUINT32)ptsConfig->teBitOrder);
- // SP = 0b000
- // Set the serial protocol to Motorola
- ptsSPIRegisters->SPI_CFG2 &= ~(0x380000U);
- // Get the bitmask to write to the register to set the communication mode
- tUINT32 iCommunicationModeBitmask = 0U;
- switch (ptsConfig->teCommunicationMode)
- {
- case SPI_HALF_DUPLEX:
- iCommunicationModeBitmask = 0x3U;
- break;
- case SPI_FULL_DUPLEX:
- iCommunicationModeBitmask = 0x0U;
- break;
- case SPI_SIMPLEX_TRANSMITTER:
- iCommunicationModeBitmask = 0x1U;
- break;
- case SPI_SIMPLEX_RECEIVER:
- iCommunicationModeBitmask = 0x2U;
- break;
- default:
- EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown communication mode was selected");
- break;
- }
- // Clear COMM bits in CFG2
- ptsSPIRegisters->SPI_CFG2 &= ~(0x60000U);
- // COMM = iCommunicationModeBitmask
- // Set the communication mode using the bitmask created above
- ptsSPIRegisters->SPI_CFG2 |= (iCommunicationModeBitmask << 17U);
- // IOSWP = 0
- // Do not swap MISO and MOSI
- ptsSPIRegisters->SPI_CFG2 &= ~(0x8000U);
- // Clear MIDI bits within CFG2
- ptsSPIRegisters->SPI_CFG2 &= ~(0xF0U);
- // MIDI = STM32SPI_IDLE_CLOCKS_BETWEEN_FRAMES
- // Set the number of idle clock cycles between frames
- ptsSPIRegisters->SPI_CFG2 |= (STM32SPI_IDLE_CLOCKS_BETWEEN_FRAMES << 4U);
- // Clear MSSI bits within CFG2 before setting them
- ptsSPIRegisters->SPI_CFG2 &= ~(0xFU);
- // MSSI = STM32SPI_IDLE_CLOCKS_BETWEEN_SS_TRANSITION
- // Set the number of idle clock cycles between first packet and end of last packet
- ptsSPIRegisters->SPI_CFG2 |= (STM32SPI_IDLE_CLOCKS_BETWEEN_SS_TRANSITION << 0U);
- // MASTER = 1
- // Set the SPI peripheral to be a master
- ptsSPIRegisters->SPI_CFG2 |= (0x400000U);
- return BT_SUCCESS;
- STM32SPI_BAD_PARAM:
- STM32SPI_INVALID_CALLBACK:
- STM32SPI_DMA_ERROR:
- return BT_FAIL;
- }
- /**
- * This function deinitialises the provided SPI peripheral.
- * @throws STM32SPI_DEINIT_FAILED if it failed to deinitialise
- * @returns SUCCESS unless there is an exception
- * @param teSPIConfig: tsSTM32SPI_CONFIGURATION*: The configuration which was previously used to
- * enable the SPI peripheral
- * @re-entrant: False
- */
- teFUNC_STATUS STM32SPI_DeinitialisePeripheral(tsSTM32SPI_CONFIGURATION* teSPIConfig)
- {
- // Get a pointer to the SPI registers
- volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)teSPIConfig->tePortNumber;
- // SPE = 0
- // Disable the SPI peripheral
- ptsSPIRegisters->SPI_CR1 &= ~(0x1U);
- teFUNC_STATUS teFuncStatus = BT_FAIL;
- // Deinitialise the DMA channel
- teFuncStatus = STM32DMA_DeconfigureChannel(iSTM32SPI_DMA1BaseAddress, teSPIConfig->iDMAStream);
- EH_ASSERT(teFuncStatus == BT_SUCCESS, STM32SPI_DEINIT_FAILED, "Failed to deconfigure DMA channel");
- return BT_SUCCESS;
- STM32SPI_DEINIT_FAILED:
- return BT_FAIL;
- }
- /**
- * This function performs a DMA transmit using the provided SPI peripheral.
- * @throws STM32SPI_DMA_ERROR if there was an error with the DMA
- * @throws STM32SPI_PARAM_ERROR if an invalid parameter was provided
- * @throws STM32SPI_SPI_BUSY if the peripheral is currently busy
- * @returns SUCCESS unless there is an exception
- * @param teSPIPort: teSTM32SPI_PORTS: The SPI port to transmit on. The SPI port must be previously
- * initialised.
- * @param paData: tUINT8*: pointer to the send buffer
- * @param iNumBytes: tBYTES: number of bytes to read
- * @re-entrant: True
- */
- teFUNC_STATUS STM32SPI_TransmitDMA(teSTM32SPI_PORTS teSPIPort, tUINT8* paData, tBYTES iNumBytes)
- {
- // Get a pointer to the SPI registers
- volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)teSPIPort;
- teFUNC_STATUS teDMAStatus = BT_FAIL;
- // Get the peripheral index associated with a port number
- tUINT32 iPeripheralIndex = 0U;
- switch (teSPIPort)
- {
- case SPI_PORT1:
- iPeripheralIndex = 0U;
- break;
- case SPI_PORT2:
- iPeripheralIndex = 1U;
- break;
- case SPI_PORT3:
- iPeripheralIndex = 2U;
- break;
- case SPI_PORT4:
- iPeripheralIndex = 3U;
- break;
- case SPI_PORT5:
- iPeripheralIndex = 4U;
- break;
- case SPI_PORT6:
- // SPI 6 requires a BDMA driver
- EH_ASSERT(FALSE, STM32SPI_PARAM_ERROR, "SPI 6 is not currently supported");
- break;
- default:
- EH_ASSERT(FALSE, STM32SPI_PARAM_ERROR, "An invalid SPI peripheral was provided");
- break;
- }
- // Check that the peripheral is not busy
- EH_ASSERT(aiSTM32SPI_PeripheralBusyFlags[iPeripheralIndex] == 0, STM32SPI_SPI_BUSY, "Attempted to transmit while the peripheral is busy");
- // Set the busy flag
- // Anything non-zero means the peripheral is busy
- // This is an atomic operation
- aiSTM32SPI_PeripheralBusyFlags[iPeripheralIndex] = 1;
- // Get the DMA stream number
- tUINT32 iDMAStream = aiSTM32SPI_SPIChannels[iPeripheralIndex];
- // Ensure the DMA stream is disabled before transmitting
- teDMAStatus = STM32DMA_DisableStream(STM32DMA_DMA1_REGISTERS_BASE_ADDRESS, iDMAStream);
- EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to disable DMA stream");
- // TXDMAEN = 1
- // Enable DMA streaming - This implementation only uses DMA
- ptsSPIRegisters->SPI_CFG1 |= (0x8000U);
- // TSIZE = 0
- // Set the transfer size to 0
- // We do this because when using DMA, we keep writing to the SPI peripheral until EOT, then we manually set the CSUSP flag to stop
- ptsSPIRegisters->SPI_CR2 &= (0x0U);
- // SPE = 1
- // Enable the SPI peripheral
- ptsSPIRegisters->SPI_CR1 |= (0x1U);
- // Transmit the data via DMA
- teDMAStatus = STM32DMA_Transmit(STM32DMA_DMA1_REGISTERS_BASE_ADDRESS, (tUINT32*)paData, iNumBytes, iDMAStream);
- EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to transmit DMA data");
- // Enable the DMA stream
- // This must be done *after* transmitting via the previous call
- teDMAStatus = STM32DMA_EnableStream(STM32DMA_DMA1_REGISTERS_BASE_ADDRESS, iDMAStream);
- EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to enable DMA steam");
- // MODFIE = 1
- // EOTIE = 1
- // Enable Mode Fault and Transmission complete callbacks
- ptsSPIRegisters->SPI_IER |= (0x208U);
- // CSTART = 1
- // Start transmission
- ptsSPIRegisters->SPI_CR1 |= (0x200U);
- return BT_SUCCESS;
- STM32SPI_DMA_ERROR:
- // SPE = 0
- // Disable the SPI peripheral in the case of any error
- ptsSPIRegisters->SPI_CR1 |= ~(0x1U);
- STM32SPI_PARAM_ERROR:
- STM32SPI_SPI_BUSY:
- return BT_FAIL;
- }
- /**
- * This is called by the SPI 1 peripheral in an event such as transmission complete or an error
- * occurred.
- * @re-entrant: True
- */
- void STM32SPI1_ISR()
- {
- // Call handler for SPI 1
- teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT1, 1);
- EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI1 failed");
- }
- /**
- * This is called by the SPI 2 peripheral in an event such as transmission complete or an error
- * occurred.
- * @re-entrant: True
- */
- void STM32SPI2_ISR()
- {
- // Call handler for SPI 2
- teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT2, 2);
- EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI2 failed");
- }
- /**
- * This is called by the SPI 3 peripheral in an event such as transmission complete or an error
- * occurred.
- * @re-entrant: True
- */
- void STM32SPI3_ISR()
- {
- // Call handler for SPI 3
- teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT3, 3);
- EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI3 failed");
- }
- /**
- * This is called by the SPI 4 peripheral in an event such as transmission complete or an error
- * occurred.
- * @re-entrant: True
- */
- void STM32SPI4_ISR()
- {
- // Call handler for SPI 4
- teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT4, 4);
- EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI4 failed");
- }
- /**
- * This is called by the SPI 5 peripheral in an event such as transmission complete or an error
- * occurred.
- * @re-entrant: True
- */
- void STM32SPI5_ISR()
- {
- // Call handler for SPI 5
- teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT5, 5);
- EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI5 failed");
- }
- /**
- * This is called by the SPI 6 peripheral in an event such as transmission complete or an error
- * occurred.
- * @re-entrant: True
- */
- void STM32SPI6_ISR()
- {
- // Call handler for SPI 6
- teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT6, 6);
- EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI6 failed");
- }
- /**
- * This is called from the STM32SPIx_ISR functions where the port is the specific SPI peripheral.
- * When this function is called, it will call the associated SPI callback registered when the
- * peripheral was initialised.
- * This function will increment the error counter if necessary then clear the error register of the
- * peripheral.
- * @throws STM32SPI_INVALID_CALLBACK if the registered callback is zero
- * @throws STM32SPI_CALLBACK_FAILED if the callback returns FAIL
- * @throws STM32SPI_DMA_ERROR if there was an error with the DMA
- * @returns SUCCESS unless there is an exception
- * @param teSPIPort: teSTM32SPI_PORTS:
- * @re-entrant: True
- */
- #ifndef UNIT_TEST
- static
- #endif /* UNIT_TEST */
- teFUNC_STATUS STM32SPI_ISRHandler(teSTM32SPI_PORTS teSPIPort, tUINT32 iSPINumber)
- {
- volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)teSPIPort;
- // Determine the peripheral index
- // The value of 'iSPINumber' is set only internally to this object, meaning it should be trustworthy
- // Therefore we omit value checking for it to keep this ISR fast
- tUINT32 iPeripheralIndex = iSPINumber - 1;
- // Get the DMA stream associated with the port number
- tUINT32 iDMAStream = aiSTM32SPI_SPIChannels[iPeripheralIndex];
- // MODFIE = 0
- // EOTIE = 0
- // Disable Mode Fault and Transmission complete interrupts
- ptsSPIRegisters->SPI_IER &= ~(0x208U);
- // CSUSP = 1
- // Stop the transfer
- ptsSPIRegisters->SPI_CR1 |= (0x400U);
- // TXDMAEN = 0
- // Disable DMA streaming
- ptsSPIRegisters->SPI_CFG1 &= ~(0x8000U);
- // SPE = 0
- // Disable the SPI peripheral
- ptsSPIRegisters->SPI_CR1 &= ~(0x1U);
- // Disable the DMA stream
- teFUNC_STATUS teDMAStatus = STM32DMA_DisableStream(iSTM32SPI_DMA1BaseAddress, iDMAStream);
- EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "There was an error disabling the DMA stream");
- // Get a pointer to the error counters for the corresponding SPI peripheral
- tsSTM32SPI_ERROR_COUNTERS* ptsErrorCounters = &(atsSTM32SPI_ErrorCounters[iPeripheralIndex]);
- // Increment the error counters for any errors which have occurred
- ptsErrorCounters->iModeFaultCount += ((ptsSPIRegisters->SPI_SR & 0x200) >> 9);
- ptsErrorCounters->iTIFrameErrorCount += ((ptsSPIRegisters->SPI_SR & 0x100) >> 8);
- ptsErrorCounters->iCRCErrorCount += ((ptsSPIRegisters->SPI_SR & 0x80) >> 7);
- ptsErrorCounters->iOverrunCount += ((ptsSPIRegisters->SPI_SR & 0x40) >> 6);
- ptsErrorCounters->iUnderrunCount += ((ptsSPIRegisters->SPI_SR & 0x20) >> 5);
- // Get the function pointer callback
- tFUNC_PTR tCallback = atSTM32SPI_DMACallbacks[iPeripheralIndex];
- EH_ASSERT((tUINT32)tCallback != 0U, STM32SPI_INVALID_CALLBACK, "The callback registered is zero");
- // Call the callback associated with the SPI peripheral
- teFUNC_STATUS teCallbackStatus = tCallback();
- EH_ASSERT(teCallbackStatus == BT_SUCCESS, STM32SPI_CALLBACK_FAILED, "The call to the SPI callback returned fail");
- // Specify that the SPI peripheral is no longer busy
- aiSTM32SPI_PeripheralBusyFlags[iPeripheralIndex] = 0U;
- return BT_SUCCESS;
- STM32SPI_INVALID_CALLBACK:
- STM32SPI_CALLBACK_FAILED:
- STM32SPI_DMA_ERROR:
- return BT_FAIL;
- }
- /**
- * This is a utility function which returns TRUE is the result of the SPI transmission was successful,
- * otherwise FALSE is returned. The user must pass the error counters into this function for the SPI
- * peripheral which was used to transmit data.
- * If the user wishes to not use this function, they can equally ensure the sum of the error counters
- * is zero. If the sum of the error counters is non-zero then an error occurred and must be handled by
- * the user.
- * After checking and handling all errors, the error counters should be reverted to all zero.
- * @param ptsSPIErrors: tsSTM32SPI_ERROR_COUNTERS*:
- * @param pbHasNoErrors: tBOOL*:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_HasNoErrors(const tsSTM32SPI_ERROR_COUNTERS* ptsSPIErrors, tBOOL* pbHasNoErrors)
- {
- // Get the number of total errors
- tUINT32 iSumOfErrors =
- ptsSPIErrors->iCRCErrorCount +
- ptsSPIErrors->iModeFaultCount +
- ptsSPIErrors->iOverrunCount +
- ptsSPIErrors->iTIFrameErrorCount +
- ptsSPIErrors->iUnderrunCount;
- // If the sum of errors is zero, there are no errors
- *pbHasNoErrors = (iSumOfErrors == 0U);
- return BT_SUCCESS;
- }
- /**
- * Initialises the SPI driver. This sets the internal state of the driver to make it ready for use.
- * The base addresses passed in are saved within the driver and used as necessary.
- * @returns SUCCESS unless there is an exception
- * @param iDMA1BaseAddress: tUINT32: The base address of the DMA 1 registers
- * @param iDMA2BaseAddress: tUINT32: The base address of the DMA 2 registers
- * @param iDMAMUX1BaseAddress: tUINT32: The base address of the DMA MUX 1 registers
- * @param iDMAMUX2BaseAddress: tUINT32: The base address of the DMA MUX 2 registers
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_Initialise(tUINT32 iDMA1BaseAddress, tUINT32 iDMA2BaseAddress, tUINT32 iDMAMUX1BaseAddress, tUINT32 iDMAMUX2BaseAddress)
- {
- // Set the addresses of each DMA and DMAMUX register
- iSTM32SPI_DMA1BaseAddress = iDMA1BaseAddress;
- iSTM32SPI_DMA2BaseAddress = iDMA2BaseAddress;
- iSTM32SPI_DMAMUX1BaseAddress = iDMAMUX1BaseAddress;
- iSTM32SPI_DMAMUX2BaseAddress = iDMAMUX2BaseAddress;
- return BT_SUCCESS;
- }
- /**
- * Returns a pointer to the error counters for SPI 1
- * @returns SUCCESS always
- * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_GetErrorCountersForSPI1(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
- {
- *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[0]);
- return BT_SUCCESS;
- }
- /**
- * Returns a pointer to the error counters for SPI 2
- * @returns SUCCESS always
- * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_GetErrorCountersForSPI2(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
- {
- *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[1]);
- return BT_SUCCESS;
- }
- /**
- * Returns a pointer to the error counters for SPI 3
- * @returns SUCCESS always
- * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_GetErrorCountersForSPI3(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
- {
- *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[2]);
- return BT_SUCCESS;
- }
- /**
- * Returns a pointer to the error counters for SPI 4
- * @returns SUCCESS always
- * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_GetErrorCountersForSPI4(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
- {
- *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[3]);
- return BT_SUCCESS;
- }
- /**
- * Returns a pointer to the error counters for SPI 5
- * @returns SUCCESS always
- * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_GetErrorCountersForSPI5(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
- {
- *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[4]);
- return BT_SUCCESS;
- }
- /**
- * Returns a pointer to the error counters for SPI 6
- * @returns SUCCESS always
- * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_GetErrorCountersForSPI6(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
- {
- *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[5]);
- return BT_SUCCESS;
- }
- /**
- * Takes a pointer to error counters and resets them all to zero.
- * @returns SUCCESS always
- * @param ptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS*:
- * @re-entrant:
- */
- teFUNC_STATUS STM32SPI_ClearErrorCounters(tsSTM32SPI_ERROR_COUNTERS* ptsErrorCounters)
- {
- ptsErrorCounters->iCRCErrorCount = 0U;
- ptsErrorCounters->iModeFaultCount = 0U;
- ptsErrorCounters->iOverrunCount = 0U;
- ptsErrorCounters->iTIFrameErrorCount = 0U;
- ptsErrorCounters->iUnderrunCount = 0U;
- return BT_SUCCESS;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement