cancel
Showing results for 
Search instead for 
Did you mean: 

SPI CR1 bit SPE will not set

JustSomeGuy
Senior

I am writing my own SPI driver, and using two B-L072Z-LRWAN1 boards to test them; one as an SPI master and one as a slave. On the master device, whenever I enable the SPE bit in the function SPI_Transmit(), it never actually sets to '1', even though the configuration registers are set to the programmed values. Why is this?

Here is the initialization function for the slave device. I am using the HAL SPI handlers so I could debug the SPI registers:

 

void SPI2_Init_Slave(SPI_HandleTypeDef * hspi)
{
	/************** STEPS TO FOLLOW *****************
	1. Wait for the TXE bit to set in the Status Register
	2. Write the data to the Data Register
	3. After the data has been transmitted, wait for the BSY bit to reset in Status Register
	4. Clear the Overrun flag by reading DR and SR
	************************************************/
	hspi->Instance = SPI2;
	if(hspi->Instance->CR1 & SPI_CR1_SPE)
	{
		SPI_Disable(hspi);
	}
	SPI_MspInit(hspi);
	//enable the clocks for the peripheral
	//turn on 2-line unidirectional mode
	hspi->Instance->CR1 &= ~SPI_CR1_BIDIMODE;
	//turn off CRC
	hspi->Instance->CR1 &= ~SPI_CR1_CRCEN;
	//8-bit frame format
	hspi->Instance->CR1 &= ~SPI_CR1_DFF;
	//turn off software slave select
	hspi->Instance->CR1 &= ~(SPI_CR1_SSM | SPI_CR1_SSI);
	//Frame format: MSB first
	hspi->Instance->CR1 &= ~SPI_CR1_LSBFIRST;
	//clear prescalar register
	hspi->Instance->CR1 &= ~SPI_BAUDRATEPRESCALER_256;
	//set prescalar register
	hspi->Instance->CR1 |= SPI_BAUDRATEPRESCALER_32;
	//slave mode
	hspi->Instance->CR1 &= ~SPI_CR1_MSTR;
	//turn off RX only
	hspi->Instance->CR1 &= ~SPI_CR1_RXONLY;
	//set clock polarity
	hspi->Instance->CR1 &= ~(SPI_CR1_CPOL);
	//set clock phase to trigger on first data capture edge
	hspi->Instance->CR1 &= ~(SPI_CR1_CPHA);

	hspi->Instance->CR2 = 0;
}

 

Here is the initialization function for the master device:

 

void SPI2_Init_Master(SPI_HandleTypeDef * hspi)
{
	/************** STEPS TO FOLLOW *****************
	1. Wait for the TXE bit to set in the Status Register
	2. Write the data to the Data Register
	3. After the data has been transmitted, wait for the BSY bit to reset in Status Register
	4. Clear the Overrun flag by reading DR and SR
	************************************************/
	hspi->Instance = SPI2;
        if(hspi->Instance->CR1 & SPI_CR1_SPE)
	{
		SPI_Disable(hspi);
	}
	SPI_MspInit(hspi);
	//enable the clocks for the peripheral
	//RCC->APB2ENR |= RCC_APB1ENR_SPI2EN;
	//turn on 2-line unidirectional mode
	hspi->Instance->CR1 &= ~SPI_CR1_BIDIMODE;
	//turn off CRC
	hspi->Instance->CR1 &= ~SPI_CR1_CRCEN;
	//8-bit frame format
	hspi->Instance->CR1 &= ~SPI_CR1_DFF;
	//turn off slave select software control
	hspi->Instance->CR1 &= ~(SPI_CR1_SSM | SPI_CR1_SSI);
	//Frame format: MSB first
	hspi->Instance->CR1 &= ~SPI_CR1_LSBFIRST;
	//clear prescalar register
	hspi->Instance->CR1 &= ~SPI_BAUDRATEPRESCALER_256;
	//set prescalar register
	hspi->Instance->CR1 |= SPI_BAUDRATEPRESCALER_32;
	//master mode
	hspi->Instance->CR1 |= SPI_CR1_MSTR;
	//turn off RX only
	hspi->Instance->CR1 &= ~SPI_CR1_RXONLY;
	//set clock polarity
	hspi->Instance->CR1 &= ~(SPI_CR1_CPOL);
	//set clock phase to trigger on first data capture edge
	hspi->Instance->CR1 &= ~(SPI_CR1_CPHA);

	hspi->Instance->CR2 = 0;
}

 

pin initialization:

 

void SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI1)
  {
    /* Peripheral clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PB3     ------> SPI1_SCK
    PA7     ------> SPI1_MOSI
    PA6     ------> SPI1_MISO
    */
    GPIO_InitStruct.Pin = PB3_RESERVED_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
    HAL_GPIO_Init(PB3_RESERVED_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = PA7_RESERVED_Pin|PA6_RESERVED_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  }
  if(hspi->Instance==SPI2)
  {
    /* Peripheral clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PB13     ------> SPI1_SCK
    PB15     ------> SPI1_MOSI
    PB14     ------> SPI1_MISO
    */
	/*Configure GPIO pin : SPI_NSS_Pin. conflicting results: either AF, pull-up, PP, HS,
	 * or as output */

	/*Configure GPIO pins : PA7_RESERVED_Pin PA6_RESERVED_Pin */
	GPIO_InitStruct.Pin = SPI2_MOSI_Pin | SPI2_CLK_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	GPIO_InitStruct.Pin = SPI2_MISO_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }

}

 

and the Tx/Rx functions:

 

void SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *data, uint32_t size)
{
	int i = 0;
	uint8_t temp;
	//enable SPI if not already enabled
	if(!(hspi->Instance->CR1 & SPI_CR1_SPE))
		SPI_Enable(hspi);

	hspi->Instance->CR1 |= SPI_CR1_SPE;

	while(i<size)
	{
		while(!((hspi->Instance->SR)&(1<<1)));
		hspi->Instance->DR = data[i];
		i++;
	}
	/*During discontinuous communications, there is a 2 APB clock period delay between the
	write operation to the SPI_DR register and BSY bit setting. As a consequence it is
	mandatory to wait first until TXE is set and then until BSY is cleared after writing the last
	data.
	*/
	while(!((hspi->Instance->SR)&(1<<1)));
	while(!((hspi->Instance->SR)&(1<<7)));

	//  Clear the Overrun flag by reading DR and SR
	temp = hspi->Instance->DR;
	temp = hspi->Instance->SR;
}

void SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *data, uint32_t size)
{
	/************** STEPS TO FOLLOW *****************
	1. Wait for the BSY bit to reset in Status Register
	2. Send some Dummy data before reading the DATA
	3. Wait for the RXNE bit to Set in the status Register
	4. Read data from Data Register
	************************************************/

	if(!(hspi->Instance->CR1 & SPI_CR1_SPE))
	{
		SPI_Enable(hspi);
	}

	while(size)
	{
		// wait for BSY bit to Reset. This will indicate that SPI is not busy in communication
		while(((hspi->Instance->SR)&(SPI_SR_BSY)));
		// send dummy data
		hspi->Instance->DR = 0;
		// Wait for RXNE to set. This will indicate that the Rx buffer is not empty
		while(!((hspi->Instance->SR)&(SPI_SR_RXNE)));
		*data++ = (hspi->Instance->DR);
		size--;
	}
}


void SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{
	if(hspi->Instance->CR1 & SPI_CR1_SSM)
		CS_Enable(hspi);
	if(!(hspi->Instance->CR1 & SPI_CR1_SPE))
		SPI_Enable(hspi);
	SPI_Transmit(hspi, pTxData, Size);
	SPI_Receive(hspi, pRxData, Size);
	if(hspi->Instance->CR1 & SPI_CR1_SSM)
		CS_Disable(hspi);
	if(hspi->Instance->CR1 & SPI_CR1_SPE)
		SPI_Disable(hspi);
}

 

 and clock configuration, which is the same for master and slave:

 

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_6;
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_3;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_RTC;
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

 

  

1 ACCEPTED SOLUTION

Accepted Solutions
gbm
Lead III

1. Start by reading this:

https://community.st.com/t5/stm32-mcus-products/regarding-function-calls-when-performing-spi-communication-in/m-p/738412#M265178

- to understand why your TransmitReceive is incorrect.

2. Replace the funny sequences of &=, |= with single assignment to CR1. To setup the SPI, write 0 to CR1 (or maube not if it wasn't configured before) then write all the bits you want to set together with SPE with a single assignment.

3. SPE must be set after writing to CR2, but in your case there is no need to write to CR2 at all.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

View solution in original post

3 REPLIES 3
gbm
Lead III

1. Start by reading this:

https://community.st.com/t5/stm32-mcus-products/regarding-function-calls-when-performing-spi-communication-in/m-p/738412#M265178

- to understand why your TransmitReceive is incorrect.

2. Replace the funny sequences of &=, |= with single assignment to CR1. To setup the SPI, write 0 to CR1 (or maube not if it wasn't configured before) then write all the bits you want to set together with SPE with a single assignment.

3. SPE must be set after writing to CR2, but in your case there is no need to write to CR2 at all.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

1. Thanks, obviously I was misunderstanding transmit/receive.

2. This worked for me. Weirdly enough, sometimes I would get SPI communication showing up on the oscilloscope with the previous method. Then I changed the baud rate prescalar and it didn't work anymore. Changed it back, and still nothing. In the HAL functions, peripherals are enabled by setting the enable bit only using an OR mask. Why does it not work in this case?

Imen.D
ST Employee

Hello @JustSomeGuy ,

Try putting a break point additionally into the IP interrupt service to be sure no error occurs, and monitor the SPI register access flow. So, check the SPI register values before and after the SPI transmit tentative.

Try changing bit running NSS as hardware.

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