Hard time to make SPI work in slave mode

Working libraries, libraries being ported and related hardware
castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Hard time to make SPI work in slave mode

Post by castortiu » Wed Nov 15, 2017 2:55 pm

I'm having a hard time to make the blue pill SPI work on slave mode.

I just found two samples which use two SPI as input/ output to a SD card, I'm trying to modify the samples however I can't make it work for what I need
https://github.com/stevstrong/Audio-sam ... C_Host.ino
download/file.php?id=623

To be honest I'm not sure what I have to do to setup SPI in slave mode.

Currently I have the blue pill using almost the whole PORTA sending a memory buffer to a RGB display making the SPI(1) unusable, the CPU spends a good amount of time to send the buffer to the RGB display so I have a ESP8266 that does image processing to blend several images in one, once the blending is done it sends the frame to the STM32 for display.

I already have the code in the ESP8266 to blend the images and is sending the frame over SPI to the STM (looks good with a logical analyzer)

Now, I want to receive the frame (about 6KB) in the SPI port 2 on my STM32 to render on the RGB display.

Can you provide some hints or a simplified sample how do I setup my SPI port 2 to work on slave mode, and get a callback or any way to get the buffer once is transferred?

Thanks.

stevestrong
Posts: 1811
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Hard time to make SPI work in slave mode

Post by stevestrong » Wed Nov 15, 2017 7:02 pm

Here is a simple example how to setup the SPI 2 and the corresponding DMA Rx channel 4:

Code: Select all

#include <SPI.h>
#include <libmaple/dma.h>
#include "libmaple/spi.h"
...
/*****************************************************************************/
SPIClass SPI_2(2);
/*****************************************************************************/
void SPI_setup(void)
{
    // use SPI 2 for recording
    SPI_2.beginTransactionSlave(SPISettings(18000000, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT));
    spi_rx_dma_enable(SPI_2.dev()); // enable SPI Rx DMA
}
/*****************************************************************************/
#define RECORDS_PER_BUFFER (6*1024)
uint8_t data_buffer[RECORDS_PER_BUFFER];
/*****************************************************************************/
void DMA_Setup(void)
{
    dma_init(DMA1);	// init DMA clock domain
// DMA setup transfer for SPI2 Rx
    dma_channel dma_ch = DMA_CH4; // SPI2 Rx DMA channel is channel 4, see reference manual 13.3.7.
    dma_disable(DMA1, dma_ch);	// Disable the DMA tube.
    dma_setup_transfer(DMA1, dma_ch,
                    &((SPI_2.dev())->regs->DR), DMA_SIZE_8BITS,
                    data_buffer, DMA_SIZE_8BITS,
                    (DMA_MINC_MODE | DMA_CIRC_MODE | DMA_HALF_TRNS | DMA_TRNS_CMPLT)); // flags
    dma_set_num_transfers(DMA1, dma_ch, RECORDS_PER_BUFFER);
    dma_set_priority(DMA1, dma_ch, DMA_PRIORITY_VERY_HIGH);
    dma_attach_interrupt(DMA1, dma_ch, DMA_Rx_irq);	// attach an interrupt handler.
    dma_enable(DMA1, dma_ch);	// Enable the DMA tube. It will now begin serving requests.
}
/*****************************************************************************/
// variables
// set by HW when a complete DMA transfer was finished.
volatile uint8_t dma_irq_full_complete;
// set by HW when a DMA transfer is at its half.
volatile uint8_t dma_irq_half_complete;
/*****************************************************************************/
// This is our DMA interrupt handler.
/*****************************************************************************/
void DMA_Rx_irq(void)
{
    uint32_t dma_isr = dma_get_isr_bits(DMA1, DMA_CH4);
    if (dma_isr&DMA_ISR_HTIF1) {
        dma_irq_half_complete = 1; // signal for the main level
    }
    if (dma_isr&DMA_ISR_TCIF1) {
        dma_irq_full_complete = 1; // signal for the main level
    }
    dma_clear_isr_bits(DMA1, DMA_CH4);
}
If you call both setup function, the data_buffer will be ready to get filled when receiving bytes over SPI2.
There will be interrupts generated when the buffer is half and completely full.
You should check those flags on main level and process the data as needed.

Note: this example will not send any data from the slave to master.

EDIT
Added DMA enable line to SPI setup, and corrected SPI device in DMA setup transfer function.

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Wed Nov 15, 2017 8:24 pm

That is awesome, I'll try and let you know.

Thanks!!!

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Nov 16, 2017 1:21 am

Tonight I'll try this code, however where is the SS setup in the STM?

SPI on the master talk to a SD card and also to the STM, so the SPI bus is busy talking different slaves, I want to make sure that the DMA channel on the STM32 will be collecting the frame from the bus only when the SS (NSS2/PB12) is low.

How the dma channel knows? I don't see this in either of the setup methods.

stevestrong
Posts: 1811
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Hard time to make SPI work in slave mode

Post by stevestrong » Thu Nov 16, 2017 6:42 am

The SS pin is the standard allocated one for the selected SPI interface. So for SPI 2 it is PB12, for SPI 1 is PA4.
In slave mode the SPI will only receive data is SS pin is activated by the master, it is the default way of working.
The DMA is only triggered when new data comes in to SPI, so this is also automated by the hardware.

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Nov 16, 2017 7:36 am

I don't know what I'm missing I can't make the IRQ to trigger.

Checked the SPI2
PB12 -> CS
PB13 -> CLK
PB14 -> MISO
PB15 -> MOSI

I have the oscilloscope and the analyzer in the bus and everything looks fine, I simplified the master code just to send in repetition 32 bytes with the "Hello There" content.

Even on the STM32 I copied the method void REGS_info(void) from the samples and dump the content every second and I get the same:
DMA1.ISR: 0
DMA1.CCR2: 0
DMA1.CNDTR2: 0
SPI2.CR1: 440
SPI2.CR2: 0
SPI2.SR: 43

The interruption is never triggered, what else can I check?
SPIbus.png
SPIbus.png (101.75 KiB) Viewed 216 times

stevestrong
Posts: 1811
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Hard time to make SPI work in slave mode

Post by stevestrong » Thu Nov 16, 2017 11:37 am

Your monitored DMA channel was wrong, it should be channel 4 , registers CCR4 and CNDTR4.

But anyway, I have found a very important step missing in the setup flow, it is the enabling the SPI Rx DMA.
This should be done by inserting an additional line to SPI setup:

Code: Select all

#include "libmaple/spi.h"
...
void SPI_setup(void)
{
    // use SPI 2 for recording
    SPI_2.beginTransactionSlave(SPISettings(18000000, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT));
    spi_rx_dma_enable(SPI_2.dev()); // enable SPI Rx DMA
}
I have also corrected the SPI_2 device address in DMA setup transfer function call in the previously posted example.

I have tested the code and it works now.

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Nov 16, 2017 1:06 pm

It works !!!

Thanks a lot, now I'll start to fix all mess in code changes I did in master and slave trying o figure out.

One more question, how transmission errors are handled?

For example I expect 6144 (6x1024) bytes, however if for some reason not all of the bytes are coming the buffer won't be totally full and the main loop won't detect the full buffer, the SS will go high and the IRQ won't be triggered.

Now when the next frame comes, will the DMA channel start over from the beginning in the buffer or will continue where it left off.

If continue where it left in the previous frame then a single byte loss can trigger a massive mess chain reaction in the buffer.

How usually errors are dealt?
Basically I'm not concerned if I lose a frame, however I want to make sure for every SS low the DMA start to write from the beginning in the buffer.

stevestrong
Posts: 1811
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Hard time to make SPI work in slave mode

Post by stevestrong » Thu Nov 16, 2017 1:15 pm

You could monitor the SPI and DMA stats rgisters for errors, and if any occur, then re-init the DMA.

Alternatively, you could signal the start of the frame with a nother pin to trIgger an interrpt wich resets the DMA before starting the frame, before NSS goes low.

castortiu
Posts: 39
Joined: Tue Nov 07, 2017 8:34 am
Location: Seattle, WA

Re: Hard time to make SPI work in slave mode

Post by castortiu » Thu Nov 16, 2017 1:31 pm

Can I attach an interrupt to the falling edge of the SS pin?

On the master I could give several microseconds as a time buffer after SS go LOW and start to transmit the data, that time should be enough for the slave to get the SS IRQ and init the DMA buffer, that way I would not need an extra pin.

Post Reply