Implementation of SPI communication using bit banging

Implementation of SPI communication using bit banging


Serial peripheral interface (SPI) is one of the most widely used interfaces between microcontroller and peripheral ICs such as sensors, ADCs, DACs, shift registers, SRAM, and others. This article provides a brief description of the SPI interface followed by an introduction to Analog Devices’ SPI enabled switches and muxes, and how they help reduce the number of digital GPIOs in system board design.

SPI is a synchronous, full duplex main-subnode-based interface. The data from the main or the subnode is synchronized on the rising or falling clock edge. Both main and subnode can transmit data at the same time. The SPI interface can be either 3-wire or 4-wire. This article focuses on the popular 4-wire SPI interface.

The SPI bus specifies four logic signals:

  • SCLK: Serial Clock (output from master)
  • MOSI: Master Out Slave In (data output from master)
  • MISO: Master In Slave Out (data output from slave)
  • CS /SS: Chip/Slave Select (often active low, output from master to indicate that data is being sent)

MOSI on a master connects to MOSI on a slave. MISO on a master connects to MISO on a slave. Slave Select has the same functionality as chip select and is used instead of an addressing concept.

Note: on a slave-only device, MOSI may be labeled as SDI (Serial Data In) and MISO may be labeled as SDO (Serial Data Out)

The signal names above can be used to label both the master and slave device pins as well as the signal lines between them in an unambiguous way, and are the most common in modern products. Pin names are always capitalized e.g. “Chip Select,” not “chip select.”

Many products can have nonstandard SPI pin names:

Serial Clock:


Master Output → Slave Input (MOSI):

  • SIMO, MTSR – correspond to MOSI on both master and slave devices, connects to each other
  • SDI, DI, DIN, SI – on slave devices; connects to MOSI on master, or to below connections
  • SDO, DO, DOUT, SO – on master devices; connects to MOSI on slave, or to above connections

Master Input ← Slave Output (MISO):

  • SOMI, MRST – correspond to MISO on both master and slave devices, connects to each other
  • SDO, DO, DOUT, SO – on slave devices; connects to MISO on master, or to below connections
  • SDI, DI, DIN, SI – on master devices; connects to MISO on slave, or to above connections

Slave Select:

  • SS, SS, SSEL, NSS, /SS, SS# (slave select)
  • CS, CS (chip select)
  • CSN (chip select/enable)
  • CE (chip enable)

What is bit banging:

In computer engineering and electrical engineering, bit banging is a “term of art” for any method of data transmission that employs software as a substitute for dedicated hardware to generate transmitted signals or process received signals. Software directly sets and samples the states of GPIOs (e.g., pins on a microcontroller), and is responsible for meeting all timing requirements and protocol sequencing of the signals. In contrast to bit banging, dedicated hardware (e.g., UART, SPI, I²C) satisfies these requirements and, if necessary, provides a data buffer to relax software timing requirements. Bit banging can be implemented at very low cost, and is commonly used in some embedded systems.

Bit banging allows a device to implement different protocols with minimal or no hardware changes. In some cases, bit banging is made feasible by newer, faster processors because more recent hardware operates much more quickly than hardware did when standard communications protocols were created. – Wikipedia.

Below code snippets demonstrate implementation of SPI communications using general purpose IO.

Define the GPIO pins as LE, MISO, MOSI, CLK we are using STM32CubeIDE for code generation, user can select GPIO pins depending upon their application/microcontroller requirements.

#define LE_HIGH         HAL_GPIO_WritePin(GPIO_LE_GPIO_Port, GPIO_LE_Pin, GPIO_PIN_SET)
#define LE_LOW          HAL_GPIO_WritePin(GPIO_LE_GPIO_Port, GPIO_LE_Pin, GPIO_PIN_RESET)
#define MI         		HAL_GPIO_ReadPin(GPIO_SDA_GPIO_Port, GPIO_SDA_Pin)

other definitions

#define RX_COUNT 4
#define BYTE_SIZE 8
#define SHIFT_BY_1_BIT 1

Define the functions prototypes

static uint8_t send_spi(unsigned char tx);
static uint32_t send_spi_32bits (uint32_t num , char read)
static uint8_t send_spi(unsigned char tx)
	int8_t i ;
	uint8_t rx = 0;
	for(i=BYTE_SIZE ;i>0; i--)
		rx <<= SHIFT_BY_1_BIT;
			rx |= 1;
		((tx >> (i-1)) & 1) ? (SPI_DATA_HIGH) : (SPI_DATA_LOW);
		SPI_CLK_HIGH;                              // SCK = 1
		// Data Hold Time, Clock High Time
		SPI_CLK_LOW;                                // SCK = 0
	return rx;

static uint32_t send_spi_32bits (uint32_t num , char read)
	int8_t tx_index ;
	int8_t rx_index ;
	uint8_t rx[RX_COUNT] ;
	uint32_t rx_data = 0;

	LE_HIGH;                    //LE = 1
	soft_delay_us(1);           //NOP4;

	for (tx_index = 3; tx_index >= 0 ; tx_index--)
		rx[tx_index] = send_spi( ( (num >> (8 * tx_index)) & 0xff ) );
	soft_delay_us(1);       //NOP4;
	LE_LOW;                 //LE = 0
	if(read == 'R')                                 //for receiving byte read function
		for (rx_index = 3; rx_index >= 0 ; rx_index--)
			rx_data |= (rx[rx_index] << (8 * rx_index));
	return (read == 'R')? rx_data : 0;
Software Tools:
  1. STM32CubeIDE
  2. STM32CubeMx
  3. Teraterm
Hardware Setup:
  1. STM32G4 Nucleo-64
  2. Mini USB Cable
  3. Jumper wire

Study Materials:

Above code snippets is one of the way developer can implement spI communication using GPIO’s (bit Banging).

If you enjoyed this article, share your feedback.

Similar topics:

Implementation of I2C communication using bit banging

If you enjoyed this article, share your feedback.

Leave a Reply

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

%d bloggers like this: