fd_
Posts: 93
Joined: Thu Oct 25, 2018 7:35 am

SPI data loss at 100k baud

Tue May 19, 2020 8:00 pm

Hi,
I'm trying to write a custom kernel module for communicating with a microcontroller via SPI. Specifically, I'm now using an STM32F103C8T6 microcontroller on a cheap 'Blue Pill' type board. The Raspberry Pi is the SPI master, the STM32 is the slave.

In the most basic setup, the STM32 requests an SPI message from the Raspberry Pi by setting an extra GPIO pin ("Request Pin") to high (3.3V) once per second. The kernel driver has an interrupt handler registered on that Request Pin that sends an SPI message containing the string 'Hello!' to the STM32. My kernel module registers as an SPI driver, and a device tree overlay links the driver to SPI0 on the Pi.

Strangely, this only works at very low baud rates: At spi-max-frequency = <10000> (in the dto), the STM32 can read the SPI message. However, when I use spi-max-frequency = <100000>, the STM32 just receives 'H!' most of the time, so the majority of the message's content gets lost. Sometimes, it even fails at 10k baud already.

From adding a printk call to the Pi interrupt handler, I know that the Request Pin is working correctly. (Also, that means the driver and overlay are loaded correctly). So the problem must be burried somewhere in the SPI code / setup.

I'm now wondering what could cause this problem. The STM32 runs at 72MHz (clocks are configured by stm32duino), so it should definitely be capable of keeping up with SPI data at 100k baud. The connections between the STM32 and the Pi are jumper wires directly connecting the pins (got rid of the breadboard in between), and I already tried different lengths (between 10 and 30cm). At one point, it seemed like the 20cm wires fixed the problem (only minor data loss at 4M baud), but the problem quickly reappeared after trying even shorter wires, and retrying the 20cm didn't work again. Still, I wonder could it be something as simple as bad-quality cables? Also, the very same wires work perfectly fine with an ENC28J60 connected via SPI, at 3.72 Mbit/s !

I'm compiling and using my device tree overlay with:

Code: Select all

dtc -Idts -O dtb -o my_spi_test.dtbo my_spi_test.dts -W no-unit_address_vs_reg
sudo dtoverlay my_spi_test.dtbo
Does anyone have any idea what could be causing this problem? I feel like I must be making a stupid mistake somewhere! I'm hoping someone here can point me in the right direction. I'm thankful for any hints you have for me!

Pin connections:
Raspberry Pi -> STM32
GPIO 8 (CE0) -> A4 (SPI1 NSS)
GPIO 11 (SCLK) -> A5 (SPI1 SCK)
GPIO 9 (MISO) -> A6 (SPI1 MISO)
GPIO 10 (MOSI) -> A7 (SPI1 MOSI)
GPIO 27 (Request Pin) -> B8
3V3 (Pin 1) -> 3.3
GND (Pin 6) -> G
(STM32 GND, A9 and A10 are also connected to a separate computer via a USB UART adapter for programming and serial monitor)

Kernel module excerpts:

Code: Select all

static char *hello = "Hello!";

void send_spi_message(void) {
    spi_write(spi_dev, hello, strlen(hello));
}

static int spi_test_probe(struct spi_device *spi_dev) {
    request_pin = of_get_named_gpio_flags(spi_dev->dev.of_node, "request_pin", 0, 0);
    int request_pin_req = devm_gpio_request(&spi_dev->dev, request_pin, "request");
    request_pin_irq = gpiod_to_irq(gpio_to_desc(request_pin));
    request_threaded_irq(request_pin_irq, send_spi_message, NULL, IRQF_TRIGGER_RISING, "request", spi_dev);
}

static struct spi_driver my_spi_test_driver = {
    .driver = {
        .name = "my_spi_test",
    },
    .probe = my_spi_test_probe,
    .remove = my_spi_test_remove
};

static int __init my_spi_test_init(void) {
    return spi_register_driver(&my_spi_test_driver);
}
Device Tree Overlay:

Code: Select all

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709";

	fragment@0 {
		target = <&spidev0>;
		__overlay__ {
			status = "disabled";
		};
	};

	fragment@1 {
		target = <&spi0>;
		__overlay__ {
			#address-cells = <1>;
			#size-cells = <0>;
			status = "okay";

			spi_test@0 {
				compatible = "my_spi_test";
				reg = <0>;
				request-pin = <&gpio 27 0>; // Active high
				spi-max-frequency = <100000>;
                status = "okay";
			};
		};
	};
};
STM32 code (I’m using stm32duino):

Code: Select all

#include "stm32f1xx.h"

#define REQUEST_PIN PB8
#define SPI_MESSAGE_SIZE 7

char spi_buffer[SPI_MESSAGE_SIZE];
SPI_HandleTypeDef spi;

void setup_spi() {   
    HAL_Init();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_SPI1_CLK_ENABLE();

    spi.Instance = SPI1;
    spi.Init.Mode = SPI_MODE_SLAVE; 
    spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    spi.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
    spi.Init.CLKPhase = SPI_PHASE_1EDGE;
    spi.Init.CLKPolarity = SPI_POLARITY_LOW;
    spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    spi.Init.DataSize = SPI_DATASIZE_8BIT;
    spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
    spi.Init.NSS = SPI_NSS_SOFT;
    spi.Init.TIMode = SPI_TIMODE_DISABLE;
    spi.Init.CRCPolynomial = 10;
    HAL_SPI_Init(&spi);

    GPIO_InitTypeDef  GPIO_InitStruct;
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    __HAL_SPI_ENABLE(&spi);
}

void setup() {
    setup_spi();
    pinMode(REQUEST_PIN, OUTPUT);
    digitalWrite(REQUEST_PIN, LOW);
    Serial.begin(9600);
    Serial.println("Started");
}

void loop() {
    digitalWrite(REQUEST_PIN, HIGH);
    delay(1);
    digitalWrite(REQUEST_PIN, LOW);

    HAL_StatusTypeDef result;
    if ((result = HAL_SPI_Receive(&spi, (uint8_t *)spi_buffer, 6, 1000)) != HAL_OK) {
       Serial.println("Receiving failed!");
    }
    
    Serial.println(spi_buffer);

    delay(1000);
}
STM32 Serial output:

Code: Select all

10k baud (most of the time):
21:50:53.401 -> Hello!
21:50:54.385 -> Hello!
21:50:55.428 -> Hello!
21:50:56.447 -> Hello!
21:50:57.449 -> Hello!
21:50:58.464 -> Hello!
21:50:59.465 -> Hello!
21:51:00.481 -> Hello!
…

Code: Select all

100k baud:
21:22:30.832 -> Receiving failed!
21:22:30.832 -> H!
21:22:31.851 -> Receiving failed!
21:22:31.851 -> H!
21:22:33.856 -> Receiving failed!
21:22:33.856 -> H!
21:22:35.834 -> Receiving failed!
21:22:35.834 -> H!
21:22:37.856 -> Receiving failed!
21:22:37.856 -> H!
…

fd_
Posts: 93
Joined: Thu Oct 25, 2018 7:35 am

Re: SPI data loss at 100k baud

Sat May 23, 2020 10:44 am

I finally found the problem, and as I was suspecting, it indeed was a stupid mistake from my part:

In the kernel modules, I was registering the interrupt for the rising edge, although it should have been the falling edge. While the Raspberry Pi already started sending out data over SPI, the STM32 still had to wait 1ms, set the request pin low and start listening on the SPI.

More details in the stm32duino forums: https://www.stm32duino.com/viewtopic.php?f=39&t=434

Return to “Interfacing (DSI, CSI, I2C, etc.)”