Adafruit-GFX-Library icon indicating copy to clipboard operation
Adafruit-GFX-Library copied to clipboard

SPI peripheral mapping not working on some RP2040 based boards

Open caternuson opened this issue 7 months ago • 4 comments

This forum poster was trying to get a Feather RP2040 CAN working with a TFT FeatherWing: https://forums.adafruit.com/viewtopic.php?t=217465

Dealing with ensuring the various CS pins were being handled properly worked to a certain point, however, it was not possible to get a full working example using both CAN and TFT at the same time. I tried to work up an example and ran into a similar issue. Drilling into things, it seems like an issue with some conditional mapping being done to set up the SPI peripheral being used. There are numerous instances of this code: https://github.com/adafruit/Adafruit-GFX-Library/blob/87e15509a9e16892e60947bc4231027882edbd34/Adafruit_SPITFT.cpp#L1034-L1035

However, it seems the philhower core maps spi0 and spi1 to the RP2040's SPI0 and SPI1 peripherals. The Feather RP2040 CAN's default SPI pins use the SPI1 peripheral:

Image

also: https://github.com/earlephilhower/arduino-pico/blob/e05dd50d626d2871ab988d8e72aa7c12e18e603c/variants/adafruit_feather_can/pins_arduino.h#L32-L37

So it seems like that tertiary logic ends up mapping to spi0 instead of spi1 as needed.

Doing a quick hack and changing all instances of:

spi_inst_t *pi_spi = hwspi._spi == &SPI ? spi0 : spi1;

to:

spi_inst_t *pi_spi = spi1;

resolves the issue.

So the problem is known, but at this point unclear what a good code fix would be.

Example test sketch:

/*
 * TFTFeatherWing + Feather RP2040 CAN Example
 */
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_MCP2515.h>

#define MCP_CS PIN_CAN_CS   // CAN
#define STMPE_CS 6          // touch
#define TFT_CS   9          // TFT
#define TFT_DC   10
#define SD_CS    5          // SD
   
#define CAN_BAUDRATE (250000)

// CAN
Adafruit_MCP2515 mcp(MCP_CS);

// TFT
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

void setup() {
  Serial.begin(115200);
  while(!Serial) delay(10);

  Serial.println("TX Feather RP2040 CAN + TFT FeatherWing test!");

  // force disable all SPI things
  pinMode(MCP_CS, OUTPUT); digitalWrite(MCP_CS, HIGH);
  pinMode(STMPE_CS, OUTPUT); digitalWrite(STMPE_CS, HIGH);
  pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH);
  pinMode(TFT_CS, OUTPUT); digitalWrite(9, HIGH); 

  Serial.println("CAN init");
  if (!mcp.begin(CAN_BAUDRATE)) {
    Serial.println("Error initializing MCP2515.");
    while(1) delay(10);
  }
  Serial.println("MCP2515 chip found");

  Serial.println("TFT init");
  tft.begin();
  uint8_t x = tft.readcommand8(ILI9341_RDMODE);
  Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDMADCTL);
  Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDPIXFMT);
  Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDIMGFMT);
  Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDSELFDIAG);
  Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);

  // fill color pattern
  tft.fillScreen(ILI9341_RED);
  delay(5000);
  tft.fillScreen(ILI9341_GREEN);
  delay(5000);
  tft.fillScreen(ILI9341_BLUE);
  delay(5000);

  Serial.println("START DONE");
}

void loop() {
  // send packet: id is 11 bits, packet can contain up to 8 bytes of data
  Serial.print("Sending packet ... ");

  mcp.beginPacket(0x12);
  mcp.write('h');
  mcp.write('e');
  mcp.write('l');
  mcp.write('l');
  mcp.write('o');
  mcp.endPacket();

  Serial.println("done");

  delay(1000);

  // send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
  Serial.print("Sending extended packet ... ");

  mcp.beginExtendedPacket(0xabcdef);
  mcp.write('w');
  mcp.write('o');
  mcp.write('r');
  mcp.write('l');
  mcp.write('d');
  mcp.endPacket();

  Serial.println("done");

  delay(1000);
}

CURRENT BEHAVIOR: Image

BEHAVIOR WITH HACK FIX: Image

caternuson avatar Apr 09 '25 16:04 caternuson

Here's the related PR that brought in these changes: https://github.com/adafruit/Adafruit-GFX-Library/pull/360

caternuson avatar Apr 09 '25 17:04 caternuson

grepping in the variants subfolder of the RP2040 BSP for boards that map spi1 to __SPI0_DEVICE:

$ grep -r __SPI0_DEVICE | grep spi1
adafruit_feather_thinkink/pins_arduino.h:#define __SPI0_DEVICE  spi1
adafruit_feather_prop_maker/pins_arduino.h:#define __SPI0_DEVICE               spi1
amken_revelop_es/pins_arduino.h:#define __SPI0_DEVICE     spi1
adafruit_feather_dvi/pins_arduino.h:#define __SPI0_DEVICE  spi1
adafruit_metro_rp2350/pins_arduino.h:#define __SPI0_DEVICE   spi1
adafruit_feather_scorpio/pins_arduino.h:#define __SPI0_DEVICE  spi1
adafruit_feather_rfm/pins_arduino.h:#define __SPI0_DEVICE  spi1
adafruit_feather_can/pins_arduino.h:#define __SPI0_DEVICE     spi1
adafruit_feather_usb_host/pins_arduino.h:#define __SPI0_DEVICE   spi1
silicognition_rp2040_shim/pins_arduino.h:#define __SPI0_DEVICE  spi1
sparkfun_iotnode_lorawanrp2350/pins_arduino.h:#define __SPI0_DEVICE spi1
adafruit_feather_adalogger/pins_arduino.h:#define __SPI0_DEVICE  spi1

This issue would affect all of those boards.

caternuson avatar Apr 09 '25 17:04 caternuson

This works for the CAN Feather, but not a universal fix since not all boards have these defines:

    spi_inst_t *pi_spi = hwspi._spi == &SPI ? __SPI0_DEVICE : __SPI1_DEVICE;

The BSP has defaults, but in .cpp file: https://github.com/earlephilhower/arduino-pico/blob/e05dd50d626d2871ab988d8e72aa7c12e18e603c/libraries/SPI/src/SPI.cpp#L419-L424

caternuson avatar Apr 09 '25 18:04 caternuson

did this get resolved with the PR?

ladyada avatar May 04 '25 16:05 ladyada