c++cspislavestm8

STM8 as SPI slave can't send back data


I have build a prototype board with a STM8L, and I want it to be used and configured as a SPI slave. I am testing it with a raspberry pi as master.

I use the lib provided by ST called "STM8 Standard Peripherals Library" for this, but the documentation is very poor and doesn't expain how to do this...

I can send data from the Raspberry Pi with no issue and receive it on the STM8 but I can't send back any data to the raspberry from the STM8 on MISO.

Is anybody known how I can send back some data to the Raspberry Pi master? Where is my mistake?

Here is the main code:

void main(void)
{
  // GPIO
  GPIO_Init(GPIOA, GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
  CLK_Config();
  

  // Set the MOSI and SCK at high level 
  GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5, ENABLE);
  
  SPI_DeInit(SPI1);

  SPI_Init(SPI1, SPI_FirstBit_LSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
           SPI_CPOL_Low, SPI_CPHA_2Edge, SPI_Direction_2Lines_FullDuplex,
           SPI_NSS_Hard, (uint8_t)0x07);

  SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);


  // Enable SPI
  SPI_Cmd(SPI1, ENABLE);


  /* Infinite loop */
  while (1)
  {
    while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));

    // SPI polling
    if(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == SET) {

      while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));
      
      GPIO_ToggleBits(GPIOA, GPIO_Pin_7);
      
      uint8_t data = SPI_ReceiveData(SPI1);

      while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));

      // I can't send back data here, it doesn't work
      SPI_SendData(SPI1, 0xFA);

      uint8_t test = SPI1->DR;

      GPIO_ResetBits(GPIOA, GPIO_Pin_7);
    }
  }
}


static void CLK_Config(void)
{
  /* Select HSE as system clock source */
  CLK_SYSCLKSourceSwitchCmd(ENABLE);
  CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_HSI);
  
  /*High speed external clock prescaler: 1*/
  CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_2);

  while (CLK_GetSYSCLKSource() != CLK_SYSCLKSource_HSI)
  {}

  /* Enable SPI clock */
  CLK_PeripheralClockConfig(CLK_Peripheral_SPI1, ENABLE);
}

And the RPi simple code:

#include <iostream>
#include <wiringPi.h>
#include <wiringPiSPI.h>

using namespace std;

int main()
{
    wiringPiSetup();
    wiringPiSPISetup(0, 50000);

    unsigned char data[] = {0x5A};

    wiringPiSPIDataRW(0, data, 2);

    std::cout<<data<<std::endl;


    return 0;

Thank you for your help! :)

Edit: I think the mistake is in uC code because the spi data register still contain the data sent by the master after I read it. I can't change it even by trying to write directly in the register.

Also: is it normal that the device only contain one data register for SPI? How is it supposed to be full duplex if it haven't one for MOSI (Rx) and one for MISO(Tx)? I think there is something I don't understand about SPI. I am not very experienced with this serial protocol. I mainly used I2C before.


Solution

  • I finaly found where were my mistakes.

    First, I forgot to configure a pullup resistor on the MISO pin:

      // Set the MOSI and SCK at high level 
      GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_7, ENABLE);
    
    

    Next, the SPI config were wrong. The Rpi was in MSB and the STM8 in LSB, and phase was on the second edge when it needed to be on the first edge:

      SPI_Init(SPI1, SPI_FirstBit_MSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
               SPI_CPOL_Low, SPI_CPHA_1Edge, SPI_Direction_2Lines_FullDuplex,
               SPI_NSS_Hard, (uint8_t)0x07);
    

    Finaly, not a mistake but a not optimal way to test: I were sending 0x81 with the master, but it is symetric in binary (0b10000001). I should have sent some asymetric message, for example 0x17 (0b00010111).

    And the complete STM8 code:

    #include "stm8l15x.h"
    #include "stm8l15x_it.h"    /* SDCC patch: required by SDCC for interrupts */
    
    static void CLK_Config(void);
    void Delay(__IO uint16_t nCount);
    
    void main(void)
    {
      // GPIO
      GPIO_Init(GPIOA, GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
      CLK_Config();
      
    
      // Set the MOSI and SCK at high level (I added MOSI)
      GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_7, ENABLE);
      
      SPI_DeInit(SPI1);
    
      SPI_Init(SPI1, SPI_FirstBit_MSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
               SPI_CPOL_Low, SPI_CPHA_1Edge, SPI_Direction_2Lines_FullDuplex,
               SPI_NSS_Hard, (uint8_t)0x07);
    
      SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
    
    
      // Enable SPI
      SPI_Cmd(SPI1, ENABLE);
    
    
      /* Infinite loop */
      while (1)
      {
        while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));
    
        // SPI polling
        if(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == SET) {
    
          // maybe this line is not necessary, I didn't have the time to test without it yet 
          while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY);
          
          uint8_t data = SPI_ReceiveData(SPI1);
    
          while(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE));
          
          if(data==0x82) SPI_SendData(SPI1, 0xCD);
    
          GPIO_ResetBits(GPIOA, GPIO_Pin_7);
        }
      }
    }
    
    /* Private functions ---------------------------------------------------------*/
    
    static void CLK_Config(void)
    {
      /* Select HSE as system clock source */
      CLK_SYSCLKSourceSwitchCmd(ENABLE);
      CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_HSI);
      
      /*High speed external clock prescaler: 1*/
      CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_2);
    
      while (CLK_GetSYSCLKSource() != CLK_SYSCLKSource_HSI)
      {}
    
      /* Enable SPI clock */
      CLK_PeripheralClockConfig(CLK_Peripheral_SPI1, ENABLE);
    }
    
    void Delay(__IO uint16_t nCount)
    {
      /* Decrement nCount value */
      while (nCount != 0)
      {
        nCount--;
      }
    }
    
    /*******************************************************************************/
    
    
    
    #ifdef  USE_FULL_ASSERT
    
    void assert_failed(uint8_t* file, uint32_t line)
    { 
      /* Infinite loop */
      while (1)
      {
      }
    }
    #endif
    
    

    PS:

    I am on linux and soft tools were not adapted to my OS, so I used some tools to be able to develop with it. I think it can be useful for some people, so I add it here:

    First, the lib were not able to compile with SDCC, so I used the patch I found here: https://github.com/gicking/STM8-SPL_SDCC_patch

    To upload to the uC, I use stm8flash with a ST-LINK V2: https://github.com/vdudouyt/stm8flash

    I also had some trouble to find the lib for the STM8L. Here it is: https://www.st.com/en/embedded-software/stsw-stm8016.html

    PS2:

    I understand that it is not easy to answer to hardware related questions. Does anybody knows some websites which are more specified on this kind of questions?