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

Post by plyatov »

Thanks for you attention Giovanni!
Xela
Posts: 15
Joined: Thu Sep 07, 2017 12:05 pm
Has thanked: 3 times

Re: SPI Slave Driver

Post by Xela »

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

Post by plyatov »

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

Post by Xela »

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

Post by Xela »

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

Post by plyatov »

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 :-)
User avatar
alex31
Posts: 445
Joined: Fri May 25, 2012 10:23 am
Has thanked: 57 times
Been thanked: 88 times

Re: SPI Slave Driver

Post by alex31 »

Hello,

I need advice to implement a SPI slave protocol using chibios/stm32

I want to mimic invensense IMU SPI protocol, which themselves mimic I²C protocol to read/write a register table :
The first byte sent select slave register on 7 LSB, and operation (read or write) on 1 MSB. then next bytes are read of written beginning at the selected address, and auto incrementing.

Since SPI does not permit clock stretching, it means that i only have one SPI clock cycle to get the register address, calculate the dma buffer address (an offset), and setup dma slave operation, does that will work ?

I need at least 1Mhz SPI frequency

Thanks for any advice.
Alexandre
asdasd
Posts: 18
Joined: Wed May 01, 2019 8:54 am
Has thanked: 9 times

Re: SPI Slave Driver

Post by asdasd »

i am using chibios 19.1 how can i use slave spi
it seen some thing different
:oops: :idea:
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: SPI Slave Driver

Post by Giovanni »

asdasd wrote:i am using chibios 19.1 how can i use slave spi
it seen some thing different
:oops: :idea:


Hi,

There is no SPI-slave driver yet in ChibiOS, what is discussed in this thread is a customization.

Giovanni
asdasd
Posts: 18
Joined: Wed May 01, 2019 8:54 am
Has thanked: 9 times

Re: SPI Slave Driver

Post by asdasd »

i know that there is no SPI slave driver, but
if i have correct understand , if i have done the spi slave v1 patch witch made by " plyatov "
it must work?? :?:
asd
Post Reply