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
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";
};
};
};
};
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);
}
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!
…