SPI Slave Driver

Use this forum for requesting small changes in ChibiOS. Large changes should be discussed in the development forum. This forum is NOT for support.
plyatov
Posts: 25
Joined: Tue Feb 25, 2014 10:24 am
Been thanked: 2 times

Re: SPI Slave Driver

Postby plyatov » Wed May 31, 2017 2:07 pm

Thanks for you attention Giovanni!

Xela
Posts: 15
Joined: Thu Sep 07, 2017 12:05 pm
Has thanked: 3 times

Re: SPI Slave Driver

Postby Xela » Fri Mar 09, 2018 5:14 pm

I'd like to bump this thread as I need to do the same thing.

I have implemented plyatov's method of adding a switch in the spiConfig to bypass the hard coded MSTR setting on the SPI_CR1.

I'm not sure which chibi spi methods I should call to handle a transaction of data from the salve to the master.

I use the following in one of my drivers that is in master mode:
spiAcquireBus(spip); // Acquire ownership of the bus.
spiSelect(spip); // Slave Select assertion.
spiSend(spip, headerLength, headerBuffer); // Slave Register Select
spiSend(spip, bodylength, bodyBuffer); // Write operation.
spiUnselect(spip); // Slave Select de-assertion.
spiReleaseBus(spip); // Ownership release.

Though that is not working here.

Should I be using spiStartExchange? Is spiAcquireBus and spiSelect not necessary?

I am just randomly trying methods, it isn't working well. Does anyone know the proper sequence of calls for slave mode transmit?

I basically need something that is going to take a buffer of data, and then wait until the master begins to clock it through and return when complete.

Cheers
Alex

plyatov
Posts: 25
Joined: Tue Feb 25, 2014 10:24 am
Been thanked: 2 times

Re: SPI Slave Driver

Postby plyatov » Sat Mar 10, 2018 9:10 am

Xela wrote:I'm not sure which chibi spi methods I should call to handle a transaction of data from the salve to the master.


Here is example of how to use my SPI slave patch in ChibiOS applications.
Example communicates with SPI master by means of tl_start_receive() and tl_start_send().
Additionally, assert_dsp_rdy() and clear_dsp_rdy() used for indication of SPI slave readiness to communicate with SPI master. Falling edge of dsp_rdy line generates IRQ for master and it initiates SPI transfer.

Code: Select all

static const SPIConfig spi2_cfg = {
   .slave_mode   = true,
   .end_cb      = NULL,
   .ssport      = GPIOB,
   .sspad      = GPIOB_SPI2_NSS,
   .cr1      = 0 // SPI_MODE_0.
};

#if (SPI_USE_WAIT != TRUE)
#error enable SPI_USE_WAIT in the halconf.h
#endif // SPI_USE_WAIT

/**
 * @brief   Sends data over the SPI bus.
 * @details This synchronous function performs a transmit operation.
 * @pre     In order to use this function the option @p SPI_USE_WAIT must be
 *          enabled.
 * @pre     In order to use this function the driver must have been configured
 *          without callbacks (@p end_cb = @p NULL).
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] spip      pointer to the @p SPIDriver object
 * @param[in] n         number of words to send
 * @param[in] txbuf     the pointer to the transmit buffer
 *
 * @api
 */
static void sspiSend(SPIDriver *spip, size_t n, const void *txbuf) {

  osalDbgCheck((spip != NULL) && (n > 0U) && (txbuf != NULL));

  osalSysLock();
  osalDbgAssert(spip->state == SPI_READY, "not ready");
  osalDbgAssert(spip->config->end_cb == NULL, "has callback");
  spiStartSendI(spip, n, txbuf);
  assert_dsp_rdy();
  (void) osalThreadSuspendS(&spip->thread);
  clear_dsp_rdy();
  osalSysUnlock();
}

/**
 * @brief   Receives data from the SPI bus.
 * @details This synchronous function performs a receive operation.
 * @pre     In order to use this function the option @p SPI_USE_WAIT must be
 *          enabled.
 * @pre     In order to use this function the driver must have been configured
 *          without callbacks (@p end_cb = @p NULL).
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] spip      pointer to the @p SPIDriver object
 * @param[in] n         number of words to receive
 * @param[out] rxbuf    the pointer to the receive buffer
 *
 * @api
 */
static void sspiReceive(SPIDriver *spip, size_t n, void *rxbuf) {

  osalDbgCheck((spip != NULL) && (n > 0U) && (rxbuf != NULL));

  osalSysLock();
  osalDbgAssert(spip->state == SPI_READY, "not ready");
  osalDbgAssert(spip->config->end_cb == NULL, "has callback");
  spiStartReceiveI(spip, n, rxbuf);
  assert_dsp_rdy();
  (void) osalThreadSuspendS(&spip->thread);
  clear_dsp_rdy();
  osalSysUnlock();
}

/**
 * tl_start_receive - setup SPI for data reception.
 */
static void tl_start_receive(void)
{
   tl_state = receive_packet;
   spiStart(&SPID2, &spi2_cfg);
   sspiReceive(&SPID2, TL_PACKET_SIZE, tl_buf);
}

/**
 * tl_start_send - setup SPI for data transmission.
 */
static void tl_start_send(void)
{
   tl_state = send_packet;
   spiStart(&SPID2, &spi2_cfg);
   sspiSend(&SPID2, TL_PACKET_SIZE, tl_buf);
}

Xela
Posts: 15
Joined: Thu Sep 07, 2017 12:05 pm
Has thanked: 3 times

Re: SPI Slave Driver

Postby Xela » Mon Mar 12, 2018 11:58 am

Thank you very much plyatov! That is a great help.

Alex

Xela
Posts: 15
Joined: Thu Sep 07, 2017 12:05 pm
Has thanked: 3 times

Re: SPI Slave Driver

Postby Xela » Tue Mar 20, 2018 1:24 pm

Now that I have my master side built up, I don't quite have this working yet.

It is likely a configuration issue though I am not 100% certain I understand the behavior of this function in terms of execution and program flow.

This spiStartSendI() will send a byte to the SPI buffer via DMA.
The loading of the SPI data register will be handled by the SPI peripheral on board the STM MCU.
I don't have to worry about the fine low level details of signalling.

I am not entirely confident if i should treat spiStartSendI() as a blocking or non-blocking function.
Will this thread remain suspended until the tx buffer has been read?

I am unfortunately working with hardware that does not have an auxiliary line to use as a DATA_RDY signal (will add one later).

At this point i just need to transmit one byte to confirm the connection works.
I run my slave board code and I assume it loads the SPI data register (LEDs/step through show the function just then waits)
On the master side i then manually execute a transfer (with code that works ok in loopback).

While I triple check every single config detail and brute force different baud rates, could someone confirm I do understand how this function is meant to be used?

Alex

plyatov
Posts: 25
Joined: Tue Feb 25, 2014 10:24 am
Been thanked: 2 times

Re: SPI Slave Driver

Postby plyatov » Fri Apr 06, 2018 6:47 pm

Xela wrote:It is likely a configuration issue

You should use only SPIx_NSS line as Chip Select. And it must be configured into Alternate mode to behave as NSS pin of corresponding SPI module.
Typically this is made in the board.h file.

Xela wrote:This spiStartSendI() will send a byte to the SPI buffer via DMA.

It will not send. It will only prepare SPI module for DMA tranfer. The transfer will be started by means of HW, only when SPI master assert NSS line of MCU.

Xela wrote:I am not entirely confident if i should treat spiStartSendI() as a blocking or non-blocking function.

It is non blocking and exits nearly immediately after call.

Xela wrote:Will this thread remain suspended until the tx buffer has been read?

Thread will be suspended by osalThreadSuspendS() until SPI transfer is finalized (NSS line is cleared).

It will be quite helpfull if you will use logical analyzer to see SPI diagrams. I recommend "Saleae Logic 16". The "Saleae Logic 8" can be used too, but it is quite limited. Look at saleae.com or better at aliexpress.com for cheaper clones :-)


Return to “Small Change Requests”

Who is online

Users browsing this forum: No registered users and 3 guests