Skip to content

Marlin SPI Review

Victor Oliveira edited this page Sep 21, 2020 · 11 revisions

Marlin SPI Review

Marlin born with a few devices using SPI. But now, we have a lot of concurrent devices sharing SPI.

For one SPI device work, it needs prepare a spi port with ALL this parameters, before each spi usage:

  • SPI PORT
  • Speed
  • Bit Order
  • Data Mode

A lot of current Marlin code rely upon HAL to configure the SPI. But it leads to a problem: the current HAL code only handles ONE SPI PORT at time. So, the client code must ensure that the SPI is properly configured before the use and the corret SPI PORT is selected.

This review try to search for every spi usage to check if it's doing all the SPI setup before the use.

Marlin Core

Sd2Card.cpp - SD using SPI (~OK)

uses spiXXXX functions available in HAL

call spiInit before use the spi

!!! rely on the spiInit to configure ALL spi paramenters for SD !!!

USB_FLASH_DRIVE_SUPPORT / MAX3421e (NOK)

uses spiXXXX functions available in HAL

call spiBegin+spiInit ONLY ONCE in the init of the code

!!! don't call any begin or init to send/recv data !!!

UHS_host/USB_HOST_SHIELD/USB_HOST_SHIELD.h (~OK)

use global SPI object. Have its own SPISettings instance and use beginTransaction. (good)

!!! rely on global SPI object to select the correct SPI PORT !!!

Temperature sensor: MAX6675 (~OK)

uses spiXXXX functions available in HAL

call spiBegin+spiInit before use the spi

!!! rely on the spiInit to configure ALL spi paramenters !!!

TFDI eve touch ui (~OK)

rely on the spiInit to configure the correct spi paramenters (media player)

use global SPI object. Have its own SPISettings instance and use beginTransaction. (good)

!!! rely on global SPI object to select the correct SPI PORT !!!

Marlin/src/HAL/STM32_F4_F7/STM32F7/TMC2660.cpp (OK)

Uses its own SPIClass instance

TFT (OK??)

After the refactoring, all TFT and TOUCH io will be using its own SPIClass instance

W25QXX (~OK)

Use global SPI object. Have its own init method that setup ALL spi parameters.

!!! it change the global SPI object and may conflit with others using that same object !!!

Stepper.cpp -> digipot_init (NOK NOK)

Use global SPI object.

Don't have its own configs.

Call SPI.begin only once in the init!

Don't call begin before spi usage.

!!! Rely totally in the CURRENT global SPI instance !!!

Marlin/src/feature/dac/dac_dac084s085.cpp (NOK)

use spiXXX functions.

Don't call spiInit, only spiBegin

Call SPI.begin only once in the init!

Don't call begin before spi usage.

!!! don't call any begin or init to use SPI !!!

LPC1768

u8g_com_HAL_LPC1768_hw_spi_fn (NOK)

uses spiXXXX functions available in HAL

call spiBegin+spiInit ONLY ONCE in the init of the LCD setup

!!! don't call any begin or init to send data to LCD !!!

u8g_com_HAL_LPC1768_ST7920_hw_spi_fn (NOK)

uses spiXXXX functions available in HAL

call spiBegin+spiInit ONLY ONCE in the init of the LCD setup

!!! don't call any begin or init to send data to LCD !!!

DUE

spiBegin calls spiInit in DUE

u8g_com_HAL_DUE_shared_hw_spi.cpp (NOK)

uses spiXXXX functions available in HAL

call spiBegin+spiInit ONLY ONCE in the init of the LCD setup

!!! don't call any begin or init to send data to LCD !!!

STM32

  • spiRec/spiRead/spiSend/etc call beginTransaction for every byte
  • !!! client code must call begin/end accordingly !!!

STM32_F4_F7

  • spiRec/spiRead/spiSend/etc call beginTransaction for every byte
  • !!! client code must call begin/end accordingly !!!

TEENSY31_32

  • spiRec/spiRead/spiSend/etc call beginTransaction for every byte
  • !!! client code must call begin/end accordingly !!!

TEENSY35_36

  • spiRec/spiRead/spiSend/etc call beginTransaction for every byte
  • !!! client code must call begin/end accordingly !!!

TEENSY40_41

  • spiRec/spiRead/spiSend/etc call beginTransaction for every byte
  • !!! client code must call begin/end accordingly !!!

Libs

U8glib-HAL (~OK)

use global SPI object. Have its own SPISettings instance and use beginTransaction. (good)

Call beginTransaction for every byte in some cases. It could call beginTransaction in the SELECT command.

!!! rely on global SPI object to select the correct SPI PORT !!!

TMCStepper (~OK)

use global SPI object. Have its own SPISettings instance and use beginTransaction. (good)

!!! rely on global SPI object to select the correct SPI PORT !!!

HAL status

As a lot of code rely upon spiInit function, here is the status of that function in each HAL.

!!! Note: the current spiXXXX functions ONLY works with ONE SPI PORT !!!

STM32F1 (~OK)

spiInit function configure ALL spi parameters to the HAL default.

!!! problem: spiXXXX functions alter the SPI global object

LPC1768

Conclusions

The most commom problem is rely in spiInit and the global SPI instance. It may break all the codes that rely in this methods, if we have two peripheral using diferent SPI PORTs, both changing the global spi config.

There're 3 or 4 places that are very problematics. But the main issue is about the current selected SPI PORT.

It's working now because seems all most common marlin setups uses the same SPI PORT for the active peripheral. And it may explain the random complains about SD or other spi peripheral not working in some users setups.

For example: SD using SPI 1 and TMC (or U8Glib) using SPI 2 will not work, because the global SPI instance will point only to the SD spi, as the TMC/U8GLib don't change the SPI PORT when using it...

In resume, we have 3 types of problems:

1 - Wrong/missing/incomplete/unneeded begin/init calls (high problematic)

2 - Almost no handling of different SPI PORT (medium)

3 - We have 2 APIs to use SPI: SPIClass (SPI global object) and spiXXXX functions. It lead to a very confusing code and errors. (low)

How to Fix

Having in mind that we cannot just change everything once and completely break Marlin, I suggest the following action plan:

Objective 1: Remove all the spiXXXX functions in favor of the SPIClass.

Objective 2: Every fix for begin/init calls should be done using SPIClass

Objective 3: Enhance the interface of the SPIClass

Objective 4: Implement SPIClass for all HALs (we can start using the spiXXX funcions for that)

Objective 5: Remove the global SPI instance, only enabling it for lib compatibilty.