I'm trying to use an STM32F103C8T6 on a generic 'Blue Pill' type board as an SPI slave to a Raspberry Pi, but I just cannot get it to work at any reasonably high data rate. I've spent days trying all sorts of combinations, but in vain. I'm desperately hoping you guys can help me out
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 every second. My custom Linux kernel driver on the Pi has an interrupt handler installed 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.
I'm now wondering what could cause this problem. Granted, I'm only using SPI in polling mode, but surely that setup should still allow a baud rate beyond 10k? The connections between the STM32 and the Pi are cheap jumper wires directly connecting the pins (got rid of the breadboard in between), and I already tried different lengths (between 10 and 30cm). I have used the very same wires to connect an SPI ENC28J60 ethernet chip to the Pi, and saw speeds of over 3Mbit/s (on the network layers, so the underlying SPI data rate must have been even higher).
Does anyone have any idea what could be causing this problem? I feel like I must be making some stupid mistake, but I just cannot find it. I've tried for days now, testing tons of different SPI setup variations, but to no avail.
I'm using the STM32 Core (As far as I understood, it should support generic boards as well?), and am using the HAL library for setting up the SPI slave, since no Arduino library is included in the core for that. I'm programming and monitoring the STM32 from a separate MacBook Pro (not from the Raspberry Pi), and am using Arduino 1.8.10 (newer versions crash on my multi-monitor macOS system as soon as I open the serial monitor). The STM32F103 is running its preinstalled bootloader, and I'm programming the board via Serial (using the recommended STM32CubeProgrammer method).
Here's my code (stripped of error handling for brevity):
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, SPI_MESSAGE_SIZE, 1000)) != HAL_OK) {
Serial.println("Receiving failed!");
}
Serial.println(spi_buffer);
delay(1000);
}
10k baud (most of the time):
100k baud: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!
…
…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!
Pin connections:
(STM32 GND, A9 and A10 are also connected to a separate computer via a USB UART adapter for programming and serial monitor)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
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);
}
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";
};
};
};
};