In this program, a message is received using interrupts with the HAL_SPI_Receive_IT() function. Interrupts are activated when the message has been completely received and call the callback function HAL_SPI_RxCpltCallback() to attend to the corresponding ISR.

#include "bsp.h"

SPI_HandleTypeDef SpiHandle;        /*Structure to handle SPI*/
GPIO_InitTypeDef GPIO_InitStruct;   /*Structure to init GPIOs*/

uint8_t RxBuffer[28];            /*Buffer to store data received through SPI*/
uint8_t TxBuffer[4];             /*Buffer to store data to send through SPI*/
uint8_t messageReceived = 0;     /*Flag that indicates a message has been received completely*/

int main(void)
{
    HAL_Init();           /*Library Initialization*/

    __GPIOD_CLK_ENABLE(); /*Enable port D clock*/

    GPIO_InitStruct.Pin   = GPIO_PIN_9;                    /*CS pin to start/finish SPI transmition */
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;           /*Output push-pull Type                  */
    GPIO_InitStruct.Pull  = GPIO_NOPULL;                   /*Pin without pull-up or pull-down       */
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;           /*Pin in low speed                       */
    /*Initialize pins with the previous parameters*/
    HAL_GPIO_Init( GPIOD, &GPIO_InitStruct );

    /*Configuration of SPI in master mode, Full-Duplex communication, Clock polarity in Low,
    clock phase in rising edge and CLK frecc of 4 MHz*/
    SpiHandle.Instance            = SPI1;                        /*SPI instance according MOSI,MISO,CLK and CS lines*/
    SpiHandle.Init.Mode           = SPI_MODE_MASTER;             /*Master mode selected to transmmit data from master*/
    SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;  /*Prescaler selected according slave work freccuency*/
    SpiHandle.Init.Direction      = SPI_DIRECTION_2LINES;        /*Number of lines of transmition (Full-Duplex)*/
    SpiHandle.Init.CLKPhase       = SPI_PHASE_1EDGE;             /*CLK Phase selected in rising edge */
    SpiHandle.Init.CLKPolarity    = SPI_POLARITY_LOW;            /*CLK polarity selected in rising edge, Idle state in 0*/
    SpiHandle.Init.DataSize       = SPI_DATASIZE_8BIT;           /*Zise of data to be transmitted*/
    SpiHandle.Init.FirstBit       = SPI_FIRSTBIT_MSB;            /*Selected the MSB as first bit to be transmitted*/
    SpiHandle.Init.NSS            = SPI_NSS_SOFT;                /*NSS-CS configured by software*/
    SpiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; /*Specifies if the CRC calculation is enabled or not*/
    SpiHandle.Init.TIMode         = SPI_TIMODE_DISABLED;         /*TI protocol master mode disabled*/

    /*Ensures slave is disabled pin D9 high*/
    HAL_GPIO_WritePin( GPIOD, GPIO_PIN_9, SET );
    /*Apply configuration to SPI1*/
    HAL_SPI_Init( &SpiHandle );
    
    /*To be able to read data stored in the EEPROM memory, make sure you have sent the data previously*/
    /*There is a program of sending data in a previous example */
    
    /*Enable reading instruction by sending 0x03 to the eeprom memory, SPI_Receive_IT() function 
    needs as argument the structure to handle SPI, amount of data to receive and buffer where data is 
    stored, when the transmission ends we will use the callback to disable Chip Select */
    HAL_GPIO_WritePin( GPIOD, GPIO_PIN_9, RESET );
    TxBuffer[0] = 0x03; 
    TxBuffer[1] = 0x00; 
    TxBuffer[2] = 0x00;
    HAL_SPI_Transmit( &SpiHandle, TxBuffer, 3 );     /* Send reading instruction and address to read*/
    HAL_SPI_Receive_IT( &SpiHandle, RxBuffer, 28 );  /* Function to receive data read */
     
    while(1)
    {
        if( messageReceived == 1u )
        {
            /* At this point data was received completely */ 
        }
    }
}

/*This callback function is called by the HAL_SPI_IRQHandler every time a message has been received completely*/
void HAL_SPI_RxCpltCallback( SPI_HandleTypeDef *hspi )
{
    /* Disable transmission pulling high Chip Select*/
    HAL_GPIO_WritePin( GPIOD, GPIO_PIN_9, SET );  
   
    /*We can enable a flag to know when the data were received completely */
    messageReceived = 1u;
}

msps.c

/*This function will be called inside the HAL_SPI_Init function*/
void HAL_SPI_MspInit( SPI_HandleTypeDef *hspi )
{
    /* pins D8, D6 and D5 in alternate function spi1 for CLK, MOSI and MISO*/
    GPIO_InitTypeDef GPIO_InitStruct;
    __GPIOD_CLK_ENABLE();
    __SPI1_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_6 | GPIO_PIN_5; /*Function of pin: CLK , MOSI , MISO */ 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                     /*Alternate Function mode for pins*/
    GPIO_InitStruct.Pull = GPIO_PULLUP;                         /*Pull-Up activation for selected pins*/
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;               /*High speed selected for pins*/
    GPIO_InitStruct.Alternate = GPIO_AF1_SPI1;                  /*Alternate function for selected pins*/
    HAL_GPIO_Init( GPIOD, &GPIO_InitStruct );

    /*Enable vector interrupt to attend SPI IRQs */
    HAL_NVIC_SetPriority( SPI1_IRQn, 2, 0 );
    HAL_NVIC_EnableIRQ( SPI1_IRQn );
}

ints.c

/*reference to SPI control structure handler*/
extern SPI_HandleTypeDef SpiHandle;        

/*Declare interrupt service rutine as it is declare in startup_stm32g0b1xx.s file,
Program will jump here every time a single byte is transmitted*/
void SPI1_IRQHandler( void )
{
    /*Process the SPI interrupt and call the corresponding callback functions*/
    HAL_SPI_IRQHandler( &SpiHandle );
}