cancel
Showing results for 
Search instead for 
Did you mean: 

Can only transmit one SPI byte?

briankaz
Associate III

Hello,
I am trying to use SPI1 on the STM32H742VITx.

I believe I have configured SPI1 I/Os and CR/CFGR fields correctly.  However, when I try to transmit bytes via SPI I am able to transmit one byte, but after that the SPI_SR_TXC bit remains at 0.  Here is my simplified code for writing a byte:

__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));
*(uint8_t *) &SPI1->TXDR = data;
}

So in other words, the SPI_SR_TXC bit is cleared on the first byte transmission, but then remains high indefinitely (so I suspect the byte is not being transmitted actually).
I've looked at everything that could possibly be related to SPI being enabled and can't see what could be missing.
Does anyone have any suggestions?

Thanks!
-Brian

1 ACCEPTED SOLUTION

Accepted Solutions

OK, I found what was wrong!  I wasn't aware that I have to set the CSTART bit to start transmission.  I edited the TX code as follows and it seems to be working 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 */
    while (!(SPI1->SR & SPI_SR_TXC));
    *(uint8_t *) &SPI1->TXDR = data;
    SPI1->CR1 |= SPI_CR1_CSTART;
}

Does this look right?

Thanks! 

View solution in original post

5 REPLIES 5
TDK
Guru

Read out and display the contents of the SPI registers at the time of the issue.

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

Good idea!  Here they are:

Before first SPI transmit:

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


After first SPI transmit:

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

As you can see, all fields are the same before and after TX except the TXC bit remains cleared after I think I've transmitted a byte.
Are there any other status registers I should examine?
Thanks!

OK, I found what was wrong!  I wasn't aware that I have to set the CSTART bit to start transmission.  I edited the TX code as follows and it seems to be working 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 */
    while (!(SPI1->SR & SPI_SR_TXC));
    *(uint8_t *) &SPI1->TXDR = data;
    SPI1->CR1 |= SPI_CR1_CSTART;
}

Does this look right?

Thanks! 

TDK
Guru

You can set CSTART as part of initialization. It won't send characters until you write them to TXDR.

TXDR should be volatile to ensure it actually writes. You're discarding the volatile qualifier here, probably getting a compiler warning about it.

*(volatile uint8_t *) &SPI1->TXDR = data;

 There are some smaller details. TXDR is a FIFO so you can write multiple bytes, but this works if your goal is just sending. Depends on your end goal exactly. If you're sending and receiving, you should synchronize writes to TXDR with reads from RXDR so you can correlate bytes out to bytes in.

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

Yes, I confirmed that CSTART can be set before or after data gets sent to the TX FIFO.

However, at the end of a transmission CSTART gets reset so you have to set it again each time you transmit.