Skip to main content
Visitor II
January 10, 2025
Solved

STM8S SPI: how to use it in 3-wire (bidirectional data) mode?

  • January 10, 2025
  • 1 reply
  • 2334 views

Hello,

I want to connect STM8S003 & ADXL345 (accelerometer) using SPI 3wire mode (lines ~CS, SCK & MOSI as single bidirectional data line).

According to 'RM0016 Reference manual STM8S...', the register SPI_CR2 of STM8S has BDM (Bidirectional data mode enable) & BDOE bits (Input/Output enable in bidirectional mode).

I expected to use these bits to control SPI 3 wire connection:

SPI_CR2 =   <BDM=1 | BDOE=1 | SSI=1 | SSM=1 (Master)>   - to set STM8 MOSI as output

SPI_CR2 =   <BDM=1 | BDOE=0 | SSI=1 | SSM=1 (Master)>   - to set STM8 MOSI as input

To exclude possible questions:

1. ADXL345+ATmega8 was tested in 4wire & 3wire modes, all is OK.

2. ADXL345+STM8S003 in 4wire mode works OK. For 3wire mode I've only disconnected line MISO in hardware.

I also can control SPI_CR2 state, chnging it before SPI operations and write the processess using oscilloscope.

Test:

STM8S transmits 0x80 | 0x2C = 0xAC to ADXL345 then it tries to read the answer.

0x80 - read the state of the Register 0x2C of ADXL345.

Result:

When SPI_CR2 =   <BDM=0 | BDOE=0 | SSI=1 | SSM=1 (Master)> (4wire mode), oscilloscope shows correct data transmitting (0xAC). As the MISO line is absent, there is not receiving data (see Osc1.jpg).

When SPI_CR2 =   <BDM=1 | BDOE=1 | SSI=1 | SSM=1 (Master)> (3wire mode), oscilloscope shows incorrect data transmitting: 0x80. (see Osc2.jpg)

Question:

how correctly use BDM & BDOE bits to organize SPI 3wire mode?

or

where one can find an example for SPI 3wire mode?

Examples for SPI 4wire mode are not interesting.

Thanks

 

#define MISO 7 //all for PORTC
#define MOSI 6
#define SCK 5

//SPI_CR2 bits
#define BDM 7
#define BDOE 6

//SPI_SR bits
#define RXNE 1
#define TXE 2

//~CS pin
#define selectSPI_Device PC_ODR_bit.ODR4=0
#define unselectSPI_Device PC_ODR_bit.ODR4=1

void iniSPI()
{
// CLK_PCKENR1 |=0x02; //Clock to SPI enabled -in CLK.h
/** SCK preset to 1 **/
 PC_DDR_bit.DDR5 = 1; //output
 PC_CR1_bit.C15 = 1; //push-pull for output
 PC_CR2_bit.C25 = 1; //if output, speed up to 10MHz
// PC_ODR_bit.ODR5=0; //SCK=0 - as SPI mode 0 is selected
 PC_ODR_bit.ODR5=1; //SCK=1 - as SPI mode 0 is selected
 
/** CS preset to 1 **/
 PC_DDR_bit.DDR4 = 1; //output 
 PC_CR1_bit.C14 = 1; //push-pull for output
 PC_CR2_bit.C24 = 1; //if output, speed up to 10MHz
 unselectSPI_Device; //PC4=1
 
 SPI_CR1=(1<<6)|(5<<3)|(1<<2)|(1<<1)|(1<<0); //SPI enabled | baudrate F/64 | Master configuration | CPOL=1| CPHA=1
 SPI_CR2=(1<<7)|(1<<6)|(1<<1)|(1<<0); //1<<7: SPI 3wire (BiDirection mode); 1<<6 MOSI as output; 1<<1: NSS pin is not used for SLAVE-MASTER mode, it is selected by 1<<0: Master mode
}

unsigned char rdSPI(unsigned char d)
{
 while((SPI_SR & RXNE)==0); //while buffer is empty
 unsigned char res=SPI_DR;	
 return(res);
}

void wrSPI(unsigned char d)
{
 SPI_DR = d; 
 while((SPI_SR & TXE)==0); //while buffer is not empty
}

/////////////////////////////

#define D_out SPI_CR2 |= (1 << BDOE); //SPI_CR2 |=(1<<6); //MOSI as Output
#define D_in SPI_CR2 &= ~(1 << BDOE); //SPI_CR2 &=~(1<<6); //MOSI as Input

void DelaySmall()
{
 __no_operation();
 __no_operation();
 __no_operation();
 __no_operation();
}

void iniADXL()
{
//D_out;
 selectSPI_Device; //FullRes, LeftJust, Range16g(200g)
 wrSPI(0x31); 
 wrSPI(0x40 | 0x08 | 0x04 | 0x03); // SPI 3wire | FullResolution | LeftJustify | Range 16/200g (ADXL345/375)
 unselectSPI_Device;
}

///////////////////////////////
void getRg2C()
{
D_out;
 selectSPI_Device;
 wrSPI(0x2c | 0x80); //Rg0x2C | 0x80 (read state of single Rg 0x2c)
D_in;
 T[0]=rdSPI(0xFF); //0xE5 -ID of ADXL345 & ADXL375 
 unselectSPI_Device;
D_out;
}

//////////////////
void main()
{
//Here UART, TIMERS, CLK, ... are initialized
 iniADXL();
 while(1)
 {
// If UART command is discovered, it'll be exec:
// cmd set STM8S' Rg SPI_SR2 <byte to set>
// cmd getRg2C()
 }
}

 

    This topic has been closed for replies.
    Best answer by Vadim_

    AA1, thanks for answer,

    but such sequence is not a problem.

    Support service proposed solution:

    unsigned char rdSPI(unsigned char d)
    {
    // while((SPI_SR & RXNE)==0); //while buffer is empty
     while(SPI_SR_bit.BSY!=0); 

    This check  waits the end of receiving and works correctly.

    The previous one stopped the process on the first receiving bit; as it correctly worked in 4-wire mode, I used it in 3-wire mode too.

    1 reply

    Visitor II
    February 8, 2025

    RM0016 page 273: In this mode, the procedure is similar to the Transmit-only procedure except that the BDM and BDOE bits must both be set in the SPI_CR2 register before enabling the SPI.

    But you are setting these bits after enable SPI. Also for SPI mode 0, SCK idle level is 0.

     

    Vadim_AuthorAnswer
    Visitor II
    February 8, 2025

    AA1, thanks for answer,

    but such sequence is not a problem.

    Support service proposed solution:

    unsigned char rdSPI(unsigned char d)
    {
    // while((SPI_SR & RXNE)==0); //while buffer is empty
     while(SPI_SR_bit.BSY!=0); 

    This check  waits the end of receiving and works correctly.

    The previous one stopped the process on the first receiving bit; as it correctly worked in 4-wire mode, I used it in 3-wire mode too.