2024-05-07 09:56 AM - edited 2024-05-07 11:31 AM
I'm trying to get SPI1 working on an STM32U575 (NUCLEO board), starting from a base of existing working STM32F4 LL API code, but have failed.
I am running this on a NUCLEO-U575ZI-Q board, so MOSI on PA7 (D11), MISO on PA6 (D12), SCLK on PA5(D13) and with SB63 not bridged so that I can use PB0 (the "IO1" pin of the QSPI pins on CN10) as NSS.
Probing the pins, the SPI1 SCLK does not activate when I attempt to transmit data and the NSS pin stays high (it goes high when I configure SPI1 so it is working, after a fashion). Not sure whether it is relevant but writes to CR1 do not modify the CR1 register contents as seen by the debugger, almost like SPI1 is not enabled (writes to other registers are visible in the debugger). Here are my steps:
// Set source clock and enable SPI clock (all APB clocks have already been configured)
__HAL_RCC_SPI1_CONFIG(RCC_SPI1CLKSOURCE_PCLK2);
__HAL_RCC_SPI1_CLK_ENABLE();
// Disable SPI while we configure
LL_SPI_Disable(SPI1);
// Configure MOSI, MISO, CLK and NSS pins
LL_GPIO_InitTypeDef gpioInitStruct;
gpioInitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
gpioInitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
gpioInitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
gpioInitStruct.Pull = LL_GPIO_PULL_UP;
gpioInitStruct.Alternate = LL_GPIO_AF_5;
gpioInitStruct.Pin = 0x00E0;
LL_GPIO_Init(GPIOA, &gpioInitStruct);
gpioInitStruct.Pin = 0x0001;
LL_GPIO_Init(GPIOB, &gpioInitStruct);
// Configure SPI registers
LL_SPI_SetMode(SPI1, LL_SPI_MODE_MASTER);
LL_SPI_SetBaudRatePrescaler(SPI1, LL_SPI_BAUDRATEPRESCALER_DIV256);
LL_SPI_SetDataWidth(SPI1, LL_SPI_DATAWIDTH_8BIT);
LL_SPI_SetNSSMode(SPI1, LL_SPI_NSS_HARD_OUTPUT);
LL_SPI_SetTransferSize(SPI1, 1);
// Enable SPI
LL_SPI_Enable(SPI1);
LL_SPI_StartMasterTransfer(SPI1);
// Try to transmit the character 'A', but nothing happens on the MOSI/NSS/CLK pins
LL_SPI_TransmitData8(SPI1, 'A');
What have I missed?
Solved! Go to Solution.
2024-05-08 03:20 AM - edited 2024-05-08 08:31 AM
I switched to SPI3 to avoid the conflict with PB0 on the NUCLEO board and realised that, in the simple test code, I had not initialised the clocks to the GPIOs and after fixing that I do get proper SPI TX behaviour. This wouldn't have been a problem in the more structured code I had started out with but now that I have a working case I can move forward. In case it helps anyone, here is some working test code:
#include "stm32u5xx_ll_bus.h"
#include "stm32u5xx_ll_rcc.h"
#include "stm32u5xx_ll_gpio.h"
#include "stm32u5xx_ll_spi.h"
#include "stm32u5xx_hal_spi.h"
LL_GPIO_InitTypeDef gpioInitStruct;
// Set SPI clock source and enable it
__HAL_RCC_SPI3_CONFIG(RCC_SPI3CLKSOURCE_PCLK3);
__HAL_RCC_SPI3_CLK_ENABLE();
// Disable SPI while we configure
LL_SPI_Disable(SPI3);
// Configure MOSI (PB_5), MISO (PB_4), CLK (PB_3) and NSS (PA_4) pins
gpioInitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
gpioInitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
gpioInitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
gpioInitStruct.Pull = LL_GPIO_PULL_UP;
gpioInitStruct.Alternate = LL_GPIO_AF_6;
gpioInitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
LL_GPIO_Init(GPIOB, &gpioInitStruct);
gpioInitStruct.Pin = GPIO_PIN_4;
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
LL_GPIO_Init(GPIOA, &gpioInitStruct);
// Configure SPI registers
LL_SPI_SetMode(SPI3, LL_SPI_MODE_MASTER);
// This gives a 625 kHz clock
LL_SPI_SetBaudRatePrescaler(SPI3, LL_SPI_BAUDRATEPRESCALER_DIV256);
LL_SPI_SetDataWidth(SPI3, LL_SPI_DATAWIDTH_8BIT);
LL_SPI_SetNSSMode(SPI3, LL_SPI_NSS_HARD_OUTPUT);
LL_SPI_SetTransferSize(SPI3, 1);
// Enable SPI
LL_SPI_Enable(SPI3);
LL_SPI_StartMasterTransfer(SPI3);
// Transmit the character 'A'
LL_SPI_TransmitData8(SPI3, 'A');
2024-05-07 01:26 PM - edited 2024-05-07 01:29 PM
The documentation says:
> The spi_pclk clock signal feeds the peripheral bus interface. It must be active when accesses to the SPI registers are required.
This is fine, I have this I think. Then it says:
> When the SPI works as master, it needs spi_ker_ck kernel clock coming from RCC active during communication to feed the serial interface clock via the clock generator where it can be divided by prescaler or bypassed optionally. The signal is then provided to slaves via the SCK pin and internally to the serial interface domain of the master.
This bit I don't understand: how do I enable this "other" clock to SPI1, and does it mean that the clock I am dividing via the MBR bits is this clock and not the peripheral clock?
2024-05-07 02:05 PM
You probably shouldn't use the F4 LL SPI code at all - the U5 SPI module is very different (not to mention the rest of the MCU).
2024-05-07 02:09 PM
In the end it needs to fit into the same structure, since the code has to support both, and 90% of it is the same: same SPI configuration, same GPIO configuration, and this is a simple polled, non-DMA'ed arrangement, so I can ignore much of the new stuff in the STM32U575 world.
But I'm obviously not doing something that I should be doing.
2024-05-08 03:20 AM - edited 2024-05-08 08:31 AM
I switched to SPI3 to avoid the conflict with PB0 on the NUCLEO board and realised that, in the simple test code, I had not initialised the clocks to the GPIOs and after fixing that I do get proper SPI TX behaviour. This wouldn't have been a problem in the more structured code I had started out with but now that I have a working case I can move forward. In case it helps anyone, here is some working test code:
#include "stm32u5xx_ll_bus.h"
#include "stm32u5xx_ll_rcc.h"
#include "stm32u5xx_ll_gpio.h"
#include "stm32u5xx_ll_spi.h"
#include "stm32u5xx_hal_spi.h"
LL_GPIO_InitTypeDef gpioInitStruct;
// Set SPI clock source and enable it
__HAL_RCC_SPI3_CONFIG(RCC_SPI3CLKSOURCE_PCLK3);
__HAL_RCC_SPI3_CLK_ENABLE();
// Disable SPI while we configure
LL_SPI_Disable(SPI3);
// Configure MOSI (PB_5), MISO (PB_4), CLK (PB_3) and NSS (PA_4) pins
gpioInitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
gpioInitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
gpioInitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
gpioInitStruct.Pull = LL_GPIO_PULL_UP;
gpioInitStruct.Alternate = LL_GPIO_AF_6;
gpioInitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
LL_GPIO_Init(GPIOB, &gpioInitStruct);
gpioInitStruct.Pin = GPIO_PIN_4;
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
LL_GPIO_Init(GPIOA, &gpioInitStruct);
// Configure SPI registers
LL_SPI_SetMode(SPI3, LL_SPI_MODE_MASTER);
// This gives a 625 kHz clock
LL_SPI_SetBaudRatePrescaler(SPI3, LL_SPI_BAUDRATEPRESCALER_DIV256);
LL_SPI_SetDataWidth(SPI3, LL_SPI_DATAWIDTH_8BIT);
LL_SPI_SetNSSMode(SPI3, LL_SPI_NSS_HARD_OUTPUT);
LL_SPI_SetTransferSize(SPI3, 1);
// Enable SPI
LL_SPI_Enable(SPI3);
LL_SPI_StartMasterTransfer(SPI3);
// Transmit the character 'A'
LL_SPI_TransmitData8(SPI3, 'A');
2024-05-08 11:44 AM - edited 2024-05-08 01:49 PM
One additional note on this: the code above works when you single step through the debugger. If you just let it run, it does not work: the LL_SPI_Enable() does not have the desired effect; if you stop it in the debugger after the code above and look at the SPI registers, the PE bit of CR1 is not set. If, however, you put a short delay (a loop of 100 nops() appears to be sufficient) between the end of the GPIO configuration part and the start of the SPI configuration part then it works, consistently, always.
I have absolutely no idea why this should be: nothing tricksie going on here, just a single thread running that is configuring HW at start of day. And it is not a "GPIO settling" thing as the PE bit of CR1 is not being successfully set by LL_SPI_Enable().
I will raise this with ST to see if there is something I have misunderstood.
2024-05-09 02:24 AM
Turns out I was falling foul of ye olde MODF problem: the SPI HW monitors the CS line (either HW, or SW if emulated) and, if it thinks it has been pulled low, it switches to slave mode and does not allow the PE bit to be set in CR1. In the code above the nops(), I guess, are required to allow the CS pin to settle before it is sampled by the HW...?
Note also that, in my real code I needed to do the following, immediately after disabling SPIx, and before doing anything else, when I happened not to have a CS pin, to avoid the problem for that case:
LL_SPI_SetNSSMode(SPIx, LL_SPI_NSS_SOFT);
LL_SPI_SetInternalSSLevel(SPIx, LL_SPI_SS_LEVEL_HIGH);
This was not necessary on STM32F4, must be a difference with the new SPI HW I guess.