Skip to content

Digital Me

Electronics, Computers at leisure time

Menu
  • Home
  • Contact
  • About
Menu
STM32 half-duplex SPI connection

HOWTO: Use STM32 SPI half duplex mode

Posted on May 31, 2014

I’ve got my hands onto some STM32F030F4P6 ARM-Cortex M0 processors. Though touted as “32 cents 32-bit micro”, it is not that inexpensive from DigiKey in one-off quantity ($1.45). However it is still cheaper than ATmegas and offers 3 times the performance. The chip comes in 20-pin TSSOP package. Limited pins require much more thoughts when assigning pin function. For example, using 3-pin half-duplex SPI instead of 4-pin full-duplex SPI saves me 1 very precious GPIO pin. It should be noted that not all SPI slave devices support half duplex mode, and most devices will not mention half-duplex mode in the datasheets. Generally, a SPI slave device supports half duplex SPI mode if:

SPI device supports half-duplex
SPI device supports half-duplex
  1. The device’s MISO (or DOUT) pin uses open-drain output. This is usually true because open-drain allows multiple SPI slaves to share the same MISO line.
  2. In the communication protocol, the slave device always waits for the master to send fixed number of bytes (commands) from  MOSI, then returns a fixed number of bytes to MISO. Some devices which transmit and receive data simultaneously cannot be used in half-duplex mode.
  3. The slave ignores whatever appears on the MOSI pin when transmitting data to the master. This is usually not mentioned in the datasheet. However, if the slave device mandates a CS or STROBE signal to be asserted at the beginning of each data exchange, we can usually assume this is true. Reason being that the slave device is using CS to reset its internal state rather than always listening and parsing command byte(s) from the master.

Half-duplex wiring of STM32 SPI is as follows:

STM32 half-duplex SPI connection
STM32 half-duplex SPI connection

In particular, MOSI and SCK are configured as “Alternate Function” mode. Hardware CS (NSS) management must be disabled and user shall manually control CS using GPIO output. R2 is pull-up resistor as required by SPI. R1 works as protection resistor in case STM32 MOSI pin somehow enters into push-pull output mode. The SPI setup code is as follows:

[code]
void SPI_Configure()
{
SPI_InitTypeDef SPI_InitStructure;
// Enable SPI1 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// SPI1 configuration
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; // Initially Tx
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // Clock steady high
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // Data write on rising (second) edge
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF);
SPI_Cmd(SPI1, ENABLE);
}
[/code]

Line 7 sets the SPI peripheral to half-dulex transmission mode. Line 17 sets the SPI FIFO buffer threshold to quarter full. This is new in STM32F0 with 4-byte SPI FIFO buffer. SPI_RxFIFOThreshold_QF meaning that the SPI_I2S_FLAG_RXNE flag will be set as soon as 1 byte (quarter buffer) is shifted into receiving FIFO. SPI master sending data to slave is as simple as:

[code]
void send_byte(uint8_t val)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS low
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //wait buffer empty
SPI_SendData8(SPI1, val);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); //wait finish sending
GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS high
}
[/code]

The following code demonstrates master sends 1 byte command to slave and reads 1 byte back.

[code]
uint8_t send_and_read_byte(uint8_t cmd)
{
uint8_t result;
GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS low
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //wait buffer empty
SPI_SendData8(SPI1, cmd);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); //wait finish sending
// Read receiving FIFO until it is empty
while (SPI_GetReceptionFIFOStatus(SPI1) != SPI_ReceptionFIFOStatus_Empty)
SPI_ReceiveData8(SPI1);
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
while (!(SPI1->SR & SPI_I2S_FLAG_RXNE)) ; // wait data received
GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS high
SPI1->CR1 |= SPI_Direction_Tx; // Set Tx mode to stop Rx clock
result = SPI_ReceiveData8(SPI1);
return result;
}
[/code]

Immediately after one byte is sent, the program empties all stale data in the FIFO (line 9, 10), then sets SPI direction to receiving mode (line 11). As soon as SPI enters into receiving mode, STM32 will continuously generate clock on SCK pin until receiving mode is disabled. Along with the clock toggling, data are shifted from MOSI pin into receiving FIFO, and SPI_I2S_FLAG_RXNE flag is set once 1 byte of data is received (line 12). The program then disables CS (line 13, to disable slave output) and switches SPI back to transmitting mode (line 14, to stop the clock). These two steps must be executed fast enough before the next clock is sent out to prevent the slave device enter into any undefined state. Timing is very critical here especially when SPI clock is high. To receive multiple bytes from the slave, put line 9-15 into a loop but disable CS only after all data are read. Important thing is to always disable receiving mode immediately after FIFO is quarter full, and verify using a scope or logic analyser to ensure exact 8 clocks are send in-between each reading.

5 thoughts on “HOWTO: Use STM32 SPI half duplex mode”

  1. replay1 says:
    October 17, 2014 at 6:56 pm

    good one! Wish you would make similar tutorial about I2C bus too.

    Keep up the good work!

    Reply
  2. fikir says:
    May 11, 2015 at 8:54 am

    Hi, I have a question. Let say my slave device has no chip select pin. My slave is an fpga and I didn’t make chip select pin. Basically, the fpga will produce serial data out as long as the spi_clk is there and I don’t need the master to send any data. So, in the above code, during the sending 1 byte by master, the spi_clk is produced or not?

    In my case, I would like the spi_clk to only be there when the master is ready to receive data. So, if the master produce 8 clock cycles, the master will receive 8 bit of data. If the clock stop, no data will be coming from the slave. Once the fpga(slave) has sent all the data, the fpga will sent a read_complete signal and all the communication will stop.

    Therefore it is important for the master to only send the clock during receiving mode only. Is this what is meant by `transmitting mode to stop the clock’ in the above code?

    Reply
    1. Baoshi says:
      May 12, 2015 at 9:43 am

      Hi, I think your case it is not standard SPI. It is more likely to be synchronized serial mode. I’m not too sure but if you’re using STM32 you should be able to config USART in synchronized mode and use it directly.

      Reply
      1. fikir says:
        May 12, 2015 at 9:53 am

        Thank you for your reply. I’m going to add chip select signal to my fpga and the reason why I want to use the spi bus is because I’m planning to add few other sensors.

        Reply
  3. Pierre says:
    June 7, 2016 at 11:23 pm

    This is not what half duplex is. SPI is not a time-multiplexed protocol, it is a one byte in / one byte out protocol. Please read about full duplex and half duplex modes (for SPI) and correct your article. Half duplex with SPI only means you only have MOSI or MISO.

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Arduino ARM AVR Charger CNC Distance Sensor emWin ESP31 ESP32 ESP8266 FT230X GCC iPad JTAG LED Strip Makefile Maker Faire MF70 OLED Oscilloscope PCB PIR Sensor Pogo pin PWM Raspberry Pi RGB LED Rigol RTOS Saleae SEGGER Shopping SPI SSD1305 SSD1306 STM8 STM32 STM32Cube STM32CubeMX Storage TFT Tindie Tool USB Power VFD Workbench

Recent Posts

  • Arduino Redesigned – Maker UNO Review
  • Is ESP8266 I/O really 5V tolerant?
  • First sight into ESP32
  • ESP8266 MQTT client on RTOS
  • Maker Faire, Yearly Review, and Rant

Archives

  • March 2018
  • August 2016
  • December 2015
  • August 2015
  • July 2015
  • April 2015
  • February 2015
  • January 2015
  • December 2014
  • October 2014
  • July 2014
  • May 2014
  • March 2014
  • January 2014
  • December 2013
  • November 2013
  • August 2013
  • July 2013
  • May 2013
  • March 2013
  • January 2013
  • December 2012
  • November 2012
  • October 2012
  • September 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
©2023 Digital Me | Design: Newspaperly WordPress Theme