Skip to main content
Graduate
May 9, 2024
Solved

SPI works only one time. STM32L4xx CMSIS

  • May 9, 2024
  • 5 replies
  • 4270 views

Hi,

I am facing a strange issue with the configurations of the SPI. I have an external module which I am communicating with that with SPI2. In order to read its ID I am driving the CS pin low send the command to the device receive the data and CS pin goes high again. If I use only this function to my main program the result is as expected, but if I try to send another command like reading the status of the external module then It does not work. However if I use only the read status function (CS goes low, ......send command......receive....data....., CS goes high) it works correctly. It seems like I am unable to do SPI transmits and receives. I use the debug view and I checked that the CS pin is working correctly while I have multiple module definitions in the main function and also the SPI registers are the same as I had configured them. Furthermore, the GPIO pins are correct. Did you face such problem before? Could be this an issue because I haven't enable FPU?

This is how I am configuring the SPI peripheral:

 

void SPIx_init(SPI_TypeDef *sSPIx, GPIO_TypeDef *GPIOx)
{

	/*Initialize the SPI GPIO pins*/
	SPIx_gpio_init(GPIOx, SPI_MODE);

	/*Enable clock access to the SPIx peripheral*/
	if (sSPIx == SPI1) {
		/*Enable clock access to SPI1*/
		SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SPI1EN);
	} else if (sSPIx == SPI2) {
		/*Enable clock access to SPI2*/
		SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_SPI2EN);
	} else if (sSPIx == SPI3) {
		/*Enable clock access to SPI3*/
		SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_SPI3EN);
	}

	/*Disable SPIx before make any changes (just in case).*/
	CLEAR_BIT(sSPIx->CR1, SPI_CR1_SPE);

	/**
	 * Since the peripheral that we need to communicate has 5MHz max speed we need
	 * to divide our clock to a valid number (16/4 = 4)
	*/
	MODIFY_REG(sSPIx->CR1, SPI_CR1_BR, (0x01 << SPI_CR1_BR_Pos));

	/*Set the idle state of the clock to be low (0)*/
	CLEAR_BIT(sSPIx->CR1, SPI_CR1_CPOL);


	/*Enable the data capture at rising edge of the clock*/
	CLEAR_BIT(sSPIx->CR1, SPI_CR1_CPHA);


	/*Enable master mode*/
	SET_BIT(sSPIx->CR1, SPI_CR1_MSTR);

	/*Set the MSB to transfer first*/
	CLEAR_BIT(sSPIx->CR1, SPI_CR1_LSBFIRST);

	/*Enable software slave management*/
	SET_BIT(sSPIx->CR1, SPI_CR1_SSM);
	SET_BIT(sSPIx->CR1, SPI_CR1_SSI);

	/*Enable full-duplex mode*/
	CLEAR_BIT(sSPIx->CR1, SPI_CR1_RXONLY);

	/*Set the RXNE event threshold to 1/8*/
	SET_BIT(sSPIx->CR2, SPI_CR2_FRXTH);

	/*Set 8-bit data size*/
	MODIFY_REG(sSPIx->CR2, SPI_CR2_DS, (0x07 << SPI_CR2_DS_Pos));

	/*Enable the peripheral*/
	SET_BIT(sSPIx->CR1, SPI_CR1_SPE);

}

 

And this is how I am using transmit and receive functions for communicating with the SPI module.

 

void SPIx_transmit(SPI_TypeDef *sSPIx, uint8_t *data, uint32_t size)
{
	int i=0; //Iterations

	/*Check if the bus is busy*/
	while (READ_BIT(sSPIx->SR, SPI_SR_BSY)) {}

	while ( i < size) {

		/*Wait until TXE bit is set, which indicates TX_FIFO is empty*/
		while (!(READ_BIT(sSPIx->SR, SPI_SR_TXE))) {}

		/*Load the data to the TX_FIFO, in order to transmit data to shift register*/

		*((volatile uint8_t*) &(sSPIx->DR)) = data[i];

		i++;
	}

	/*Wait until TXE is set (TX_FIFO is empty)*/
	while (!(READ_BIT(sSPIx->SR, SPI_SR_TXE))) {}

	/*Wait for BSY to reset (Bus is free)*/
	while (READ_BIT(sSPIx->SR, SPI_SR_BSY)) {}

	/*	Clearing the OVRN = Overrun flag.
	 * 	An overrun condition occurs when data is received by a master or slave and the RXFIFO
	 *	has not enough space to store this received data. This can happen if the software
	 *	did not have enough time to read the previously received data.
	 */

	(void)sSPIx->DR;
	(void)sSPIx->SR;
}

void SPIx_receive(SPI_TypeDef *sSPIx, uint8_t *recBuf, uint32_t size)
{

	/*Wait for the bus to be free*/
	while (READ_BIT(sSPIx->SR, SPI_SR_BSY)) {}

	/*While there is enough space to read*/
	while (size) {

		/*Load dummy data to DR register*/
		sSPIx->DR = 0x00;

		/*Wait for RXNE to become 1, means RX_FIFO has data to be read*/
		while (!(READ_BIT(sSPIx->SR, SPI_SR_RXNE))) {}

		/*Read the data from DR register*/
		*recBuf++ = sSPIx->DR;

		/*Decrease the size of the payload to be read.*/
		size--;
	}
}

void SPIx_enable_slave(GPIO_TypeDef *GPIOx)
{
	/*High to low transaction of CS pin enables the slave devise*/
	CLEAR_BIT(GPIOx->ODR, (1U<<SPIx_GPIO_CS_PIN));
}

void SPIx_disable_slave(GPIO_TypeDef *GPIOx)
{
	/*Low to high transaction of CS pin disables the slave device*/
	SET_BIT(GPIOx->ODR, (1U<<SPIx_GPIO_CS_PIN));
}

 

 I appreciate if someone help me. 

 

Thank you in advance! 

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

    @Saket_Om Found the solution. I have added this 

    *((volatile uint8_t*) &(sSPIx->DR))

    in the SPIx_receive function and everything works fine.

    5 replies

    Technical Moderator
    May 9, 2024

    Hello @ngrigoriadis 

     

    Did you try to use the API provided in HAL library and check if you still have the same issue?

    Could you please share with us the reference to the external module you are using?

     

    Technical Moderator
    May 13, 2024

    Hello @ngrigoriadis 

     

    Any updates regarding the issue you were facing?

     

    Has the HAL API resolved your issue?

    Graduate
    May 14, 2024

    Hello @Saket_Om ,

     

    Sorry for the late response. Unfortunately, I didn't manage to make it work. However I did what you recommended and the operation was successful. Transmission and reception of data works correctly with HAL API, I can read what ever I want from the external flash more than once, in addition with the CMSIS standard, which I have created the peripherals alone I can communicate with the external flash only once.

    Technical Moderator
    May 15, 2024

    Hello @ngrigoriadis 

     

    Please ensure to compare the content of the CR1 and CR2 registers in both cases when using the HAL API and when using the CMSIS standard and make sure that you are not missing any settings. Additionally, check the status register after each transaction and verify if any error flags are set.

    Graduate
    May 28, 2024

    @Saket_Om  Hi, I have checked the CR1 and CR2 registers there are no differences. However, I noticed that after the second SPIx_receive function is called data is being received properly with the only difference is that it misses the first byte. For example its like it sifts one byte right the data that has received. I don't know why it is doing that.

    Graduate
    May 28, 2024

    @Saket_Om After the second use of SPI receive the overrun flag erases. However, if I run the code step by step in debug mode then the overrun is not asserted and the data is received properly.

    Technical Moderator
    May 28, 2024

    Hello @ngrigoriadis 

     

    The overrun flag typically indicates that a new data frame was received before the previous one was read, which can happen if the code isn't processing the received data quickly enough.

    Please consider using DMA process to transfer SPI data directly to memory without CPU intervention, which can help prevent overrun errors.

    Try lowering the SPI clock speed to see if the overrun issue persists. This can help determine if the problem is related to the speed of data processing in your code.

    Graduate
    May 28, 2024

    I lower the baudrate as maximum it can get and the OVR flag is still set after the second use of the SPIx_receive function

     

    Graduate
    May 28, 2024

    @Saket_Om As I decrease the baudrate of the SPI2 that I am using then it is not able to receive any data. If I divide the SystemClock = 16MHz by two or four then it is able to receive data but it seems like it is shifting those data for instance instead of receiving this : 0x1f, 0x24, 0x00, 0x01, 0x00 I am receiving 0x00, 0x00, 0x1f, 0x24, 0x00. But it happens only If I use the receive more than one time.

    Technical Moderator
    May 28, 2024

    @ngrigoriadis 

     

    Ensure that the SPI configuration (clock polarity, clock phase, and data frame size) matches the specifications of the device you're communicating with.

    Please connect the GND of STM32 and the devices you're communicating with. 

    Try to use logic analyzer to be sure that slave is sending the good data. 

    Graduate
    May 28, 2024

    When I am using HAL library the SPI init function is being configured like this(I know that those configurations are correct since I have implement the project with HAL and works as expected):

     

     

     hspi2.Instance = SPI2;
    
     hspi2.Init.Mode = SPI_MODE_MASTER;
    
     hspi2.Init.Direction = SPI_DIRECTION_2LINES;
    
     hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
    
     hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
    
     hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
    
     hspi2.Init.NSS = SPI_NSS_SOFT;
    
     hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
    
     hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
    
     hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
    
     hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    
     hspi2.Init.CRCPolynomial = 7;
    
     hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    
     hspi2.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;

     

    I think that I have the same configurations on my CMSIS SPI implementation:

     

    void SPIx_init(SPI_TypeDef *sSPIx, GPIO_TypeDef *GPIOx)
    {
    
    	/*Initialize the SPI GPIO pins*/
    	SPIx_gpio_init(GPIOx, SPI_MODE);
    
    	/*Enable clock access to the SPIx peripheral*/
    	if (sSPIx == SPI1) {
    		/*Enable clock access to SPI1*/
    		SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SPI1EN);
    	} else if (sSPIx == SPI2) {
    		/*Enable clock access to SPI2*/
    		SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_SPI2EN);
    	} else if (sSPIx == SPI3) {
    		/*Enable clock access to SPI3*/
    		SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_SPI3EN);
    	}
    
    	/*Disable SPIx before make any changes (just in case).*/
    	CLEAR_BIT(sSPIx->CR1, SPI_CR1_SPE);
    
    	/**
    	 * Since the peripheral that we need to communicate has 5MHz max speed we need
    	 * to divide our clock to a valid number (16/4 = 4)
    	*/
    	MODIFY_REG(sSPIx->CR1, SPI_CR1_BR, (0x01 << SPI_CR1_BR_Pos));
    
    	/*Set the idle state of the clock to be low (0)*/
    	CLEAR_BIT(sSPIx->CR1, SPI_CR1_CPOL);
    
    
    	/*Enable the data capture at rising edge of the clock*/
    	CLEAR_BIT(sSPIx->CR1, SPI_CR1_CPHA);
    
    
    	/*Enable master mode*/
    	SET_BIT(sSPIx->CR1, SPI_CR1_MSTR);
    
    	/*Set the MSB to transfer first*/
    	CLEAR_BIT(sSPIx->CR1, SPI_CR1_LSBFIRST);
    
    	/*Enable software slave management*/
    	SET_BIT(sSPIx->CR1, SPI_CR1_SSM);
    	SET_BIT(sSPIx->CR1, SPI_CR1_SSI);
    
    	/*Enable full-duplex mode*/
    	CLEAR_BIT(sSPIx->CR1, SPI_CR1_RXONLY);
    
    	/*Set the RXNE event threshold to 1/8*/
    	SET_BIT(sSPIx->CR2, SPI_CR2_FRXTH);
    
    	/*Set 8-bit data size*/
    	MODIFY_REG(sSPIx->CR2, SPI_CR2_DS, (0x07 << SPI_CR2_DS_Pos));
    
    	/*Enable the peripheral*/
    	SET_BIT(sSPIx->CR1, SPI_CR1_SPE);
    
    }

     

    As for the logic analyzer test I will do it tomorrow and I will post my results