Advertisement
SolarisFalls

Untitled

Feb 4th, 2025
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 29.85 KB | None | 0 0
  1. /***********************************************************************************
  2.  *  @file STM32SPI_SerialPeripheralInterface.c
  3.  ***********************************************************************************
  4.  *   _  _____ ____  ____  _____
  5.  *  | |/ /_ _/ ___||  _ \| ____|
  6.  *  | ' / | |\___ \| |_) |  _|  
  7.  *  | . \ | | ___) |  __/| |___
  8.  *  |_|\_\___|____/|_|   |_____|
  9.  *
  10.  ***********************************************************************************
  11.  *  Copyright (c) 2024 KISPE Space Systems Ltd.
  12.  *  
  13.  *  www.kispe.co.uk/projectlicenses/RA2202001001
  14.  ***********************************************************************************
  15.  *  Created on: 10-Apr-2024 10:54:13                      
  16.  *  Implementation of the Class STM32SPI_SerialPeripheralInterface      
  17.  *  @author: Charlie Gallie                    
  18.  ***********************************************************************************/
  19.  
  20. #include "STM32SPI_SerialPeripheralInterface.h"
  21. #include "STM32GPIO_GeneralPurposeInputOutput.h"
  22. #include "STM32DMA_DirectMemoryAccess.h"
  23. #include "STM32DMAMUX_DirectMemoryAccessMultiplexer.h"
  24. #include "ISISRD_ISISRadioDownlink.h"
  25. #include "EH_ExceptionHandler.h"
  26.  
  27. // TODO: Remove this include once we're no longer using HAL
  28. #include "stm32h7xx.h"
  29.  
  30. /**
  31.  * This object currently supports transmission on SPI via DMA (RAM->SPI). Once
  32.  * transmission is complete, a function is called to allow the calling code to
  33.  * react to the completed transmission.
  34.  * This code also supports a chip select signal from the SPI controller.
  35.  */
  36.  
  37. /* PRIVATE ATTRIBUTES */
  38.  
  39. /** TODO: Remove once no longer using HAL
  40.  * Associates an index from 0 to 15, inclusive, to the DMA enumeration used within HAL
  41.  */
  42. IRQn_Type ateSTM32SPI_DMAIRQs[] = {
  43.         DMA1_Stream0_IRQn, DMA1_Stream1_IRQn, DMA1_Stream2_IRQn, DMA1_Stream3_IRQn,
  44.         DMA1_Stream4_IRQn, DMA1_Stream5_IRQn, DMA1_Stream6_IRQn, DMA1_Stream7_IRQn,
  45.         DMA2_Stream0_IRQn, DMA2_Stream1_IRQn, DMA2_Stream2_IRQn, DMA2_Stream3_IRQn,
  46.         DMA2_Stream4_IRQn, DMA2_Stream5_IRQn, DMA2_Stream6_IRQn, DMA2_Stream7_IRQn
  47. };
  48.  
  49. /**
  50.  * An array representing which DMA channel the SPI port uses
  51.  * SPI 1 = index 0, SPI 2 = index 1...
  52.  * This value is set when the peripheral is being initialised
  53.  */
  54. static tUINT32 aiSTM32SPI_SPIChannels[6] = { 0U };
  55.  
  56. /**
  57.  * STM32H7 device contains all these hardware registers for the complete operation
  58.  * of the SPI protocol capabilities
  59.  */
  60. typedef struct tsSTM32SPI_REGISTERS
  61. {
  62.     tUINT32 SPI_CR1;
  63.     tUINT32 SPI_CR2;
  64.     tUINT32 SPI_CFG1;
  65.     tUINT32 SPI_CFG2;
  66.     tUINT32 SPI_IER;
  67.     tUINT32 SPI_SR;
  68.     tUINT32 SPI_IFCR;
  69.     tUINT32 SPI_Reserved_0;
  70.     tUINT32 SPI_TXDR;
  71.     tUINT32 SPI_Reserved_1[3U];
  72.     tUINT32 SPI_RXDR;
  73.     tUINT32 SPI_Reserved_2[3U];
  74.     tUINT32 SPI_CRCPOLY;
  75.     tUINT32 SPI_TXCRC;
  76.     tUINT32 SPI_RXCRC;
  77.     tUINT32 SPI_UDRDR;
  78.     tUINT32 SPI_I2SCFGR;
  79. }  tsSTM32SPI_REGISTERS;
  80.  
  81. /**
  82.  * Number of SPI Ports available
  83.  */
  84. #define STM32SPI_NUM_PORTS (6U)
  85. /**
  86.  * The number of idle clock cycles which there are between frames.
  87.  */
  88. #define STM32SPI_IDLE_CLOCKS_BETWEEN_FRAMES (5U)
  89. /**
  90.  * The number of idle clock cycles between the slave select going active and
  91.  * inactive to allow time for the slave to react to the change.
  92.  */
  93. #define STM32SPI_IDLE_CLOCKS_BETWEEN_SS_TRANSITION (5U)
  94. /**
  95.  * An array containing the DMA completion callback for each SPI peripheral. Index
  96.  * 0 refers to SPI 1, index 1 = SPI 2 and so on.
  97.  */
  98. static tFUNC_PTR atSTM32SPI_DMACallbacks[6U] = { 0U };
  99. /**
  100.  * The base address of the DMA 1 registers
  101.  */
  102. static tUINT32 iSTM32SPI_DMA1BaseAddress = STM32DMA_DMA1_REGISTERS_BASE_ADDRESS;
  103. /**
  104.  * The base address of the DMA 2 registers
  105.  */
  106. static tUINT32 iSTM32SPI_DMA2BaseAddress = STM32DMA_DMA2_REGISTERS_BASE_ADDRESS;
  107. /**
  108.  * The base address of the DMA MUX 1 registers
  109.  */
  110. static tUINT32 iSTM32SPI_DMAMUX1BaseAddress = STM32DMAMUX_DMAMUX1_REGISTERS_BASE_ADDRESS;
  111. /**
  112.  * The base address of the DMA MUX 2 registers
  113.  */
  114. static tUINT32 iSTM32SPI_DMAMUX2BaseAddress = STM32DMAMUX_DMAMUX2_REGISTERS_BASE_ADDRESS;
  115. /**
  116.  * An array containing the error counters for each SPI peripheral. Each index
  117.  * corresponds with the SPI peripheral value, minus 1. That means index 0 = SPI 1,
  118.  * index 1 = SPI 2...
  119.  */
  120. static tsSTM32SPI_ERROR_COUNTERS atsSTM32SPI_ErrorCounters[6] = { 0U };
  121.  
  122. /**
  123.  * These are flags to specify whether an SPI peripheral is "busy"
  124.  * If the value of the flag is 0, then the peripheral is deemed to be available for use
  125.  * If the value is non-zero, then the peripheral is busy. These are stored as U32s so that the operations are atomic
  126.  */
  127. static tUINT32 aiSTM32SPI_PeripheralBusyFlags[6] = { 0U };
  128.  
  129. /* PRIVATE FUNCTIONS */
  130.  
  131. #ifndef UNIT_TEST
  132.  
  133. static teFUNC_STATUS STM32SPI_ISRHandler(teSTM32SPI_PORTS teSPIPort, tUINT32 iSPINumber);
  134.  
  135. #endif /* UNIT_TEST */
  136.  
  137. teFUNC_STATUS STM32SPI_DMACallback(__attribute__((unused)) tUINT32 iDmaChannel, __attribute__((unused)) tsSTM32DMA_ISR_FLAGS tsDMAFlags)
  138. {
  139.     // This goes unused because it's all handled within the interrupt for the SPI peripheral
  140.  
  141.     return BT_SUCCESS;
  142. }
  143.  
  144. /**
  145.  * This initialises a given SPI peripheral ready for communication.
  146.  * This driver implementation currently only supports transmission, therefore slave mode and other
  147.  * slave specific functionality is an invalid configuration.
  148.  * The teSTM32SPI_CONFIGURATION must be set with the peripheral configuration prior to calling this
  149.  * function.
  150.  * Note: SPI 6 is currently unsupported due to it using BDMA.
  151.  * @throws STM32SPI_BAD_PARAM if the given configuration is invalid
  152.  * @throws STM32SPI_INVALID_CALLBACK if the provided callback is 0
  153.  * @throws STM32SPI_DMA_ERROR if the initialisation of DMA failed
  154.  * @returns SUCCESS unless there is an exception
  155.  * @param ptsConfig: tsSTM32SPI_CONFIGURATION*: the spi operation configuration structure
  156.  * @param fpTransmitCompleteCallback: tFUNC_PTR: This function is called when the DMA transmission has
  157.  * completed.
  158.  * @re-entrant: False
  159.  */
  160. teFUNC_STATUS STM32SPI_InitialisePeripheral(const tsSTM32SPI_CONFIGURATION* ptsConfig, tFUNC_PTR fpTransmitCompleteCallback)
  161. {
  162.     // Ensure the callback pointer is not zero
  163.     EH_ASSERT((tUINT32)fpTransmitCompleteCallback != 0U, STM32SPI_INVALID_CALLBACK, "An invalid callback was provided - Callback must be non-zero");
  164.  
  165.     // Ensure the DMA stream is within the valid range
  166.     EH_ASSERT(ptsConfig->iDMAStream <= 15U, STM32SPI_BAD_PARAM, "The DMA stream must be within the range 0 to 15, inclusive");
  167.  
  168.     // Ensure the operating mode is set to master
  169.     EH_ASSERT(ptsConfig->teOperatingMode == SPI_MASTER, STM32SPI_BAD_PARAM, "Only Master SPI mode is supported");
  170.  
  171.     // Get a pointer to the registers for the SPI peripheral
  172.     volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)((tUINT32)ptsConfig->tePortNumber);
  173.  
  174.     // Set the callbacks and enables the peripheral clock for the specific SPI peripheral
  175.     // TODO: Replace the HAL related things with RCC driver calls once implemented
  176.     IRQn_Type teSPIIRQ = HardFault_IRQn;
  177.  
  178.     // The request input to the DMA MUX for the specific SPI peripheral
  179.     tUINT32 iMUXRequestInput = 0U;
  180.  
  181.     // The index of the SPI peripheral
  182.     tUINT32 iPeripheralIndex = 0U;
  183.  
  184.     switch (ptsConfig->tePortNumber)
  185.     {
  186.     case SPI_PORT1:
  187.         __HAL_RCC_SPI1_CLK_ENABLE();
  188.         iPeripheralIndex = 0U;
  189.         teSPIIRQ = SPI1_IRQn;
  190.         iMUXRequestInput = 38U;
  191.         break;
  192.  
  193.     case SPI_PORT2:
  194.         __HAL_RCC_SPI2_CLK_ENABLE();
  195.         iPeripheralIndex = 1U;
  196.         teSPIIRQ = SPI2_IRQn;
  197.         iMUXRequestInput = 40U;
  198.         break;
  199.  
  200.     case SPI_PORT3:
  201.         __HAL_RCC_SPI3_CLK_ENABLE();
  202.         iPeripheralIndex = 2U;
  203.         teSPIIRQ = SPI3_IRQn;
  204.         iMUXRequestInput = 62U;
  205.         break;
  206.  
  207.     case SPI_PORT4:
  208.         __HAL_RCC_SPI4_CLK_ENABLE();
  209.         iPeripheralIndex = 3U;
  210.         teSPIIRQ = SPI4_IRQn;
  211.         iMUXRequestInput = 84U;
  212.         break;
  213.  
  214.     case SPI_PORT5:
  215.         __HAL_RCC_SPI5_CLK_ENABLE();
  216.         iPeripheralIndex = 4U;
  217.         teSPIIRQ = SPI5_IRQn;
  218.         iMUXRequestInput = 86U;
  219.         break;
  220.  
  221.     case SPI_PORT6:
  222.         // SPI 6 is not supported because it requires BDMA
  223.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "SPI 6 is not currently supported.");
  224.         break;
  225.  
  226.     default:
  227.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown SPI peripheral enumeration value was provided");
  228.         break;
  229.     }
  230.  
  231.     // Set the DMA callback function for the peripheral
  232.     atSTM32SPI_DMACallbacks[iPeripheralIndex] = fpTransmitCompleteCallback;
  233.  
  234.     // Set the DMA port for the peripheral
  235.     aiSTM32SPI_SPIChannels[iPeripheralIndex] = ptsConfig->iDMAStream;
  236.  
  237.     // Enable the SPI IRQ function
  238.     // TODO: Don't use HAL
  239.     HAL_NVIC_SetPriority(teSPIIRQ, 7, 7);
  240.     HAL_NVIC_EnableIRQ(teSPIIRQ);
  241.  
  242.     // Get the DMA & DMAMUX base addresses and IRQ for the specific DMA stream chosen
  243.     tUINT32 iDMABaseAddress = 0U;
  244.     tUINT32 iDMAMUXBaseAddress = 0U;
  245.  
  246.     IRQn_Type teDMAIRQ = ateSTM32SPI_DMAIRQs[ptsConfig->iDMAStream];
  247.  
  248.     switch (ptsConfig->iDMAStream)
  249.     {
  250.     case 0:
  251.     case 1:
  252.     case 2:
  253.     case 3:
  254.     case 4:
  255.     case 5:
  256.     case 6:
  257.     case 7:
  258.         iDMABaseAddress = iSTM32SPI_DMA1BaseAddress;
  259.         iDMAMUXBaseAddress = iSTM32SPI_DMAMUX1BaseAddress;
  260.         break;
  261.  
  262.     case 8:
  263.     case 9:
  264.     case 10:
  265.     case 11:
  266.     case 12:
  267.     case 13:
  268.     case 14:
  269.     case 15:
  270.         iDMABaseAddress = iSTM32SPI_DMA2BaseAddress;
  271.         iDMAMUXBaseAddress = iSTM32SPI_DMAMUX1BaseAddress;
  272.         break;
  273.  
  274.     default:
  275.         // Should be unreachable
  276.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An invalid DMA stream was chosen");
  277.         break;
  278.     }
  279.  
  280.     teFUNC_STATUS teFuncStatus = BT_FAIL;
  281.  
  282.     // Configure the DMA channel
  283.     teFuncStatus = STM32DMA_ConfigureChannel(iDMABaseAddress, ptsConfig->iDMAStream, (tUINT32)&(ptsSPIRegisters->SPI_TXDR), 0U, 2, STM32SPI_DMACallback);
  284.     EH_ASSERT(teFuncStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to configure a channel for DMA");
  285.  
  286.     // Attach DMA MUX to channel
  287.     teFuncStatus = STM32DMAMUX_AttachInputToChannel(iDMAMUXBaseAddress, ptsConfig->iDMAStream, iMUXRequestInput);
  288.     EH_ASSERT(teFuncStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to attach DMAMUX");
  289.  
  290.     HAL_NVIC_SetPriority(teDMAIRQ, 7, 7);
  291.     HAL_NVIC_EnableIRQ(teDMAIRQ);
  292.  
  293.     // SPE = 0
  294.     // Disable the peripheral before working with it
  295.     ptsSPIRegisters->SPI_CR1 &= ~(0x1U);
  296.  
  297.     // MBR = 0
  298.     // Clear the value at MBR
  299.     ptsSPIRegisters->SPI_CFG1 &= ~(0x70000000U);
  300.  
  301.     // MBR = config baudrate
  302.     // Set the divider of the SPI baudrate
  303.     ptsSPIRegisters->SPI_CFG1 |= (((tUINT32)ptsConfig->teBaudrate) << 28U);
  304.  
  305.     // CRCEN = 0
  306.     // Disable CRC
  307.     ptsSPIRegisters->SPI_CFG1 &= ~(0x400000U);
  308.  
  309.     // CRCSIZE = default
  310.     // CRCSIZE must be left at default when CRCEN = 0
  311.  
  312.     // RXDMAEN = 0
  313.     // Disable Rx DMA stream
  314.     ptsSPIRegisters->SPI_CFG1 &= ~(0x4000U);
  315.  
  316.     // UDRDET = default
  317.     // UDRDET is only necessary in slave mode
  318.  
  319.     // UDRCFG = default
  320.     // UDRCFG is only necessary in slave mode
  321.  
  322.     // FTHLV = 0b0000
  323.     // Set the FIFO threshold level to 1-data
  324.     ptsSPIRegisters->SPI_CFG1 &= ~(0x1E0U);
  325.  
  326.     // DSIZE = 0b00111
  327.     // Set the number of bits in an SPI frame to 8-bits
  328.     ptsSPIRegisters->SPI_CFG1 |= (0x7U);
  329.  
  330.     // AFCNTR = 0
  331.     // The SPI peripheral will not have control of the peripheral when SPE=0
  332.     // You may decide for this to be 1 if you plan to change the SPI configuration without disrupting the IO lines
  333.     ptsSPIRegisters->SPI_CFG2 &= ~(0x80000000U);
  334.  
  335.     // SSOM = 0
  336.     // This will leave the slave select line at the active level between frames instead of temporarily deactivating the line
  337.     // The alternative is the slave select line becomes inactive between frames for the duration of the MIDI register
  338.     ptsSPIRegisters->SPI_CFG2 &= ~(0x40000000U);
  339.  
  340.     // Set whether the hardware controls SS or not
  341.     if (ptsConfig->bChipSelectInHardware)
  342.     {
  343.         // SSOE = 1
  344.         // Enable SS output
  345.         ptsSPIRegisters->SPI_CFG2 |= (0x20000000U);
  346.  
  347.         // SSM = 0
  348.         // SS hardware management
  349.         ptsSPIRegisters->SPI_CFG2 &= ~(0x4000000U);
  350.     }
  351.     else
  352.     {
  353.         // SSOE = 0
  354.         // Disable SS output
  355.         ptsSPIRegisters->SPI_CFG2 &= ~(0x20000000U);
  356.  
  357.         // SSM = 1
  358.         // SS software management
  359.         ptsSPIRegisters->SPI_CFG2 |= (0x4000000U);
  360.  
  361.         // SSI = 1
  362.         // Set SSI value to 1
  363.         ptsSPIRegisters->SPI_CR1 |= (0x1000U);
  364.     }
  365.  
  366.     // Set the register for the chip selects active level
  367.     if (ptsConfig->teChipSelectActiveLevel == SPI_CHIP_SELECT_ACTIVE_HIGH)
  368.     {
  369.         // SSIOP = 1
  370.         // SS active level is high
  371.         ptsSPIRegisters->SPI_CFG2 |= (0x10000000U);
  372.     }
  373.     else if (ptsConfig->teChipSelectActiveLevel == SPI_CHIP_SELECT_ACTIVE_LOW)
  374.     {
  375.         // SSIOP = 0
  376.         // SS active level is low
  377.         ptsSPIRegisters->SPI_CFG2 &= ~(0x10000000U);
  378.     }
  379.     else
  380.     {
  381.         // Throw because we don't know the value given
  382.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown chip select active level enumeration value was provided");
  383.     }
  384.  
  385.     // Set the idle state of the clock line
  386.     if (ptsConfig->teClockPolarity == SPI_CLOCK_IDLE_HIGH)
  387.     {
  388.         // CPOL = 1
  389.         // The idle state of the clock is high
  390.         ptsSPIRegisters->SPI_CFG2 |= (0x2000000U);
  391.     }
  392.     else if (ptsConfig->teClockPolarity == SPI_CLOCK_IDLE_LOW)
  393.     {
  394.         // CPOL = 0
  395.         // The idle state of the clock is low
  396.         ptsSPIRegisters->SPI_CFG2 &= ~(0x2000000U);
  397.     }
  398.     else
  399.     {
  400.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown clock polarity enumeration value was provided");
  401.     }
  402.  
  403.     // Set which clock transition the data should be captured on
  404.     if (ptsConfig->teClockPhase == SPI_CLOCK_SAMPLE_ON_FIRST_TRANSITION)
  405.     {
  406.         // CPHA = 0
  407.         // The first transition of the clock is the data capture
  408.         ptsSPIRegisters->SPI_CFG2 &= ~(0x1000000U);
  409.     }
  410.     else if (ptsConfig->teClockPhase == SPI_CLOCK_SAMPLE_ON_SECOND_TRANSITION)
  411.     {
  412.         // CPHA = 1
  413.         // The second transition of the clock is the data capture
  414.         ptsSPIRegisters->SPI_CFG2 |= (0x1000000U);
  415.     }
  416.     else
  417.     {
  418.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown clock transition enumeration value was provided");
  419.     }
  420.  
  421.     // Clear LSBFRST before setting it
  422.     ptsSPIRegisters->SPI_CFG2 &= ~(0x800000U);
  423.  
  424.     // LSBFRST = teBitOrder
  425.     // Set the bit order for transmission
  426.     ptsSPIRegisters->SPI_CFG2 |= ((tUINT32)ptsConfig->teBitOrder);
  427.  
  428.     // SP = 0b000
  429.     // Set the serial protocol to Motorola
  430.     ptsSPIRegisters->SPI_CFG2 &= ~(0x380000U);
  431.  
  432.     // Get the bitmask to write to the register to set the communication mode
  433.     tUINT32 iCommunicationModeBitmask = 0U;
  434.  
  435.     switch (ptsConfig->teCommunicationMode)
  436.     {
  437.     case SPI_HALF_DUPLEX:
  438.         iCommunicationModeBitmask = 0x3U;
  439.         break;
  440.  
  441.     case SPI_FULL_DUPLEX:
  442.         iCommunicationModeBitmask = 0x0U;
  443.         break;
  444.  
  445.     case SPI_SIMPLEX_TRANSMITTER:
  446.         iCommunicationModeBitmask = 0x1U;
  447.         break;
  448.  
  449.     case SPI_SIMPLEX_RECEIVER:
  450.         iCommunicationModeBitmask = 0x2U;
  451.         break;
  452.  
  453.     default:
  454.         EH_ASSERT(FALSE, STM32SPI_BAD_PARAM, "An unknown communication mode was selected");
  455.         break;
  456.     }
  457.  
  458.     // Clear COMM bits in CFG2
  459.     ptsSPIRegisters->SPI_CFG2 &= ~(0x60000U);
  460.  
  461.     // COMM = iCommunicationModeBitmask
  462.     // Set the communication mode using the bitmask created above
  463.     ptsSPIRegisters->SPI_CFG2 |= (iCommunicationModeBitmask << 17U);
  464.  
  465.     // IOSWP = 0
  466.     // Do not swap MISO and MOSI
  467.     ptsSPIRegisters->SPI_CFG2 &= ~(0x8000U);
  468.  
  469.     // Clear MIDI bits within CFG2
  470.     ptsSPIRegisters->SPI_CFG2 &= ~(0xF0U);
  471.  
  472.     // MIDI = STM32SPI_IDLE_CLOCKS_BETWEEN_FRAMES
  473.     // Set the number of idle clock cycles between frames
  474.     ptsSPIRegisters->SPI_CFG2 |= (STM32SPI_IDLE_CLOCKS_BETWEEN_FRAMES << 4U);
  475.  
  476.     // Clear MSSI bits within CFG2 before setting them
  477.     ptsSPIRegisters->SPI_CFG2 &= ~(0xFU);
  478.  
  479.     // MSSI = STM32SPI_IDLE_CLOCKS_BETWEEN_SS_TRANSITION
  480.     // Set the number of idle clock cycles between first packet and end of last packet
  481.     ptsSPIRegisters->SPI_CFG2 |= (STM32SPI_IDLE_CLOCKS_BETWEEN_SS_TRANSITION << 0U);
  482.  
  483.     // MASTER = 1
  484.     // Set the SPI peripheral to be a master
  485.     ptsSPIRegisters->SPI_CFG2 |= (0x400000U);
  486.  
  487.     return BT_SUCCESS;
  488.  
  489. STM32SPI_BAD_PARAM:
  490. STM32SPI_INVALID_CALLBACK:
  491. STM32SPI_DMA_ERROR:
  492.     return BT_FAIL;
  493. }
  494.  
  495. /**
  496.  * This function deinitialises the provided SPI peripheral.
  497.  * @throws STM32SPI_DEINIT_FAILED if it failed to deinitialise
  498.  * @returns SUCCESS unless there is an exception
  499.  * @param teSPIConfig: tsSTM32SPI_CONFIGURATION*: The configuration which was previously used to
  500.  * enable the SPI peripheral
  501.  * @re-entrant: False
  502.  */
  503. teFUNC_STATUS STM32SPI_DeinitialisePeripheral(tsSTM32SPI_CONFIGURATION* teSPIConfig)
  504. {
  505.     // Get a pointer to the SPI registers
  506.     volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)teSPIConfig->tePortNumber;
  507.  
  508.     // SPE = 0
  509.     // Disable the SPI peripheral
  510.     ptsSPIRegisters->SPI_CR1 &= ~(0x1U);
  511.  
  512.     teFUNC_STATUS teFuncStatus = BT_FAIL;
  513.  
  514.     // Deinitialise the DMA channel
  515.     teFuncStatus = STM32DMA_DeconfigureChannel(iSTM32SPI_DMA1BaseAddress, teSPIConfig->iDMAStream);
  516.     EH_ASSERT(teFuncStatus == BT_SUCCESS, STM32SPI_DEINIT_FAILED, "Failed to deconfigure DMA channel");
  517.  
  518.     return BT_SUCCESS;
  519.  
  520. STM32SPI_DEINIT_FAILED:
  521.     return BT_FAIL;
  522. }
  523.  
  524. /**
  525.  * This function performs a DMA transmit using the provided SPI peripheral.
  526.  * @throws STM32SPI_DMA_ERROR if there was an error with the DMA
  527.  * @throws STM32SPI_PARAM_ERROR if an invalid parameter was provided
  528.  * @throws STM32SPI_SPI_BUSY if the peripheral is currently busy
  529.  * @returns SUCCESS unless there is an exception
  530.  * @param teSPIPort: teSTM32SPI_PORTS: The SPI port to transmit on. The SPI port must be previously
  531.  * initialised.
  532.  * @param paData: tUINT8*: pointer to the send buffer
  533.  * @param iNumBytes: tBYTES: number of bytes to read
  534.  * @re-entrant: True
  535.  */
  536.  
  537. teFUNC_STATUS STM32SPI_TransmitDMA(teSTM32SPI_PORTS teSPIPort, tUINT8* paData, tBYTES iNumBytes)
  538. {
  539.     // Get a pointer to the SPI registers
  540.     volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)teSPIPort;
  541.  
  542.     teFUNC_STATUS teDMAStatus = BT_FAIL;
  543.  
  544.     // Get the peripheral index associated with a port number
  545.     tUINT32 iPeripheralIndex = 0U;
  546.  
  547.     switch (teSPIPort)
  548.     {
  549.     case SPI_PORT1:
  550.         iPeripheralIndex = 0U;
  551.         break;
  552.  
  553.     case SPI_PORT2:
  554.         iPeripheralIndex = 1U;
  555.         break;
  556.  
  557.     case SPI_PORT3:
  558.         iPeripheralIndex = 2U;
  559.         break;
  560.  
  561.     case SPI_PORT4:
  562.         iPeripheralIndex = 3U;
  563.         break;
  564.  
  565.     case SPI_PORT5:
  566.         iPeripheralIndex = 4U;
  567.         break;
  568.  
  569.     case SPI_PORT6:
  570.         // SPI 6 requires a BDMA driver
  571.         EH_ASSERT(FALSE, STM32SPI_PARAM_ERROR, "SPI 6 is not currently supported");
  572.         break;
  573.  
  574.     default:
  575.         EH_ASSERT(FALSE, STM32SPI_PARAM_ERROR, "An invalid SPI peripheral was provided");
  576.         break;
  577.     }
  578.  
  579.     // Check that the peripheral is not busy
  580.     EH_ASSERT(aiSTM32SPI_PeripheralBusyFlags[iPeripheralIndex] == 0, STM32SPI_SPI_BUSY, "Attempted to transmit while the peripheral is busy");
  581.  
  582.     // Set the busy flag
  583.     // Anything non-zero means the peripheral is busy
  584.     // This is an atomic operation
  585.     aiSTM32SPI_PeripheralBusyFlags[iPeripheralIndex] = 1;
  586.  
  587.     // Get the DMA stream number
  588.     tUINT32 iDMAStream = aiSTM32SPI_SPIChannels[iPeripheralIndex];
  589.  
  590.     // Ensure the DMA stream is disabled before transmitting
  591.     teDMAStatus = STM32DMA_DisableStream(STM32DMA_DMA1_REGISTERS_BASE_ADDRESS, iDMAStream);
  592.     EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to disable DMA stream");
  593.  
  594.     // TXDMAEN = 1
  595.     // Enable DMA streaming - This implementation only uses DMA
  596.     ptsSPIRegisters->SPI_CFG1 |= (0x8000U);
  597.  
  598.     // TSIZE = 0
  599.     // Set the transfer size to 0
  600.     // 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
  601.     ptsSPIRegisters->SPI_CR2 &= (0x0U);
  602.  
  603.     // SPE = 1
  604.     // Enable the SPI peripheral
  605.     ptsSPIRegisters->SPI_CR1 |= (0x1U);
  606.  
  607.     // Transmit the data via DMA
  608.     teDMAStatus = STM32DMA_Transmit(STM32DMA_DMA1_REGISTERS_BASE_ADDRESS, (tUINT32*)paData, iNumBytes, iDMAStream);
  609.     EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to transmit DMA data");
  610.  
  611.     // Enable the DMA stream
  612.     // This must be done *after* transmitting via the previous call
  613.     teDMAStatus = STM32DMA_EnableStream(STM32DMA_DMA1_REGISTERS_BASE_ADDRESS, iDMAStream);
  614.     EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "Failed to enable DMA steam");
  615.  
  616.     // MODFIE = 1
  617.     // EOTIE = 1
  618.     // Enable Mode Fault and Transmission complete callbacks
  619.     ptsSPIRegisters->SPI_IER |= (0x208U);
  620.  
  621.     // CSTART = 1
  622.     // Start transmission
  623.     ptsSPIRegisters->SPI_CR1 |= (0x200U);
  624.  
  625.     return BT_SUCCESS;
  626.  
  627. STM32SPI_DMA_ERROR:
  628.     // SPE = 0
  629.     // Disable the SPI peripheral in the case of any error
  630.     ptsSPIRegisters->SPI_CR1 |= ~(0x1U);
  631.  
  632. STM32SPI_PARAM_ERROR:
  633. STM32SPI_SPI_BUSY:
  634.     return BT_FAIL;
  635. }
  636.  
  637. /**
  638.  * This is called by the SPI 1 peripheral in an event such as transmission complete or an error
  639.  * occurred.
  640.  
  641.  * @re-entrant: True
  642.  */
  643. void STM32SPI1_ISR()
  644. {
  645.     // Call handler for SPI 1
  646.     teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT1, 1);
  647.     EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI1 failed");
  648. }
  649.  
  650. /**
  651.  * This is called by the SPI 2 peripheral in an event such as transmission complete or an error
  652.  * occurred.
  653.  
  654.  * @re-entrant: True
  655.  */
  656. void STM32SPI2_ISR()
  657. {
  658.     // Call handler for SPI 2
  659.     teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT2, 2);
  660.     EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI2 failed");
  661. }
  662.  
  663. /**
  664.  * This is called by the SPI 3 peripheral in an event such as transmission complete or an error
  665.  * occurred.
  666.  
  667.  * @re-entrant: True
  668.  */
  669. void STM32SPI3_ISR()
  670. {
  671.     // Call handler for SPI 3
  672.     teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT3, 3);
  673.     EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI3 failed");
  674. }
  675.  
  676. /**
  677.  * This is called by the SPI 4 peripheral in an event such as transmission complete or an error
  678.  * occurred.
  679.  
  680.  * @re-entrant: True
  681.  */
  682. void STM32SPI4_ISR()
  683. {
  684.     // Call handler for SPI 4
  685.     teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT4, 4);
  686.     EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI4 failed");
  687. }
  688.  
  689. /**
  690.  * This is called by the SPI 5 peripheral in an event such as transmission complete or an error
  691.  * occurred.
  692.  
  693.  * @re-entrant: True
  694.  */
  695. void STM32SPI5_ISR()
  696. {
  697.     // Call handler for SPI 5
  698.     teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT5, 5);
  699.     EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI5 failed");
  700. }
  701.  
  702. /**
  703.  * This is called by the SPI 6 peripheral in an event such as transmission complete or an error
  704.  * occurred.
  705.  
  706.  * @re-entrant: True
  707.  */
  708. void STM32SPI6_ISR()
  709. {
  710.     // Call handler for SPI 6
  711.     teFUNC_STATUS teFuncStatus = STM32SPI_ISRHandler(SPI_PORT6, 6);
  712.     EH_THROW(teFuncStatus == BT_SUCCESS, "Call to ISR handler for SPI6 failed");
  713. }
  714.  
  715. /**
  716.  * This is called from the STM32SPIx_ISR functions where the port is the specific SPI peripheral.
  717.  * When this function is called, it will call the associated SPI callback registered when the
  718.  * peripheral was initialised.
  719.  * This function will increment the error counter if necessary then clear the error register of the
  720.  * peripheral.
  721.  * @throws STM32SPI_INVALID_CALLBACK if the registered callback is zero
  722.  * @throws STM32SPI_CALLBACK_FAILED if the callback returns FAIL
  723.  * @throws STM32SPI_DMA_ERROR if there was an error with the DMA
  724.  * @returns SUCCESS unless there is an exception
  725.  * @param teSPIPort: teSTM32SPI_PORTS:
  726.  * @re-entrant: True
  727.  */
  728. #ifndef UNIT_TEST
  729. static
  730. #endif /* UNIT_TEST */
  731.  teFUNC_STATUS STM32SPI_ISRHandler(teSTM32SPI_PORTS teSPIPort, tUINT32 iSPINumber)
  732. {
  733.     volatile tsSTM32SPI_REGISTERS* ptsSPIRegisters = (volatile tsSTM32SPI_REGISTERS*)teSPIPort;
  734.  
  735.     // Determine the peripheral index
  736.     // The value of 'iSPINumber' is set only internally to this object, meaning it should be trustworthy
  737.     // Therefore we omit value checking for it to keep this ISR fast
  738.     tUINT32 iPeripheralIndex = iSPINumber - 1;
  739.  
  740.     // Get the DMA stream associated with the port number
  741.     tUINT32 iDMAStream = aiSTM32SPI_SPIChannels[iPeripheralIndex];
  742.  
  743.     // MODFIE = 0
  744.     // EOTIE = 0
  745.     // Disable Mode Fault and Transmission complete interrupts
  746.     ptsSPIRegisters->SPI_IER &= ~(0x208U);
  747.  
  748.     // CSUSP = 1
  749.     // Stop the transfer
  750.     ptsSPIRegisters->SPI_CR1 |= (0x400U);
  751.  
  752.     // TXDMAEN = 0
  753.     // Disable DMA streaming
  754.     ptsSPIRegisters->SPI_CFG1 &= ~(0x8000U);
  755.  
  756.     // SPE = 0
  757.     // Disable the SPI peripheral
  758.     ptsSPIRegisters->SPI_CR1 &= ~(0x1U);
  759.  
  760.     // Disable the DMA stream
  761.     teFUNC_STATUS teDMAStatus = STM32DMA_DisableStream(iSTM32SPI_DMA1BaseAddress, iDMAStream);
  762.     EH_ASSERT(teDMAStatus == BT_SUCCESS, STM32SPI_DMA_ERROR, "There was an error disabling the DMA stream");
  763.  
  764.     // Get a pointer to the error counters for the corresponding SPI peripheral
  765.     tsSTM32SPI_ERROR_COUNTERS* ptsErrorCounters = &(atsSTM32SPI_ErrorCounters[iPeripheralIndex]);
  766.  
  767.     // Increment the error counters for any errors which have occurred
  768.     ptsErrorCounters->iModeFaultCount += ((ptsSPIRegisters->SPI_SR & 0x200) >> 9);
  769.     ptsErrorCounters->iTIFrameErrorCount += ((ptsSPIRegisters->SPI_SR & 0x100) >> 8);
  770.     ptsErrorCounters->iCRCErrorCount += ((ptsSPIRegisters->SPI_SR & 0x80) >> 7);
  771.     ptsErrorCounters->iOverrunCount += ((ptsSPIRegisters->SPI_SR & 0x40) >> 6);
  772.     ptsErrorCounters->iUnderrunCount += ((ptsSPIRegisters->SPI_SR & 0x20) >> 5);
  773.  
  774.     // Get the function pointer callback
  775.     tFUNC_PTR tCallback = atSTM32SPI_DMACallbacks[iPeripheralIndex];
  776.     EH_ASSERT((tUINT32)tCallback != 0U, STM32SPI_INVALID_CALLBACK, "The callback registered is zero");
  777.  
  778.     // Call the callback associated with the SPI peripheral
  779.     teFUNC_STATUS teCallbackStatus = tCallback();
  780.     EH_ASSERT(teCallbackStatus == BT_SUCCESS, STM32SPI_CALLBACK_FAILED, "The call to the SPI callback returned fail");
  781.  
  782.     // Specify that the SPI peripheral is no longer busy
  783.     aiSTM32SPI_PeripheralBusyFlags[iPeripheralIndex] = 0U;
  784.  
  785.     return BT_SUCCESS;
  786.  
  787. STM32SPI_INVALID_CALLBACK:
  788. STM32SPI_CALLBACK_FAILED:
  789. STM32SPI_DMA_ERROR:
  790.     return BT_FAIL;
  791. }
  792.  
  793. /**
  794.  * This is a utility function which returns TRUE is the result of the SPI transmission was successful,
  795.  * otherwise FALSE is returned. The user must pass the error counters into this function for the SPI
  796.  * peripheral which was used to transmit data.
  797.  * If the user wishes to not use this function, they can equally ensure the sum of the error counters
  798.  * is zero. If the sum of the error counters is non-zero then an error occurred and must be handled by
  799.  * the user.
  800.  * After checking and handling all errors, the error counters should be reverted to all zero.
  801.  * @param ptsSPIErrors: tsSTM32SPI_ERROR_COUNTERS*:
  802.  * @param pbHasNoErrors: tBOOL*:
  803.  * @re-entrant:
  804.  */
  805. teFUNC_STATUS STM32SPI_HasNoErrors(const tsSTM32SPI_ERROR_COUNTERS* ptsSPIErrors, tBOOL* pbHasNoErrors)
  806. {
  807.     // Get the number of total errors
  808.     tUINT32 iSumOfErrors =
  809.             ptsSPIErrors->iCRCErrorCount +
  810.             ptsSPIErrors->iModeFaultCount +
  811.             ptsSPIErrors->iOverrunCount +
  812.             ptsSPIErrors->iTIFrameErrorCount +
  813.             ptsSPIErrors->iUnderrunCount;
  814.  
  815.     // If the sum of errors is zero, there are no errors
  816.     *pbHasNoErrors = (iSumOfErrors == 0U);
  817.  
  818.     return BT_SUCCESS;
  819. }
  820.  
  821. /**
  822.  * Initialises the SPI driver. This sets the internal state of the driver to make it ready for use.
  823.  * The base addresses passed in are saved within the driver and used as necessary.
  824.  * @returns SUCCESS unless there is an exception
  825.  * @param iDMA1BaseAddress: tUINT32: The base address of the DMA 1 registers
  826.  * @param iDMA2BaseAddress: tUINT32: The base address of the DMA 2 registers
  827.  * @param iDMAMUX1BaseAddress: tUINT32: The base address of the DMA MUX 1 registers
  828.  * @param iDMAMUX2BaseAddress: tUINT32: The base address of the DMA MUX 2 registers
  829.  * @re-entrant:
  830.  */
  831. teFUNC_STATUS STM32SPI_Initialise(tUINT32 iDMA1BaseAddress, tUINT32 iDMA2BaseAddress, tUINT32 iDMAMUX1BaseAddress, tUINT32 iDMAMUX2BaseAddress)
  832. {
  833.     // Set the addresses of each DMA and DMAMUX register
  834.     iSTM32SPI_DMA1BaseAddress = iDMA1BaseAddress;
  835.     iSTM32SPI_DMA2BaseAddress = iDMA2BaseAddress;
  836.     iSTM32SPI_DMAMUX1BaseAddress = iDMAMUX1BaseAddress;
  837.     iSTM32SPI_DMAMUX2BaseAddress = iDMAMUX2BaseAddress;
  838.  
  839.     return BT_SUCCESS;
  840. }
  841. /**
  842.  * Returns a pointer to the error counters for SPI 1
  843.  * @returns SUCCESS always
  844.  * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
  845.  * @re-entrant:
  846.  */
  847. teFUNC_STATUS STM32SPI_GetErrorCountersForSPI1(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
  848. {
  849.     *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[0]);
  850.     return BT_SUCCESS;
  851. }
  852.  
  853. /**
  854.  * Returns a pointer to the error counters for SPI 2
  855.  * @returns SUCCESS always
  856.  * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
  857.  * @re-entrant:
  858.  */
  859. teFUNC_STATUS STM32SPI_GetErrorCountersForSPI2(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
  860. {
  861.     *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[1]);
  862.     return BT_SUCCESS;
  863. }
  864.  
  865. /**
  866.  * Returns a pointer to the error counters for SPI 3
  867.  * @returns SUCCESS always
  868.  * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
  869.  * @re-entrant:
  870.  */
  871. teFUNC_STATUS STM32SPI_GetErrorCountersForSPI3(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
  872. {
  873.     *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[2]);
  874.     return BT_SUCCESS;
  875. }
  876.  
  877. /**
  878.  * Returns a pointer to the error counters for SPI 4
  879.  * @returns SUCCESS always
  880.  * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
  881.  * @re-entrant:
  882.  */
  883. teFUNC_STATUS STM32SPI_GetErrorCountersForSPI4(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
  884. {
  885.     *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[3]);
  886.     return BT_SUCCESS;
  887. }
  888.  
  889. /**
  890.  * Returns a pointer to the error counters for SPI 5
  891.  * @returns SUCCESS always
  892.  * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
  893.  * @re-entrant:
  894.  */
  895. teFUNC_STATUS STM32SPI_GetErrorCountersForSPI5(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
  896. {
  897.     *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[4]);
  898.     return BT_SUCCESS;
  899. }
  900.  
  901. /**
  902.  * Returns a pointer to the error counters for SPI 6
  903.  * @returns SUCCESS always
  904.  * @param pptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS**:
  905.  * @re-entrant:
  906.  */
  907. teFUNC_STATUS STM32SPI_GetErrorCountersForSPI6(tsSTM32SPI_ERROR_COUNTERS** pptsErrorCounters)
  908. {
  909.     *pptsErrorCounters = &(atsSTM32SPI_ErrorCounters[5]);
  910.     return BT_SUCCESS;
  911. }
  912.  
  913. /**
  914.  * Takes a pointer to error counters and resets them all to zero.
  915.  * @returns SUCCESS always
  916.  * @param ptsErrorCounters: tsSTM32SPI_ERROR_COUNTERS*:
  917.  * @re-entrant:
  918.  */
  919. teFUNC_STATUS STM32SPI_ClearErrorCounters(tsSTM32SPI_ERROR_COUNTERS* ptsErrorCounters)
  920. {
  921.     ptsErrorCounters->iCRCErrorCount = 0U;
  922.     ptsErrorCounters->iModeFaultCount = 0U;
  923.     ptsErrorCounters->iOverrunCount = 0U;
  924.     ptsErrorCounters->iTIFrameErrorCount = 0U;
  925.     ptsErrorCounters->iUnderrunCount = 0U;
  926.  
  927.     return BT_SUCCESS;
  928. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement