cancel
Showing results for 
Search instead for 
Did you mean: 

Having trouble with NSS control using STM32H742VIT6

briankaz
Associate III

Hello,

I've configured SPI1 on STM32H742VIT6 connected to pins PA4-7 using AF5.

Here is a dump of the relevant SPI1 registers:

SPI1->CR1  = 00000001
SPI1->CR2  = 00000000
SPI1->CFG1 = 70000007
SPI1->CFG2 = 20400000
SPI1->SR   = 00001002

The MCU should be the SPI master and I am trying to control the NSS output as follows (NSS is on PA4): 

__INLINE void bsp_spi_nss_assert(void)
{
    GPIOA->BSRR = GPIO_BSRR_BR4;
}

__INLINE void bsp_spi_nss_release(void)
{
    GPIOA->BSRR = GPIO_BSRR_BS4;
}

I find that the NSS signal is asserting though and remains low forever (so I don't think the bsp_spi_nss_release function is working).
Can someone recommend anything to try which I might be missing?

Thanks!
-Brian

1 ACCEPTED SOLUTION

Accepted Solutions

Hello,

I solved one problem I was having.  I understood that the RXWNE flag is only set after I have transmitted 4 bytes.  Since I am trying to do SPI send/receive one byte at a time, the correct flag to use to initiate a read seems to be RXP instead of RXWNE.  So I have modified my bsp_spi_read_byte() function as follows:

__INLINE uint8_t bsp_spi_read_byte(void)
{
    uint8_t data;
    while (!(SPI1->SR & SPI_SR_RXP));
    data = *(uint8_t *) &SPI1->RXDR;
		bsp_spi_nss_release();
    return data;
}

So far this is working better.  I still need to debug some final problems related to reading from SD card.
Thanks,
-Brian 

View solution in original post

11 REPLIES 11
TDK
Guru

There are two methods of controlling the NSS/CS pin on SPI:

  • Assign it to the SPI and let the SPI peripheral handle it (this is how you have it configured)
  • Assign it as a GPIO pin and control it manually (this is how you're trying to use it)

I'd recommend the latter. Assign it as a GPIO, set it low when accessing the slave, and high after transaction is complete.

If it's assigned to the SPI, the BSRR register does nothing.

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

Thanks!  Part of the problem I'm having is that I'm porting some SPI code from STM32F373XC to my current STM32H7* project.  So a lot of the SPI configuration settings are different.  The STM32F3* project has the NSS configured as a GPIO as you suggested, but then it configures NSS for software control like this:

    /* Manage NSS in software */
    SPI2->CR1 |= SPI_CR1_SSM;
    SPI2->CR1 |= SPI_CR1_SSI;

I tried to do this (of course with the difference that SSM on the STM32H7* is in the SPI_CFG2 register) but it doesn't seem to work.

I have a lot of nested functions doing disk I/O and it seems the software control with NSS configured as GPIO works well on STM32F3* -- however, when I probe the SSM signal from STM32H7* it just remains high after power-up.

Do you think I have to re-write all of the disk access functions to explicitly assert and de-assert NSS?  I didn't write these functions so I'm a bit apprehensive here...

Thanks!
-Brian

TDK
Guru

The pin needs to be configured as a GPIO output. This means setting the appropriate bits in the GPIO->MODER register. This is not part of the SPI configuration at all--it doesn't matter what you put for SSM as the SPI won't be driving the pin.

If you still can't get it, print out and show the GPIOA->MODER registers after configuration. It should reflect that PA4 is in output mode.

> Do you think I have to re-write all of the disk access functions to explicitly assert and de-assert NSS?  I didn't write these functions so I'm a bit apprehensive here...

If they're already there for the F3, no need for you to rewrite them. The GPIO->BSRR register works the same on both chips.

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

OK, I tried to change my TX function to explicitly assert and release NSS.  It looks like this now:

__INLINE void bsp_spi_write_byte(uint8_t data)
{
    
    /* *(uint8_t *) &SPI1->DR casting is necessary to force sending ONLY 8-bits to the peripheral,
       otherwise it keeps casting uint8_t to uint16_t again and stores additional 0x00 value in
       the TX FIFO */
	bsp_spi_nss_assert();
	SPI1->CR1 |= SPI_CR1_CSTART;
    *(uint8_t *) &SPI1->TXDR = data;
	while (!(SPI1->SR & SPI_SR_TXC));
	bsp_spi_nss_release();
}

For some reason now though, the (SPI1->SR & SPI_SR_TXC) condition is never getting realized so I'm getting stuck in the while loop.

I realize that I will want to leave NSS asserted if the next step is going to be reading a byte back from the memory card, but I need to get the TX to work before fixing that.

It seemed like configuring NSS to be controlled from the SPI allowed the TX to work, but there are some places I need to explicitly assert or release NSS and, as you mentioned, I couldn't do that with NSS controlled from SPI.

Go back to the code you had at the start of the thread where it was working.

The only thing you should change from that is configuring PA4 as a GPIO output.

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

I tried exactly what you suggested, still doesn't seem to work.  It looks like only TX was working earlier, not RX.  When I leave everything the same and configure PA4 as GPIO, I can only write one byte to TXDR.  After that TXC bit remains low -- in other words that first byte I tried to send never got sent.  I can also see that there is no SCK or data activity on the SPI bus.  Here is my entire SPI configuration after setting up the I/Os:

    /* Turn on the clock for SPI1 */
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    
    /* Configure the serial clock baud rate:
       PCLK1 / BR = SPI_BAUDRATE
       73.728 MHz / 256 = 288 kbit/s */
    SPI1->CFG1 |= (SPI_CFG1_MBR_0 | SPI_CFG1_MBR_1 | SPI_CFG1_MBR_2);
    
    /* Set SPI in master mode */
    SPI1->CFG2 |= SPI_CFG2_MASTER;
    
    /* Manage NSS in hardware */
    SPI1->CFG2 &= ~(SPI_CFG2_SSM);
    
    /* Set data size to 8-bit */
    SPI1->CFG1 |= (7U << SPI_CFG1_DSIZE_Pos);
    
    /* Enable SPI peripheral */
    SPI1->CFG2 |= SPI_CFG2_SSOE;
		SPI1->CR1 |= SPI_CR1_SPE;

There's not much there to go wrong.  I guess maybe SSOE can be left unset, but it seems like setting SSOE=1 shouldn't affect anything if NSS is configured as a GPIO.

For completeness, here's my TX code:

__INLINE void bsp_spi_write_byte(uint8_t data)
{
    
    /* *(uint8_t *) &SPI1->DR casting is necessary to force sending ONLY 8-bits to the peripheral,
       otherwise it keeps casting uint8_t to uint16_t again and stores additional 0x00 value in
       the TX FIFO */
	while (!(SPI1->SR & SPI_SR_TXC));
	bsp_spi_nss_assert();
	__log("NSS asserted\n\r");
	SPI1->CR1 |= SPI_CR1_CSTART;
    *(uint8_t *) &SPI1->TXDR = data;
	__log("Data sent\n\r");
}

The __log() calls send text to the UART TX for observation on a terminal.  The terminal outputs "NSS asserted" and "Data sent" and then nothing else, as we get stuck in the while() loop the next time around.

Any ideas what I'm still doing wrong?

TDK
Guru

Try this. Works for me on H723 just now.

You might be getting a MODF error with your code, although your register printouts at the first post don't show it.

  SPI_TypeDef* SPIx = SPI1;
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
  SPIx->CFG1 |= (SPI_CFG1_MBR_0 | SPI_CFG1_MBR_1 | SPI_CFG1_MBR_2);
  SPIx->CFG1 |= (7U << SPI_CFG1_DSIZE_Pos);
  SPIx->CR1 |= SPI_CR1_SSI;
  SPIx->CFG2 |= SPI_CFG2_MASTER | SPI_CFG2_AFCNTR | SPI_CFG2_SSM;
  SPIx->CR1 |= SPI_CR1_SPE;
  SPIx->CR1 |= SPI_CR1_CSTART;

  while (!(SPIx->SR & SPI_SR_TXC));
  *(volatile uint8_t*)&SPIx->TXDR = 0x42;
  while (!(SPIx->SR & SPI_SR_TXC));
  *(volatile uint8_t*)&SPIx->TXDR = 0x69;
  while (!(SPIx->SR & SPI_SR_TXC));
  *(volatile uint8_t*)&SPIx->TXDR = 0x42;
  while (!(SPIx->SR & SPI_SR_TXC));
  *(volatile uint8_t*)&SPIx->TXDR = 0x69;
  while (!(SPIx->SR & SPI_SR_TXC));

 

 If that doesn't work, show your registers with your latest code. Should be this at the end of the above sequence:

TDK_0-1701483758752.png

 

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

Thanks!  Here are the results for the registers:

CR1     = 00001201
CR2     = 00000000
CFG1    = 70000007
CFG2    = 84400000
IER     = 00000000
SR      = 00009007
IFCR    = 00000000
TXDR    = 00000000
RXDR    = 00000000
CRCPOLY = 00000107
TXCRC   = 00000000
RXCRC   = 00000000
UDRDR   = 00000000
I2SCFGR = 00000000

 Looks like the same as what you got except for SR.  Here are the bits I have set that are cleared in your example:
1) RXWNE = 1
2) DXP = 1
3) RXP = 1

The SD card was present when I ran your code so it's possible that it had an affect on these results...

Looks like it's working then. My debugger is reading RXDR which is the difference in SR.

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