cancel
Showing results for 
Search instead for 
Did you mean: 

SPI works only one time. STM32L4xx CMSIS

ngrigoriadis
Senior

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! 

15 REPLIES 15

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

 

@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. 

If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar

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

@ngrigoriadis 

 

Could you try this implementation please 

 

    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;

        /* Wait for RXNE to become 0, means RX_FIFO has no data */
        while ((READ_BIT(sSPIx->SR, SPI_SR_RXNE))) {}

        /* Decrease the size of the payload to be read. */
        size--;
    }
If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar

@Saket_Om  After the second use of the SPIx_receive it stops at the while loop and can't continue.

@Saket_Om Found the solution. I have added this 

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

in the SPIx_receive function and everything works fine.