cancel
Showing results for 
Search instead for 
Did you mean: 

How can I get a correct initial idle clock level with STM32F401/Cubemx and multiple SPI devices on one SPI bus?

HSteg
Associate II

I'm using cubemx for stm32f401.

I have to address several SPI devices using a sing spi bus, and different chip selects.

Each SPI device requires a different configuration of CPOL/CPHA.

So when the bus is idle (when all the chip selects are high) I use HAL_SPI_Init () to change the settings SPI settings accordingly. This works fine with the exception of the initial idle clock level.

Apparantly the SPI controller does not set SCK to the correct idle value until after the first message is sent.

The reference manual (RM0368 Rev 5) states on page 564 that:

The idle state of SCK must correspond to the polarity selected in the SPI_CR1 register (by pulling up SCK if CPOL=1 or pulling down SCK if CPOL=0)

It seems that this isn't implemented in the HAL SPI driver?

1 ACCEPTED SOLUTION

Accepted Solutions
HSteg
Associate II

I corrected the issue by adding the code at the bottom of this post to a user section of generated stm32f4xx_hal_msp.c:

This fix is specifically for my situation, and will have to be adapted for other SPI busses / CPUs.

In my opinion the neglection of this part of the SPI configuration is a bug and should not have to be added by the user, but should have been generated by CubeMX, and/or have been part of stm32f4xx_hal_spi.c.

As also mentioned in my comments below with this fix a call to HAL_SPI_DeInit () is required to re-execute this initialization.

This may be a little ineficient in some circumstances, but it is good enough for me.

HAL_SPI_Init () returns no error when it is called while it was already initialized, however it's effect depends on the value hspi->State this may be efficient in some cases, but is not very robust in terms of reproducibility.

  /* USER CODE BEGIN SPI1_MspInit 1 */
 
    /* Setup the initial idle level of SCK according to note at botom of
     * page 564 of STM32F401 reference manual (RM0368 Rev 5):
     *
     * The idle state of SCK must correspond to the polarity selected in the
     * SPI_CR1 register (by pulling up SCK if CPOL=1 or pulling down SCK if
     * CPOL=0).
     *
     * Warning: For a proper reconfiguration of the SPI bus initial SCK level
     * hspi->State has to be HAL_SPI_STATE_RESET, therefore call
     * HAL_SPI_DeInit() before the reconfiguring HAL_SPI_Init ().
     */
    GPIO_InitStruct.Pin = SPI1_SCLCK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    if (hspi->Init.CLKPolarity==SPI_POLARITY_HIGH) {
        GPIO_InitStruct.Pull = GPIO_PULLUP;
    } else {
        GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    }
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* USER CODE END SPI1_MspInit 1 */

View solution in original post

4 REPLIES 4
S.Ma
Principal

Don't know if it helps: Don't disable the SPI, just change the clock polarity when setting wihen SPI is not BUSY (SR register). Otherwise, push a dummy byte out before NSS goes low if under deadline pressure (and it would still be SPI compliant). More than the clock polarity, the data sampling edge is what really matters.

HSteg
Associate II
  • I don't disable SPI before I initialize using the HAL_SPI_Init (). HAL_SPI_Init () however does disable SPI, which is as adviced by the reference manual.
  • I already did the dummy write when Idle to be SPI compliant, that works, but I am now in the process of cleaning things up. IMHO HAL_SPI_Init should take care of it.
  • True about the data sampling edges, and I would not have noticed unless I had an issue with one of the slaves. Therefore I had to make sure everyting is compliant before I could make a proper complaint to the manufacturer of the slave device. This slave device has a minimum time in the datasheet for the falling edge of NSS and the initial edge of SCK. The dummy write was the work-around that helped me become compliant with the slave.

And now I just want to have it work without the dummy write work-around.

HSteg
Associate II

I corrected the issue by adding the code at the bottom of this post to a user section of generated stm32f4xx_hal_msp.c:

This fix is specifically for my situation, and will have to be adapted for other SPI busses / CPUs.

In my opinion the neglection of this part of the SPI configuration is a bug and should not have to be added by the user, but should have been generated by CubeMX, and/or have been part of stm32f4xx_hal_spi.c.

As also mentioned in my comments below with this fix a call to HAL_SPI_DeInit () is required to re-execute this initialization.

This may be a little ineficient in some circumstances, but it is good enough for me.

HAL_SPI_Init () returns no error when it is called while it was already initialized, however it's effect depends on the value hspi->State this may be efficient in some cases, but is not very robust in terms of reproducibility.

  /* USER CODE BEGIN SPI1_MspInit 1 */
 
    /* Setup the initial idle level of SCK according to note at botom of
     * page 564 of STM32F401 reference manual (RM0368 Rev 5):
     *
     * The idle state of SCK must correspond to the polarity selected in the
     * SPI_CR1 register (by pulling up SCK if CPOL=1 or pulling down SCK if
     * CPOL=0).
     *
     * Warning: For a proper reconfiguration of the SPI bus initial SCK level
     * hspi->State has to be HAL_SPI_STATE_RESET, therefore call
     * HAL_SPI_DeInit() before the reconfiguring HAL_SPI_Init ().
     */
    GPIO_InitStruct.Pin = SPI1_SCLCK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    if (hspi->Init.CLKPolarity==SPI_POLARITY_HIGH) {
        GPIO_InitStruct.Pull = GPIO_PULLUP;
    } else {
        GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    }
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* USER CODE END SPI1_MspInit 1 */

I am using SPI2 on a STM32L471 device. I've had the same problem and the solution listed above worked brilliantly. Thank you. Although I've had to modify the code slightly since I use SPI2. Also note that I had to put this code block after the HAL_SPI_Init() within MX_SPI2_Init(), that is between /* USER CODE SPI2_Init 2 */ begin and end statements. I did not use HAL_SPI_DeInit ().

/* USER CODE BEGIN SPI2_Init 2 */
  
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_1; // GPIOD Pin 1 is the SPI2_CLK
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  if (hspi2.Init.CLKPolarity==SPI_POLARITY_HIGH) {
    GPIO_InitStruct.Pull = GPIO_PULLUP;
  } else {
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  }
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

/* USER CODE END SPI2_Init 2 */