cancel
Showing results for 
Search instead for 
Did you mean: 

How to use SPI NSS on STM32G0

Andre_k
Associate III

We are trying to implement the SPI interface on a STM32G070 MCU using DMA and no interrupts. MOSI MISO and SCLK all work fine, but the NSS signal does not behave the way we want it to. What we need is a precise change from 1 to 0 signaling the start of the transmission and right after the transmission is finished we need the immediate change from 0 to 1. What do we need to do to get this behavior?

SPI1 registers:

CR1: 0x0105

CR2: 0x1704

0693W00000SunfWQAR.jpg 

SPI and DMA init:

    LL_SPI_InitTypeDef SPI_InitStruct = {0};
    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
 
    /* Peripheral clock enable */
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
    /** SPI1 GPIO Configuration +**/
    GPIO_InitStruct.Pin = SPI_SCK_PIN;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(SPI_SCK_GPIO_Port, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = SPI_MOSI_PIN;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(SPI_MOSI_GPIO_Port, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = SPI_MISO_PIN;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(SPI_MISO_GPIO_Port, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = SPI_NSS_PIN;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(SPI_NSS_GPIO_Port, &GPIO_InitStruct);
 
    LL_GPIO_SetOutputPin(SPI_NSS_GPIO_Port, SPI_NSS_PIN);
 
    /* SPI1 DMA Init */
    /* SPI1_RX Init */
    LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_SPI1_RX);
    LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
    LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
 
    /* SPI1_TX Init */
    LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_SPI1_TX);
    LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
    LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_LOW);
    LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MDATAALIGN_BYTE);
 
    /* SPI1 parameter configuration*/
    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
    SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
    SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
    SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
    SPI_InitStruct.NSS = LL_SPI_NSS_HARD_OUTPUT;
    SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;
    SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
    SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitStruct.CRCPoly = 7;
    LL_SPI_Init(SPI1, &SPI_InitStruct);
    LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
    LL_SPI_DisableNSSPulseMgt(SPI1);

1 ACCEPTED SOLUTION

Accepted Solutions
Andre_k
Associate III

We did some more debugging and found the following:

Using hardware NSS with DMA and NSSP mode enabled, the NSS is handled by the peripheral as expected(See image), just that the NSS is deasserted between the datawords. This is not what we wanted but it's usable, without controlling NSS in software 😉.

> There is no hardware in the 'G0 (and other STM32, except 'H7) SPI, which would do what you describe.

That's incorrect. Every behavior I described was previously validated.

Thanks for all the inputs. I think we can close this.

View solution in original post

10 REPLIES 10
AScha.3
Chief II

SPI (Motorola/master/hardware out/..) should work;

what is >> LL_SPI_DisableNSSPulseMgt << ?

and is out enabled?

NSS output enable (SSM=0,SSOE = 1): MCU is set as master. The NSS pin is managed .

If you feel a post has answered your question, please click "Accept as Solution".
Andre_k
Associate III

Best case it should work, ive read that some STM32 dont support proper NSS :(

CR1: 0x0105 ->SSM[9] = 0

CR2: 0x1704 ->SSOE[2] = 1

Btw, LL_SPI_DisableNSSPulseMgt just clears the NSSP bit:

/**
  * @brief  Disable NSS pulse management
  * @note   This bit should not be changed when communication is ongoing. This bit is not used in SPI TI mode.
  * @rmtoll CR2          NSSP          LL_SPI_DisableNSSPulseMgt
  * @param  SPIx SPI Instance
  * @retval None
  */
__STATIC_INLINE void LL_SPI_DisableNSSPulseMgt(SPI_TypeDef *SPIx)
{
  CLEAR_BIT(SPIx->CR2, SPI_CR2_NSSP);
}

AScha.3
Chief II

did you look at chip errata?

and >>some STM32 dont support proper NSS << , yes, i had such a problem with STM32F103: had to make NSS in software , because the chip makes no useful NSS modes (must read in manual the details).

If you feel a post has answered your question, please click "Accept as Solution".

> What we need is a precise change from 1 to 0 signaling the start of the transmission and right after the transmission is finished we need the immediate change from 0 to 1.

> What do we need to do to get this behavior?

You need to handle NSS in software.

JW

Andre_k
Associate III

Thanks for your answers. I have now implemented NSS in Software using DMA transaction complete interrupt.

Can someone from ST please post a list here with all the devices that do not have proper SPI NSS support(NSS Output not deasserted after SCLK stops)?@William Antunes DA MAIA​ 

That way everyone knows which series to avoid, if proper SPI implementation is needed.

Thanks!

> Can someone from ST

This is a primarily user-driven forum, with only casual ST presence.

> after SCLK stops

Define "after SCLK stops" from the SPI module's point of view. Should it implement some kind of timeout? Should it be bound to back-to-back transactions? What if there's a delay in supplying data so that inadvertent delay occurs?

AFAIK there's no such feature in any STM32 SPI, the 'H7 SPI being maybe the only exception. It has a transaction counter and may (I don't use it so am not sure) handle NSS when the transaction count is exhausted. But the SPI in "H7 is an overcomplicated beast, probably occupying way more silicon area (read: is expensive and power hungry) than it's worth.

As I've said in the writeup, should "precise" non-software/interrupt-controlled NSS be a priority, I'd use timers at the cost of externally interconnected pins. If the system is not very loaded (i.e. there are not many interacting busmasters etc., resulting of risk of the SPI feed underrunning), and the timing is not necessarily very tight, the interconnects are not necessary and a simple timer-generated pulse would do. In 'G0, I'd be tempted to pull it out using the DMAMUX.

JW

Andre_k
Associate III

> Define "after SCLK stops" from the SPI module's point of view.

The peripheral obviously knows when to stop the SCLK. Based on that it could wait x peripheral clock cycles before NSS is deasserted. It works when DMA is disabled!

> This is a primarily user-driven forum, with only casual ST presence.

Casual but existent.

In certain applications, knowing which STM32 supports proper NSS with DMA is vital.

> The peripheral obviously knows when to stop the SCLK. Based on that it could wait x peripheral clock cycles before NSS is deasserted.

> It works when DMA is disabled!

There is no hardware in the 'G0 (and other STM32, except 'H7) SPI, which would do what you describe.

Software can disable SPI after end of frame - and that's what Cube/HAL does - by setting SPIx_CR1.SPE, and that sets NSS to high-Z. If you have a pullup on that pin, internal or external, it will pull NSS up, with delay depending on the loading (parasitic capacitance) on that line.

The NSS behaviour is described in the Slave select (NSS) pin management subchapter of SPI chapter in RM:

NSS output enable (SSM=0,SSOE = 1): this configuration is only used when the

MCU is set as master. The NSS pin is managed by the hardware. The NSS signal

is driven low as soon as the SPI is enabled in master mode (SPE=1), and is kept

low until the SPI is disabled (SPE =0). A pulse can be generated between

continuous communications if NSS pulse mode is activated (NSSP=1).

I understand your sentiment, and I can imagine way better peripherals in STM32 and not just SPI (which btw. I would pull out not by overcomplicating the existing modules but by rethinking them and adding more and better directed inter-module connections). However, it is as it is, and we have to cope with it.

JW

Andre_k
Associate III

We did some more debugging and found the following:

Using hardware NSS with DMA and NSSP mode enabled, the NSS is handled by the peripheral as expected(See image), just that the NSS is deasserted between the datawords. This is not what we wanted but it's usable, without controlling NSS in software 😉.

> There is no hardware in the 'G0 (and other STM32, except 'H7) SPI, which would do what you describe.

That's incorrect. Every behavior I described was previously validated.

Thanks for all the inputs. I think we can close this.