cancel
Showing results for 
Search instead for 
Did you mean: 

SPI1 DMA hardware nSS control for use with Wiznet W5500 network chip.

Gordon Freemansky
Associate II

Good day everyone.

I'm trying to get Wiznet made W5500 working with STM32F030 series, using SPI in DMA mode.

Unfortunately, in nSS hardware output mode, SPI bus pulls up nSS line after every transmitted byte (see attached image), while W5500 manual states, that nSS line should be low during whole transaction.

I'm not professional. Using Cube MX for device configuration.

Here is the MX generated code for SPI+DMA setup:

void MX_SPI1_Init(void)

{

 LL_SPI_InitTypeDef SPI_InitStruct = {0};

 LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* Peripheral clock enable */

 LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SPI1);

 LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);

 /**SPI1 GPIO Configuration 

 PA4  ------> SPI1_NSS

 PA5  ------> SPI1_SCK

 PA6  ------> SPI1_MISO

 PA7  ------> SPI1_MOSI

 */

 GPIO_InitStruct.Pin = SPI1_nSS_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(SPI1_nSS_GPIO_Port, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = SPI1_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(SPI1_SCK_GPIO_Port, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = SPI1_MISO_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(SPI1_MISO_GPIO_Port, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = SPI1_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(SPI1_MOSI_GPIO_Port, &GPIO_InitStruct);

 /* SPI1 DMA Init */

 /* SPI1_RX Init */

 LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

 LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_MEDIUM);

 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_TX Init */

 LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

 LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_VERYHIGH);

 LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);

 LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);

 LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);

 LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_HALFWORD);

 LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_HALFWORD);

 /* SPI1 interrupt Init */

 NVIC_SetPriority(SPI1_IRQn, 1);

 NVIC_EnableIRQ(SPI1_IRQn);

 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_1EDGE;

 SPI_InitStruct.NSS = LL_SPI_NSS_HARD_OUTPUT;

 SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV64;

 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_EnableNSSPulseMgt(SPI1);

}

Any ideas?

14 REPLIES 14

Set the NSS pin as GPIO Output and handle it manually.

JW

Gordon Freemansky
Associate II

Hi Jan.

Thanks for reply.

Unfortunately that doesn't work either.

If I set pin 4 GPIOA to Push-Pull output, and register in Wiznet library, then it behavior is really weird. See attached image.

I have registered my functions as per Wiznet recommendations:

uint8_t rcvBuf[20], bufSize[] = {4, 4, 4, 4, 0, 0, 0, 0};

 wiz_NetInfo netInfo = { .mac   = {0xA5, 0x08, 0xdc, 0xab, 0xcd, 0xef},                  // Mac address

                          .ip   = {192, 168, 0, 123},                // IP address

                          .sn   = {255, 255, 255, 0},                // Subnet mask

                          .gw   = {192, 168, 0, 1}};                                    // Gateway address

 uint8_t Broadcast[4] = {192, 168, 0, 255};

 uint8_t ControlIP[4] = {192, 168, 0, 1};

 uint8_t DataIP[4] = {192, 168, 0, 1};

...

reg_wizchip_cs_cbfunc(SPI_nSS_On, SPI_nSS_Off);

 reg_wizchip_spiburst_cbfunc( SPI_DMA_Rx_Burst, SPI_DMA_Tx_Burst);

 wizchip_init(bufSize, bufSize);

 wizchip_setnetinfo(&netInfo);

 wizchip_getnetinfo(&netInfo);

...

void SPI_nSS_On(void)

{

 LL_GPIO_ResetOutputPin(SPI1_nSS_GPIO_Port, SPI1_nSS_Pin);

}

void SPI_nSS_Off(void)

{

 LL_GPIO_SetOutputPin(SPI1_nSS_GPIO_Port, SPI1_nSS_Pin);

}

In any case I'd like to realize the SPI transactions on "pure" SPI, with hardware controlled nSS signal. So my question stays the same: is it possible to have nSS, controlled by hardware, in low state during whole transaction?

> In any case I'd like to realize the SPI transactions on "pure" SPI, with hardware controlled nSS signal.

> So my question stays the same: is it possible to have nSS, controlled by hardware, in low state during whole transaction?

An how would you define "transaction"? How would the SPI module know what is a "transaction" - where does it start and where does it end?

My guess is, that you see pulses in NSS, the above waveform, because there are indeed several transactions back to back. Except that the pulse may be misplaced due to incorrect way to wait for transaction end - you have to wait for the last frame to be *received* rather than transmitted (this is purely guesswork, you did not provide information what is in those "drivers".)

JW

Gordon Freemansky
Associate II

>An how would you define "transaction"? How would the SPI module know what is a "transaction" - where does it start and where does it end?

In case of SPI DMA we have to set addresses of memory & peripheral (DMA_CPARx & DMA_CMARx registers) and buffer size (The post-decrementing of the DMA_CNDTRx register, which contains the number of transactions that have still to be performed).

Yes, but that's DMA, a separate unit from SPI.

I can imagine some extra signalling between DMA and SPI, and I can also imagine a counter within SPI. None of these is present in your STM32 model do there no point in discussing it further.

At the end of the day there's nothing demanding in toggling a pin when the transfer end. As I've said above, the transfer end when all frames have been *received*.

JW

TDK
Guru

> reg_wizchip_cs_cbfunc(SPI_nSS_On, SPI_nSS_Off);

> reg_wizchip_spiburst_cbfunc( SPI_DMA_Rx_Burst, SPI_DMA_Tx_Burst);

The Wiznet chip assumes these are blocking functions. Sending it non-blocking DMA functions is not going to work, since the peripheral may still be busy when the next one executes. The library also assumes the memory it's modifying is valid after the function returns, which is not the case with DMA.

As Jan said, the nSS pin is next to useless. It is not a CS pin and can not be used as one. Toggle CS manually with GPIO.

Change the functions to blocking SPI and your library will work.

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

Hi TDK. Hi Jan.

Thanks for replies!

TDK,

Sorry for stupid question: is blocking function the one, which working with flags (not interrupts), and blocks the other functions from execution until it's finished?

I have rebuilt my SPI Tx/Rx function to use flags only and managed to get expected SPI behavior. At least with simple buffer. Haven't tested with Wiznet library yet.

See attached screenshot.

void SPI_DMA_TxRx_Burst(uint8_t* TxRxBuffer, uint16_t BufLen)

{

 /* Enable SPI1 */

 LL_SPI_Enable(SPI1);

 LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_3, (uint32_t) TxRxBuffer, LL_SPI_DMA_GetRegAddr(SPI1),

                        LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3));

 LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, BufLen);

 LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2, LL_SPI_DMA_GetRegAddr(SPI1), (uint32_t) TxRxBuffer,

                        LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2));

 LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, BufLen);

 /* Enable DMA Channels */

 LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);

 LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);

 while (!(LL_DMA_IsActiveFlag_TC3(DMA1)));

 while (!(LL_DMA_IsActiveFlag_TC2(DMA1)));

 /* Disable DMA1 Channes*/

 LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);

 LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);

 /* Disable SPI1 */

 LL_SPI_Disable(SPI1);

}

The issue with nSS line going high after each byte is sorted out by disabling NSSP (NSS pulse mode).

Also I found that in case of hardware managed SPI1_NSS, nSS line goes down as soon as SPI is enabled and up as soon as it's disabled. Therefore SPI1 enable/disable commands should be issued before/after each SPI DMA transaction.

> Also I found that in case of hardware managed SPI1_NSS, nSS line goes down as soon as SPI is enabled

> and up as soon as it's disabled.

> Therefore SPI1 enable/disable commands should be issued before/after each SPI DMA transaction

You might get disappointed when you find out that the "NSS going up when SPI is disabled" is just because NSS pullup, and SPI disabled threestates the pin. This will start to matter when you'd want to make short NSS pulses on a relatively heavily capacitively loaded line.

JW

TDK
Guru

> Sorry for stupid question: is blocking function the one, which working with flags (not interrupts), and blocks the other functions from execution until it's finished?

Yes. A blocking function (HAL_SPI_Transmit) doesn't return until the operation is complete. A nonblocking function (such as HAL_SPI_Transmit_IT or HAL_SPI_Transmit_DMA) returns even though the SPI operation is still in progress.

I still recommend setting the CS pin as a GPIO output. But of course it's your decision. In any case, the logic analyzer output looks solid.

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