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! 

1 ACCEPTED SOLUTION

Accepted Solutions

@Saket_Om Found the solution. I have added this 

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

in the SPIx_receive function and everything works fine.

View solution in original post

15 REPLIES 15
Saket_Om
ST Employee

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?

 

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

Thanks
Omar
Saket_Om
ST Employee

Hello @ngrigoriadis 

 

Any updates regarding the issue you were facing?

 

Has the HAL API resolved your issue?

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

Thanks
Omar

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.

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.

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

Thanks
Omar

@Saket_Om Thank you for the recommendation I will try it and I will come back for update.

ngrigoriadis
Senior

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

ngrigoriadis
Senior

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

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.

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

Thanks
Omar
ngrigoriadis
Senior

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