Arduino не регистрирует передачу SPI от STM32F407 - PullRequest
0 голосов
/ 21 января 2020

РЕДАКТИРОВАТЬ / РЕШЕНО: В итоге проблема заключалась в моих заявлениях Serial.print в коде Arduino, которые замедлялись до фактического выполнения кода связи. По сути, Arduino получал байты после того, как плата STM закончила отправлять их, поэтому регистрировала только 'd' / 100.

В настоящее время я записался на онлайн-курс и столкнулся с проблемой одного из упражнения. Упражнение кажется довольно простым, но я не могу заставить все работать. Упражнение состоит в том, чтобы отправить строку с главного MCU STM32F407 на плату Arduino Uno, которая затем должна напечатать сообщение и его длину.

Кажется, плата Arduino зарегистрировала SS, но ничего не печатала, не печатала неполное / неправильное сообщение (например, «hello vorl») или печатала gibberi sh. После некоторой обнаруженной отладки я неправильно установил регистры CPOL и CPHA, однако теперь он не печатает ничего похожего на «Hello world». После дополнительной отладки кажется, что Arduino застревает в своей функции SPI_SlaveReceive. Код SPI для STM предназначен для первой отправки длины сообщения, а затем для отправки фактического сообщения. Из того, что я могу сказать, Arduino, похоже, не регистрирует правильную длину для первой передачи, что приводит к путанице в l oop для вызовов SPI_SlaveReceive, которые должны захватывать фактическое сообщение.

Я новичок во встроенном и пытался изучить его в основном самостоятельно. Я чувствую, что, возможно, есть что-то, что мне не хватает, но я не знаю. Мне также кажется, что код плохо спроектирован, но я стараюсь придерживаться курса (код STM «написан» в соответствии с инструкциями, и код arduino был только что предоставлен). Я опубликую код ниже, но даже оценил бы идеи о том, что может пойти не так / как я могу отлаживать. Ниже всего я также опубликую некоторые сообщения об ошибках.

Это также моя первая публикация stackoverflow, поэтому, пожалуйста, дайте мне знать, если есть что-то, что мне нужно добавить (или удалить). Спасибо!

Обновление: я думаю, что это может быть что-то с тактовой частотой. Я попытался снова выполнить код в отладчике и обнаружил, что смог успешно отправить данные, если я установил точку останова в коде, который изменяет регистр данных SPI. Похоже, что SPI может отправлять данные быстрее, чем Arduino может обработать. Если я перешагиваю код, который отправляет длину сообщения «Hello world», то я вижу, что Arduino успешно получил 11. Если я продолжу перебирать pSPIx-> DR = * pTxBuffer; Инструкция второго вызова SPI_SendData, я могу успешно отправить сообщение. Однако, если после отправки длины я пытаюсь выполнить весь второй вызов SPI_SendData за один шаг. Arduino не регистрирует всю строку. Я попытался изменить предскаляр SPI, чтобы получить более высокие значения, и это, похоже, помогло. то есть arduino был способен выводить больше допустимых символов (в результате "hellod"). Это меня смущает, так как APB1, управляющий SPI2, имеет максимальную частоту 42 МГц. Предскалярное значение, которое я нашел, дало мне лучшие результаты, было 7, что означает, что PCLK делится на 256, что приводит, если я правильно об этом думаю, к частоте максимум 164 МГц. Основываясь на спецификациях Ardunio, у него не должно возникнуть проблем при обработке этого сообщения. Я думаю, у меня может быть проблема в том, как часто отправляются биты? Может быть, моя функция SPI_SendData работает неправильно?

Другие детали:

  • 4 провода, соединяющих платы: GND, CLK (STM: PB13 <-> UNO: 13), MOSI (STM: PB15 <-> A: 11) и NSS (STM: PB12 <-> A: 10)
  • У меня нет анализатора logi c, поэтому я не смог чтобы увидеть, что на самом деле отправляется (что отстой)
  • Я понимаю, что может быть трудно получить полный контекст без публикации всего кода, но я подумал, что это будет слишком много, поэтому попытался просто опубликовать соответствующий части.

Регистрация:

16:01:42.461 -> Slave Initialized
16:01:42.494 -> Slave waiting for ss to go low
16:01:45.500 -> slave selected
16:01:45.500 -> Waiting to receive length
16:01:45.534 -> Length: 68
16:01:47.520 -> Received: 
16:01:47.554 -> Received: d
16:01:48.968 -> Received: 
16:01:48.968 -> Received: d
16:01:49.858 -> Received: 
16:01:49.858 -> Received: d
16:01:50.499 -> Received: 
16:01:50.499 -> Received: d
16:01:51.137 -> Received: 
16:01:51.137 -> Received: d
16:01:51.707 -> Received: 
16:01:51.741 -> Received: d
16:01:52.244 -> Received: 
16:01:52.277 -> Received: d
16:01:52.782 -> Received: 
16:01:52.782 -> Received: d
16:01:53.360 -> Received: 
16:01:53.393 -> Received: d
16:01:53.866 -> Received: 
16:01:53.899 -> Received: d
16:01:54.441 -> Received: 
16:01:54.441 -> Received: d
16:01:54.946 -> Received: 
16:01:54.980 -> Received: d
16:01:55.484 -> Received: 
16:01:55.518 -> Received: d
16:01:55.994 -> Received: 
16:01:56.027 -> Received: d
16:01:56.599 -> Received: 
16:01:56.599 -> Received: d
16:01:57.105 -> Received: 
16:01:57.138 -> Received: `
16:01:57.609 -> Received: 
16:01:57.644 -> Received: d
16:01:58.085 -> Received: 
16:01:58.118 -> Received: d
16:01:58.622 -> Received: 
16:01:58.622 -> Received: d
16:01:59.128 -> Received: 
16:01:59.161 -> Received: d
16:01:59.676 -> Received: 
16:01:59.676 -> Received: d
16:02:00.146 -> Received: 
16:02:00.180 -> Received: d
16:02:00.654 -> Received: 
16:02:00.654 -> Received: d
16:02:01.127 -> Received: 
16:02:01.127 -> Received: d
16:02:01.634 -> Received: 
16:02:01.668 -> Received: d
16:02:02.105 -> Received: 
16:02:02.139 -> Received: d
16:02:02.582 -> Received: 
16:02:02.582 -> Received: d
16:02:03.052 -> Received: 
16:02:03.052 -> Received: d
16:02:03.729 -> Received: 
16:02:03.729 -> Received: d
16:02:04.236 -> Received: 
16:02:04.236 -> Received: d
16:02:04.774 -> Received: 
16:02:04.774 -> Received: d
16:02:05.336 -> Received: 
16:02:05.336 -> Received: d
16:02:05.947 -> Received: 
16:02:05.980 -> Received: d
16:02:06.485 -> Received: 
16:02:06.485 -> Received: d
16:02:06.518 -> done receiving word
16:02:06.518 -> Rcvd:
16:02:06.553 -> ddddddddddddddd`dddddddddddddddddd
16:02:06.620 -> Length:68
16:02:06.620 -> Slave deselected
16:02:06.654 -> Slave waiting for ss to go low
16:02:07.025 -> slave selected
16:02:07.058 -> Waiting to receive length
16:02:07.092 -> Length: 100

Код Arduino:

/* SPI Slave Demo
 *
 * SPI pin numbers:
 * SCK   13  // Serial Clock.
 * MISO  12  // Master In Slave Out.
 * MOSI  11  // Master Out Slave In.
 * SS    10  // Slave Select . Arduino SPI pins respond only if SS pulled low by the master
 *

 */
#include <SPI.h>
#include<stdint.h>
#define SPI_SCK 13
#define SPI_MISO 12
#define SPI_MOSI 11
#define SPI_SS 10

char dataBuff[500];
//Initialize SPI slave.
void SPI_SlaveInit(void) 
{ 
 #if 0 
  // Initialize SPI pins.
  pinMode(SPI_SCK, INPUT);
  pinMode(SPI_MOSI, INPUT);
  pinMode(SPI_MISO, OUTPUT);
  pinMode(SPI_SS, INPUT);

  // Enable SPI as slave.
  SPCR = (1 << SPE);
 #endif 
   // Initialize SPI pins.
  pinMode(SCK, INPUT);
  pinMode(MOSI, INPUT);
  pinMode(MISO, OUTPUT);
  pinMode(SS, INPUT);
  //make SPI as slave

  // Enable SPI as slave.
  SPCR = (1 << SPE);

}

//This function returns SPDR Contents 
uint8_t SPI_SlaveReceive(void)
{
  /* Wait for reception complete */
  while(!(SPSR & (1<<SPIF)));
  /* Return Data Register */
  return SPDR;
}


//sends one byte of data 
void SPI_SlaveTransmit(char data)
{
  /* Start transmission */
  SPDR = data;

  /* Wait for transmission complete */
  while(!(SPSR & (1<<SPIF)));
}


// The setup() function runs right after reset.
void setup() 
{
  // Initialize serial communication 
  Serial.begin(9600);

  // Initialize SPI Slave.
  SPI_SlaveInit();

  Serial.println("Slave Initialized");
}
 uint16_t dataLen = 0;
  uint32_t i = 0;
// The loop function runs continuously after setup().
void loop() 
{



  Serial.println("Slave waiting for ss to go low");
  while(digitalRead(SS) );
  Serial.println("slave selected");

 //  Serial.println("start");

  //1. read the length  
//  dataLen = (uint16_t)( SPI_SlaveReceive() | (SPI_SlaveReceive() << 8) );
  //Serial.println(String(dataLen,HEX));
 i = 0;
  Serial. println("Waiting to receive length");
  dataLen = SPI_SlaveReceive();
  for(i = 0 ; i < dataLen ; i++ )
  {
    dataBuff[i] =  SPI_SlaveReceive();
    Serial.print("Received: ");
    Serial.println(dataBuff[i]);
  }
  Serial.println("done receiving word");

  //  Serial.println(String(i,HEX));
  dataBuff[i] = '\0';

  Serial.println("Rcvd:");
  Serial.println(dataBuff);
  Serial.print("Length:");
  Serial.println(dataLen);

  while(!digitalRead(SS));
  Serial.println("Slave deselected");


}

Основной код STM:

#include <string.h>
#include "stm32f407xx.h"


/*
 * PB15 --> SPI2_MOSI
 * PB14 --> SPI2_MISO
 * PB13 --> SPI2_SCLK
 * PB12 --> SPI2_NSS
 * ALT Function mode: 5
 */

void SPI_GPIO_Setup(void);
void GPIO_ButtonInit(void);
void SPI_Setup(void);

void delay(int time){
    for(uint32_t i=0; i<time; i++);
}

int main(void) {

    char userData[] = "Hello world";

    //initizalize button
    GPIO_ButtonInit();

    //This function is used to initialize the gpio pins to behave as spi pins
    SPI_GPIO_Setup();

    //This function is used to initialize the SPI peripheral
    SPI_Setup();

    //this makes NSS signal internally high and avoids MODF error
    //NOTE: This should be taken care of in my SPI_Init function
//  SPI_SSIConfig(SPI2, ENABLE);

    //SSOE to 1 does NSS output enable
    //I don't want to enable this in this way, but it is more simple due to the code design
    //i.e. (using shifts instead of masks for registers)
    SPI_SSOEConfig(SPI2, ENABLE);

    while(1){
        //wait for button to be pressed
        while(! GPIO_ReadFromInputPin(GPIOA, GPIO_PIN_0));

        delay(250000); //used for button debouncing

        //enable the SPI2 peripheral --- according to generated code, this seems to be done in the transmit function
        // I a SET_BIT macro in the transmit function if SPI isn't enabled, make sure to test this
        SPI_PeripheralControl(SPI2, ENABLE);

        //first send the length of data (Arduino expects this as 1 byte)
        uint8_t dataLen = strlen(userData);
        SPI_SendData(SPI2, &dataLen, 1);

        //send data
        SPI_SendData(SPI2, (uint8_t*)userData, dataLen);

        //confirm the SPI is not busy
        while(SPI_GetFlagStatus(SPI2, SPI_BSY_FLAG));

        //disable the SPI2 peripheral --- according to Generated code, this seems to be done in abort functions
        SPI_PeripheralControl(SPI2, DISABLE);
    }

    return 0;
}

void SPI_GPIO_Setup(void) {

    GPIO_Handle_t SPIpins;

    SPIpins.pGPIOx = GPIOB;
    SPIpins.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_ALTFN;
    SPIpins.GPIO_PinConfig.GPIO_PinAltFunMode = 5;
    SPIpins.GPIO_PinConfig.GPIO_PinOPType = GPIO_OPTYPE_PP;
    SPIpins.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PU;
    SPIpins.GPIO_PinConfig.GPIO_PinSpeed = GPIO_SPEED_FAST;

    //MOSI
    SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_15;
    GPIO_Init(&SPIpins);


    //MISO
//  SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_14;
//  GPIO_Init(&SPIpins);

    //SCLK
    SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_13;
    GPIO_Init(&SPIpins);

    //NSS
    SPIpins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_12;
    GPIO_Init(&SPIpins);


}
void GPIO_ButtonInit(void){
    GPIO_Handle_t GpioBtn;

    // configure button
    //Set up PA0 to be an input for the button press
    GpioBtn.pGPIOx = GPIOA;
    GpioBtn.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_0;
    GpioBtn.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_IN;
    GpioBtn.GPIO_PinConfig.GPIO_PinSpeed = GPIO_SPEED_FAST;
    GpioBtn.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_NO_PUPD;
    GPIO_Init(&GpioBtn);
}
void SPI_Setup(void){
    SPI_Handle_t hSPI;

    hSPI.SPIx = SPI2;
    hSPI.SPIConfig.SPI_BusConfig = SPI_BUS_CONFIG_FD;
    hSPI.SPIConfig.SPI_DeviceMode = SPI_DEVICE_MODE_MASTER;
    hSPI.SPIConfig.SPI_SclkSpeed = SPI_SCLK_SPEED_DIV8; //generate SCLK of 2 MHz
    hSPI.SPIConfig.SPI_DFF = SPI_DFF_8BITS;
    hSPI.SPIConfig.SPI_CPOL = SPI_CPOL_LOW;
    hSPI.SPIConfig.SPI_CPHA = SPI_CPHA_LOW;
    hSPI.SPIConfig.SPI_SSM = SPI_SSM_DI; //Hardware slave management disabled for NSS pin

    SPI_Init(&hSPI);
}

Код драйвера SPI:

    /*
 * stm32f407xx_spi_driver.c
 *
 *  Created on: Oct 17, 2019
 *      Author: pigmo
 */

#include "stm32f407xx_spi_driver.h"

/**************************************************************************
 *                  APIs supported by this driver
 * For more information about these APIs check the function description
 **************************************************************************/
/*
 * Peripheral clock setup
 */

/******************************************************
 * @fn                  - SPI_PeriClockControl
 *
 * @brief               - Enables or disables peripheral clock for the given SPI port
 *
 * @param[in]           - base address of SPI peripheral
 * @param[in]           - ENABLE or DISABLE macros
 *
 * @return              - none
 *
 * @Note                - SPI4 is reserved and won't be affected by this function
 */
void SPI_PeriClockControl(SPI_RegDef_t* pSPIx, uint8_t enOrDi){
    if(enOrDi == ENABLE){
            if(pSPIx == SPI1){
                SPI1_PCLK_EN();
            } else if (pSPIx == SPI2){
                SPI2_PCLK_EN();
            } else if (pSPIx == SPI3){
                SPI3_PCLK_EN();
            }
        } else {
            if(pSPIx == SPI1){
                SPI1_PCLK_DI();
            } else if (pSPIx == SPI2){
                SPI2_PCLK_DI();
            } else if (pSPIx == SPI3){
                SPI3_PCLK_DI();
            }
        }
}

/*
 * Init and De-init
 */

/******************************************************
 * @fn                  - SPI_Init
 *
 * @brief               - Initializes a given GPIO peripheral with given configurations
 *
 * @param[in]           - Struct holding base address and desired configurations of SPI peripheral
 *
 * @return              - none
 *
 * @Note                - I don't like the way this is done, but I'm doing it to be consistent with the course
 *                          Instead, I think the SPIConfig should hold masks, not enum type values
 */
void SPI_Init(SPI_Handle_t *pSPIHandle){

    //enable the spi clock
    SPI_PeriClockControl(pSPIHandle->SPIx, ENABLE);

    //disable the SPI (concept taken from STM generated code)
    SPI_PeripheralControl(pSPIHandle->SPIx, DISABLE);

    //first configure the SPI CR1 register
    uint32_t tempReg = 0;

    //1. configure the device mode
    if(pSPIHandle->SPIConfig.SPI_DeviceMode == SPI_DEVICE_MODE_MASTER){
        // if the device is master mode, then the SSI must also be 1 to avoid an error (unless Multi master?)
        // this is only true if SSM is enabled, can we keep this functionality even is SSM is disabled?
        // based on stm generated code, the SSI stays enabled for SSM enabled and disabled
        tempReg |= pSPIHandle->SPIConfig.SPI_DeviceMode << SPI_CR1_MSTR;
        tempReg |= (1 << SPI_CR1_SSI); // this should probably be done in another way
    } else {
        tempReg |= pSPIHandle->SPIConfig.SPI_DeviceMode << SPI_CR1_MSTR;
    }

    //2. configure the bus config
    if(pSPIHandle->SPIConfig.SPI_BusConfig == SPI_BUS_CONFIG_FD){
        //bdi should be cleared
        tempReg &= ~(1 << SPI_CR1_BIDIMODE);
    } else if(pSPIHandle->SPIConfig.SPI_BusConfig == SPI_BUS_CONFIG_HD){
        //bdi should be set
        tempReg |= ~(1 << SPI_CR1_BIDIMODE);
    } else {
        //bdi should be cleared
        tempReg &= ~(1 << SPI_CR1_BIDIMODE);
        //RXONLY should be set
        tempReg |= (1 << SPI_CR1_RXONLY);
    }

    //3. configure the clock speed
    tempReg |= (pSPIHandle->SPIConfig.SPI_SclkSpeed << SPI_CR1_BR);

    //4. configure the Data frame format
    tempReg |= (pSPIHandle->SPIConfig.SPI_DFF << SPI_CR1_DFF);

    //5. configure the clock polarity
    tempReg |= (pSPIHandle->SPIConfig.SPI_CPOL << SPI_CR1_CPOL);

    //6. configure the clock phase
    tempReg |= (pSPIHandle->SPIConfig.SPI_CPHA << SPI_CR1_CPHA);

    //7. determine hardware or software slave management
    tempReg |= (pSPIHandle->SPIConfig.SPI_SSM << SPI_CR1_SSM);

    pSPIHandle->SPIx->CR1 = tempReg;
}

/******************************************************
 * @fn                  - SPI_DeInit
 *
 * @brief               - Deinitializes a given SPI periphal and resets register
 *
 * @param[in]           - base address of SPI peripheral
 *
 * @return              - none
 *
 * @Note                - none
 */
void SPI_DeInit(SPI_RegDef_t* pSPIx){
    if(pSPIx == SPI1){
        SPI1_REG_RESET();
    } else if (pSPIx == SPI2){
        SPI2_REG_RESET();
    } else if (pSPIx == SPI3){
        SPI3_REG_RESET();
    }
}

/*
 * Data send and receive
 */

uint8_t SPI_GetFlagStatus(SPI_RegDef_t* pSPIx, uint32_t flag){
    if(pSPIx->SR & flag){
        return SET;
    } else {
        return RESET;
    }
}

/******************************************************
 * @fn                  - SPI_SendData
 *
 * @brief               - sends data of length len to transmit buffer
 *
 * @param[in]           - base address of SPI peripheral
 * @param[in]           - address of Tx buffer
 * @param[in]           - length of data to send
 *
 * @return              - none
 *
 * @Note                - This is a blocking call
 */
//actual code seems much more complex, check that out
void SPI_SendData(SPI_RegDef_t* pSPIx, uint8_t *pTxBuffer, uint32_t len){
    //if SPi is not enabled, then enable it
    if((pSPIx->CR1 & (1 << SPI_CR1_SPE)) != (1 << SPI_CR1_SPE)){
        SET_BIT(pSPIx->CR1, (1 << SPI_CR1_SPE)); // this is repetitive, should just have a mask,
    }

    while(len > 0) {
        //1. Wait until Tx is empty
        while(SPI_GetFlagStatus(pSPIx, SPI_TXE_FLAG) ==  RESET);

        //2. Check DFF in CR1
        if (pSPIx->CR1 & (1 << SPI_CR1_DFF) ) {
            //16 bit dff
            //3. load data into DR
            pSPIx->DR = *((uint16_t*)pTxBuffer);
            len--;
            len--;
            (uint16_t*)pTxBuffer++;
        } else {
            //8 bit dff
            //3. load data into DR
            pSPIx->DR = *pTxBuffer;
            len--;
            pTxBuffer++;
        }

    }
}

/******************************************************
 * @fn                  - SPI_ReceiveData
 *
 * @brief               - Receives data of length len to Receive buffer
 *
 * @param[in]           - base address of SPI peripheral
 * @param[in]           - address of Rx buffer
 * @param[in]           - length of data to Receive
 *
 * @return              - none
 *
 * @Note                - none
 */
void SPI_ReceiveData(SPI_RegDef_t* pSPIx, uint8_t *pRxBuffer, uint32_t len){

}

/*
 * IRQ configuration and handling
 */

/******************************************************
 * @fn                  - SPI_IRQITConfig
 *
 * @brief               - enables or disables a SPI peripherals IRQ functionality
 *
 * @param[in]           - IRQ number of SPI peripheral
 * @param[in]           - ENABLE or DISABLE macros
 *
 * @return              - none
 *
 * @Note                - none
 */
void SPI_IRQInterruptConfig(uint8_t IRQNumber, uint8_t enOrDi){

}

/******************************************************
 * @fn                  - SPI_IRQPriorityConfig
 *
 * @brief               - sets the priority for a given IRQ number
 *
 * @param[in]           - IRQ priority of SPI peripheral
 * @param[in]           - IRQ number of SPI peripheral
 *
 * @return              - none
 *
 * @Note                - modifies the NVIC peripheral
 */
void SPI_IRQPriorityConfig(uint8_t IRQPriority, uint8_t IRQNumber){

}

/******************************************************
 * @fn                  - SPI_IRQHandling
 *
 * @brief               - deals with a interrupt triggered by a SPI pin
 *
 * @param[in]           - Struct holding base address and desired configurations of SPI peripheral
 *
 * @return              - none
 *
 * @Note                - none
 */
void SPI_IRQHandling(SPI_Handle_t *pHandle){

}

/******************************************************
 * @fn                  - SPI_PeripheralControl
 *
 * @brief               - Enables or Disables SPI
 *
 * @param[in]           - Pointer to a SPI register
 *
 * @return              - none
 *
 * @Note                - I'm implementing this in the transmit function
 */
void SPI_PeripheralControl(SPI_RegDef_t *pSPIx, uint8_t enOrDi){
    if(enOrDi == ENABLE){
        pSPIx->CR1 |= (1 << SPI_CR1_SPE);
    } else {
        pSPIx->CR1 &= ~(1 << SPI_CR1_SPE);
    }
}

/******************************************************
 * @fn                  - SPI_SSIConfig
 *
 * @brief               - Sets or Resets SSI bit in SPI CR1 reg
 *
 * @param[in]           - Pointer to a SPI register
 *
 * @return              - none
 *
 * @Note                - This code is used in the course I'm following,
 *                      but I think it is redundant and bad practice. It will not be used.
 *                      Instead, I implemented a check in the SPI init function to enable SSI w/ master
 */
void SPI_SSIConfig(SPI_RegDef_t *pSPIx, uint8_t enOrDi){
    if(enOrDi == ENABLE){
        pSPIx->CR1 |= (1 << SPI_CR1_SSI);
    } else {
        pSPIx->CR1 &= ~(1 << SPI_CR1_SSI);
    }
}

/******************************************************
 * @fn                  - SPI_SSOEConfig
 *
 * @brief               - Sets or Resets SSOE bit in SPI CR2 reg
 *
 * @param[in]           - Pointer to a SPI register
 *
 * @return              - none
 *
 * @Note                - This code is used in the course I'm following,
 *                      but I think it is redundant and bad practice. I will use it bec of consistency
 *                      Otherwise, in the STM generated code, this bit is determine by a NSS value in the init struct
 *                      See: WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode));
 */
void SPI_SSOEConfig(SPI_RegDef_t *pSPIx, uint8_t enOrDi){
    if(enOrDi == ENABLE){
        pSPIx->CR2 |= (1 << SPI_CR2_SSOE);
    } else {
        pSPIx->CR2 &= ~(1 << SPI_CR2_SSOE);
    }
}
...