BitBanging – SPI

Things to mention in this post:
* SPI Communication protocol

In this post we are going to do SPI communication protocol via GPIO pins.

About SPI Communication Protocol

As all you know, Spi is a hardware(peripheral) based communication protocol. SPI is a common communication protocol used by many different devices. For example, SD card reader modulesRFID card reader modules, and 2.4 GHz wireless transmitter/receivers all use SPI to communicate with microcontrollers. At the end of the day we could be run out of SPI peripheral. Thus, if we need to use Spi, we should do BitBang Spi.

Let’s BANG BANG! :

Spi have 4 wired connection and if we want to simulate it over GPIO pins we need to simulate every wire’s protocol over GPIO pins seperately.

So we need just 4 GPIO pins for that and they will simulate Sck, Mosi, Miso, Ss connections .

  • SCK : For SCK connection protocol , GPIO pin which is simulate SCK will always toggle.
  • SS : For SS , related GPIO pin have to action before transmitting data.
  • MOSI : Master Out Slave In pin simulates the data as Low and High.
  • MISO : Master In Slave Out pin simulates the data as Low and High.

Let’s Code :

HEADER FILE

In the header file we need to configure our spi pins and port as we want.

/*				DEFINES				*/

/*
 *Spi general ports
*/
#define SPI_PORT	GPIOE
	
/*
 *Spi general pins
*/
#define SPI_CLK		GPIO_PIN_15
#define SPI_MOSI	GPIO_PIN_7
#define SPI_MISO	GPIO_PIN_12
#define SPI_CS		GPIO_PIN_10

And useful macros here ;

Help us to configure GPIO states fastly.

#define	GPIO_OUTPUT_HIGH(port,pin)		HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET);
#define	GPIO_OUTPUT_LOW(port,pin)		HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET);
#define GPIO_INPUT_READ(port,pin)		HAL_GPIO_ReadPin (port, pin);

Microsecond Delay here;

#define DELAYUS(x)		                {\
							uint32_t m = (x * 30);\
							while(m > 0 ){ m--;}  \
						}

C FILE

First of all, we need an Init function that will help us simulating Polarization of the CLK. Here, CPOL is predefined in header file.

/**
  *  @brief  Inits SPI. This function inits chip select and clk pins
  */
void BitBangSpi_Init(void){
	GPIO_OUTPUT_HIGH(SPI_PORT,SPI_CS);
	if(CPOL)
		GPIO_OUTPUT_HIGH(SPI_PORT,SPI_CLK);
}

There we read bits :

/**
  *  @brief  Reads the 1 bit data from GPIO
  *  @retval Related GPIO output
  */
uint8_t BitBangSpi_bitRead(){
	return GPIO_INPUT_READ(SPI_PORT, SPI_MISO);
}

When we need to write bit :

/**
  *  @brief  Writes an 1 bit data.
  *  @param  bit: variable that configures pin output
  */
uint8_t BitBangSpi_bitWrite(uint8_t bit){
	
	uint8_t result = 0;
	
	if (bit)
	{
		// Write '1' bit
		GPIO_OUTPUT_HIGH(SPI_PORT, SPI_MOSI);
	}
	else
	{
		// Write '0' bit
		GPIO_OUTPUT_LOW(SPI_PORT, SPI_MOSI);
	}
	return result;
}

Then, we can write bytes using write bit function which is above :

/**
  *  @brief  Writes an 1 byte data.
  *  @retval Received 1 byte value from GPIO
  */
uint8_t BitBangSpi_byteWrite(uint8_t data){
	
	uint8_t result = 0;
	
	// Loop to write each bit in the byte, LS-bit first
	for (uint8_t loop = 0; loop < 8; loop++)
	{
		if(CPOL){
			GPIO_OUTPUT_LOW(SPI_PORT, SPI_CLK);}
		else
			GPIO_OUTPUT_HIGH(SPI_PORT, SPI_CLK);

		BitBangSpi_bitWrite(data & 0x80);

		// shift the data byte for the next bit
		result  |= (BitBangSpi_bitRead() << loop);

		if(CPOL){
			GPIO_OUTPUT_HIGH(SPI_PORT, SPI_CLK);}
		else
			GPIO_OUTPUT_LOW(SPI_PORT, SPI_CLK);

		data = data << 1;
	}

	return result;
}

So, the final step to send data which is wanted to transmit over SPI. In this function we are using :

/**
  *  @brief  Sends the 1 byte data via GPIO
  *  @param  Tx_data: Variable to the transmits data address
  *  @retval Related GPIO output
  */
uint8_t BitBangSpi_sendByte(uint8_t	Tx_data){
	// Incoming data buffer
	uint8_t Rx_data = 0;

	// To Start the Communication make sure that Chip Select is low.
	GPIO_OUTPUT_LOW(SPI_PORT,SPI_CS);

	// Writing data through to the IO pins
	Rx_data = BitBangSpi_byteWrite(Tx_data);

	// To Finish the Communication make sure that Chip Select is low.
	GPIO_OUTPUT_HIGH(SPI_PORT,SPI_CS);

	return Rx_data;
}

Overall, we achieved the SPI without SPI peripheral. You can use these functions to transmit data and observe the flow using logic analyzer over the related GPIO pins.

You can check the GitHub Link,

Click Here For GitHub

Leave a Reply

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