[STM32] SPI master 3 wires (bidimode)

Discussions and support about ChibiOS/HAL, the MCU Hardware Abstraction Layer.
User avatar
alex31
Posts: 360
Joined: Fri May 25, 2012 10:23 am
Location: toulouse, france
Has thanked: 36 times
Been thanked: 56 times
Contact:

[STM32] SPI master 3 wires (bidimode)

Postby alex31 » Thu Jun 09, 2022 10:01 am

Hello,

Does anyone had any success in using spi 3 wires, managing SPI_CR1_BIDIMODE and SPI_CR1_BIDIOE ?

What I have tried is to have 2 spiConfig structure, one for reading, one for writing, both with SPI_CR1_BIDIMODE, the writing one having SPI_CR1_BIDIOE in addition.

When I want to send, I do spiStart with the write configuration, then spiSend, but it hangs : spi does not signal completion to the dma which get stuck.
If I remove SPI_CR1_BIDIMODE from configuration, that does not hangs (but obviously works in 4 wire mode)

I use chibios 21.11.trunk with lastest patches.

Alexandre -- trying to communicate with infineon TDA5150

edit1 : polled exchange does not work neither because loop get stuck in this loop, which is logic in 3 wire mode where write and read must be sequential.

Code: Select all

    while ((spip->spi->SR & SPI_SR_RXNE) == 0U) {
      /* Waiting frame transfer.*/
    }

User avatar
alex31
Posts: 360
Joined: Fri May 25, 2012 10:23 am
Location: toulouse, france
Has thanked: 36 times
Been thanked: 56 times
Contact:

Re: [STM32] SPI master 3 wires (bidimode)

Postby alex31 » Thu Jun 09, 2022 3:28 pm

Since I have not a lot data to transmit, ended writing low level polled function to send and receive one frame.

Code: Select all

/**
 * @brief   send one frame using a polled wait.
 * @details This synchronous function send one frame using a polled
 *          synchronization method. This function is useful when in 3 wires mode
 *          where half duplex data use a shared MOSI line.
 *          Send and Read have to be done sequentially
 *
 * @param[in] spip      pointer to the @p SPIDriver object
 * @param[in] frame     the data frame to send over the SPI bus
 * @return              void
 */
void spi_lld_polled_send(SPIDriver *spip, uint16_t frame) {

  /*
   * Data register must be accessed with the appropriate data size.
   * Byte size access (uint8_t *) for transactions that are <= 8-bit.
   * Halfword size access (uint16_t) for transactions that are <= 8-bit.
   */
  chDbgAssert(spip->spi->CR1 & SPI_CR1_BIDIMODE, "spi bidimode must be activated");
  if ((spip->config->cr2 & SPI_CR2_DS) <= (SPI_CR2_DS_2 |
                                           SPI_CR2_DS_1 |
                                           SPI_CR2_DS_0)) {
    volatile uint8_t *dr8p = (volatile uint8_t *)&spip->spi->DR;
    *dr8p = (uint8_t)frame;
    while ((spip->spi->SR & SPI_SR_TXE) == 0U) {
      /* Waiting frame sending.*/
    }
  }
  else {
    volatile uint16_t *dr16p = (volatile uint16_t *)&spip->spi->DR;
    *dr16p = (uint16_t)frame;
    while ((spip->spi->SR & SPI_SR_TXE) == 0U) {
      /* Waiting frame transfer.*/
    }
  }

}

/**
 * @brief   receive one frame using a polled wait.
 * @details This synchronous function receive one frame using a polled
 *          synchronization method. This function is useful when in 3 wires mode
 *          where half duplex data use a shared MOSI line.
 *          Send and Read have to be done sequentially
 * @note    as soon as SPI_CR1_BIDIOE is 0, sck is clocking and data is
 *           deserialised from MOSI line
 * @param[in] spip      pointer to the @p SPIDriver object
 * @return              The received data frame from the SPI bus.
 */

uint16_t spi_lld_polled_receive(SPIDriver *spip) {
  uint16_t frame;
  /*
   * Data register must be accessed with the appropriate data size.
   * Byte size access (uint8_t *) for transactions that are <= 8-bit.
   * Halfword size access (uint16_t) for transactions that are <= 8-bit.
   */
  chDbgAssert(spip->spi->CR1 & SPI_CR1_BIDIMODE, "spi bidimode must be activated");
  spip->spi->CR1 &= ~SPI_CR1_BIDIOE;  /* trigger clocking and receiving */
  if ((spip->config->cr2 & SPI_CR2_DS) <= (SPI_CR2_DS_2 |
                                           SPI_CR2_DS_1 |
                                           SPI_CR2_DS_0)) {
    volatile uint8_t *dr8p = (volatile uint8_t *)&spip->spi->DR;
    while ((spip->spi->SR & SPI_SR_RXNE) == 0U) {
      /* Waiting frame transfer.*/
    }
    frame = (uint16_t)*dr8p;
  }
  else {
    volatile uint16_t *dr16p = (volatile uint16_t *)&spip->spi->DR;
    while ((spip->spi->SR & SPI_SR_RXNE) == 0U) {
      /* Waiting frame transfer.*/
    }
    frame = (uint16_t)*dr16p;
  }

  spip->spi->CR1 |= SPI_CR1_BIDIOE; /* stop clocking, but spurious clock pulse could have been emitted */
  return frame;
}


I think that trying to make 3 wires mode working with DMA would need a lot of rework on the driver, so polled API will be sufficient for now :-)

Alexandre

User avatar
Giovanni
Site Admin
Posts: 14056
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 990 times
Been thanked: 839 times
Contact:

Re: [STM32] SPI master 3 wires (bidimode)

Postby Giovanni » Thu Jun 09, 2022 3:34 pm

Hi,

That was not considered in the current driver. Best approach would be to make a dedicated mini-driver. Keep the same start()/stop() functions from the current driver and add your own exchange().

Giovanni

User avatar
alex31
Posts: 360
Joined: Fri May 25, 2012 10:23 am
Location: toulouse, france
Has thanked: 36 times
Been thanked: 56 times
Contact:

Re: [STM32] SPI master 3 wires (bidimode)

Postby alex31 » Thu Jun 09, 2022 4:00 pm

Hi Giovanni,

In 3 wires mode, exchange is not relevant, what you can do is send or receive since there is only one data line.

Speaking of the current driver, one reason actual dma send / dma receive function does not work in 3 wires mode is the fact that even for only sending or receiving, two dma channels are activated.

For the classic 4 wires exchange function, two dma channels have to be used since sending is concurrent with receiving.
But, send and receive functions also activate two dma channels and fake buffer (aka sink buffer) are used to achieve a dummy dma transaction.
In three wire mode, the SPI peripheral does not generate the SPI_TX_DMA_REQUEST in read mode, neither SPI_RX_DMA_REQUEST in write mode, so a 3 wire SPI driver using DMA would have to activate only one dma channel.

What is the reason the actual driver activates two dma channels for the send and receive API ?

Alexandre

User avatar
Giovanni
Site Admin
Posts: 14056
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 990 times
Been thanked: 839 times
Contact:

Re: [STM32] SPI master 3 wires (bidimode)

Postby Giovanni » Thu Jun 09, 2022 4:05 pm

alex31 wrote:What is the reason the actual driver activates two dma channels for the send and receive API ?


For simplicity, in order to receive you need to pump data in TX or use that "receive only" mode which opens to other problems (stopping the transfer mainly, if does not stop by itself, if you don't stop withing a small time frame then you get an overflow because the RX DMA stops pulling data).

"TX only" uses RX to empty the RX data register or overflows would happen.

Basically it is always an "exchange".

Giovanni


Return to “ChibiOS/HAL”

Who is online

Users browsing this forum: No registered users and 4 guests