SPI Flash on RedBoard Turbo Arduino

In March I asked Sparkfun support about using the RedBoard Turbo SPIFlash memory from Arduino code.

This is my solution to the problem. I have used as a reference code for the AdaFruit M0 boards such as ItsyBitsy that also have SPIFlash but these boards use a diffferent chip and different ports. However the Adafruit code was a good starting point.

The RedBoard Turbo has a 4Mbit flash chip W25Q32FV connected on a SPI port set up on SerCom 5. To enable this in Arduino it is necessary to edit variant.h and variant.cpp

variant.cpp: add the following to the array PinDescription g_APinDescription as the last entries, these will be entries 45,46,47 and 48 , you will see the index numbers reflected in the edit for the variant.h code below.

/*
 45-47 Access FLASH via SPI on Sercom 5 for Sparkfun RedBoard Turbo
 The D21 datasheet shows:
PB22 SERCOM5/ PAD[2] ALT //MOSI
PB03 SERCOM5/ PAD[1] ALT //MISO
PB23 SERCOM5/ PAD[3] ALT //SCK
48 will be the chip select pin 
*/
  { PORTB,  3, PIO_SERCOM_ALT, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // 45 MISO : SERCOM5/PAD[1]
  { PORTB, 22, PIO_SERCOM_ALT, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // 46 MOSI : SERCOM5/PAD[2]
  { PORTB, 23, PIO_SERCOM_ALT, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // 47 SCK : SERCOM5/PAD[3]    
  { PORTA, 13, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_13 } //48 CS - not on SERCOM

As we need a second SPI port the following edit is needed in variant.h:

/*
 * SPI Interfaces
 */
//#define SPI_INTERFACES_COUNT 1
#define SPI_INTERFACES_COUNT 2

#define PIN_SPI_MISO         (22u)
#define PIN_SPI_MOSI         (23u)
#define PIN_SPI_SCK          (24u)
#define PERIPH_SPI           sercom4
#define PAD_SPI_TX           SPI_PAD_2_SCK_3
#define PAD_SPI_RX           SERCOM_RX_PAD_0

static const uint8_t SS	  = PIN_A2 ;	// SERCOM4 last PAD is present on A2 but HW SS isn't used. Set here only for reference.
static const uint8_t MOSI = PIN_SPI_MOSI ;
static const uint8_t MISO = PIN_SPI_MISO ;
static const uint8_t SCK  = PIN_SPI_SCK ;

/* based on ItsyBitsy M0 but pin numbers for RedTurbo variant.cpp as extended for SPIFlash*/

#define PIN_SPI1_MISO         (45u)
#define PIN_SPI1_MOSI         (46u)
#define PIN_SPI1_SCK          (47u)
#define PERIPH_SPI1           sercom5
#define PAD_SPI1_TX           SPI_PAD_2_SCK_3
#define PAD_SPI1_RX           SERCOM_RX_PAD_1

static const uint8_t SS1   = 48 ;	// HW SS isn't used. Set here only for reference.
static const uint8_t MOSI1 = PIN_SPI_MOSI ;
static const uint8_t MISO1 = PIN_SPI_MISO ;
static const uint8_t SCK1  = PIN_SPI_SCK ;

With these changes I used the SPIMemory library https://github.com/Marzogh/SPIMemory

There are various example programs - I used FlashDiagnostics.ino. Add the following to ensure that the USB serial can be used.

#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
// Required for Serial on Zero based boards
#define Serial SERIAL_PORT_USBVIRTUAL
#endif

Also configure the SPI port for the SPIFlash memory

//SPIFlash flash;
//SPIFlash flash(11);
SPIFlash flash(SS1, &SPI1);       //Use this constructor if using an SPI bus other than the default SPI. Only works with chips with more than one hardware SPI bus

Running the diagnostics program gave the following output:

Initialising..........

SPIMemory Library version: 3.3.0

JEDEC ID: 0xEF4016
Man ID: 0xEF
Memory ID: 0x40
Capacity: 4194304
Max Pages: 16384
Unique ID: 1569669708200192, 0x6854751B355F3900
-----------------------------------------------------------------------------------------------------------------------------
							Testing library code
-----------------------------------------------------------------------------------------------------------------------------
			Function		Test result			     Runtime
-----------------------------------------------------------------------------------------------------------------------------
			Power Down		   PASS				      0 us
			Power Up		   PASS				      0 us

			Erase Chip		   PASS				      0 us
			Erase 72KB		   PASS				      0 us
			Erase 64KB		   PASS				      0 us
			Erase 32KB		   PASS				      0 us
			Erase 4KB		   PASS				      0 us
-----------------------------------------------------------------------------------------------------------------------------
			Data type		I/O Result	      Write time	      Read time
-----------------------------------------------------------------------------------------------------------------------------
			Byte			   PASS				      0 us
			Char			   PASS				      0 us
			Word			   PASS				      0 us
			Short			   PASS				      0 us
			ULong			   PASS				      0 us
			Long			   PASS				      0 us
			Float			   PASS				      0 us
			Struct			   PASS				      0 us
			Byte Array		   PASS				      0 us
			String			   PASS				      0 us
-----------------------------------------------------------------------------------------------------------------------------
To see function runtimes ncomment RUNDIAGNOSTIC in SPIMemory.h.

With some editing it is feasible to use the Adafruit SPI Flash FatFs library. The parameters for the larger memory need to be added and a new class created for the different chip at the end of Adafruit_SPIFLash_FatFs.h The Winbond commands are the same.

// addition for W25Q32FV flash as used on Sparkfun RedTurbo//

class Adafruit_W25Q32FV_FatFs: public Adafruit_SPIFlash_FatFs {
public:
  Adafruit_W25Q32FV_FatFs(Adafruit_SPIFlash& flash):
    Adafruit_SPIFlash_FatFs(flash, 4096)
  {}

protected:
  virtual uint32_t _flashSectorBase(uint32_t address) {
    // Upper 12-bits of address are the base (start) of the sector for this
    // address.
    return address & 0xFFF000;
  }

  virtual uint32_t _flashSectorOffset(uint32_t address) {
    // Lower 12-bits of address are the offset into a sector for the specified
    // address.
    return address & 0x000FFF;
  }
};

class SparkFun_RedTurbo_CircuitPython: public Adafruit_W25Q32FV_FatFs {
public:
  SparkFun_RedTurbo_CircuitPython(Adafruit_SPIFlash& flash):
    Adafruit_W25Q32FV_FatFs(flash)
  {}

  // Override block read and write functions to add synthesized MBR (block 0).
  virtual DRESULT diskRead(BYTE *buff, DWORD sector, UINT count);
  virtual DRESULT diskWrite(const BYTE *buff, DWORD sector, UINT count);

protected:

  virtual uint32_t _fatSectorCount() {
    // Remove a 4kb flash sector from the fat sector count because CircuitPython
    // reserves one flash sector for a cache.
    return (_flash.pageSize()*_flash.numPages()-4096)/_fatSectorSize;
  }

  virtual uint32_t _fatSectorAddress(uint32_t sector) {
    // Offset the sector by 1 as the last flash sector is actually an unused
    // cache for writes in CircuitPython.
    return (sector-1)*_fatSectorSize;
  }
};

I hope this is of help.

Ted Finch

Hi Ted,

Awesome write-up! I am sure this will help other users trying to take advantage of that flash memory on the RedBoard Turbo. Thanks for sharing!

These instructions need to be added to the Turbo Hookup Guide, or at least have a link to it posted there so one doesn’t have to look for it. Otherwise it runs the risk of just being asked again, like I was about to do before I found this thread.

Thanks for this SPI Flash code update. I see the Sparkfun RedBoard Turbo variant files on GitHub but I don’t see those files in my computer’s Arduino APP (Mac OS Content) or Arduino user folder. I can make my own variant files with those SPI Flash code changes, but where do those files need to be placed on a Mac so Arduino will properly use them for the RedBoard?

Somewhat related question is that the RedBoard Turbo has option for 3 SPIs but default variant files only support one.

SPI on D11, D12, D13 header pins

SPI on ISP header pins

SPI Flash (board only, no header pins)

Is there a version of the variant files that supports all three?

Thanks for your help !

For tfcroft4 solution for using Flash SPI, I think the MOSI1, MISO1, and SCK1 const values should point to the SPI1 pins as follows (instead of SPI pins in the original code):

static const uint8_t MOSI1 = PIN_SPI1_MOSI ;

static const uint8_t MISO1 = PIN_SPI1_MISO ;

static const uint8_t SCK1 = PIN_SPI1_SCK ;

Here are suggested code changes for variant.cpp and variant.h to add SPI2 bus that uses D10 (SS2), D11 (MOSI2), D12 (MISO2), and D13 (SCK2) pins for the Sparkfun Redboard Turbo (per its Graphical Datasheet). Adding SPI2 does mean that one loses the Blue LED control (on D13).

I could use some help / guidance on how to install the updated variant.cpp and variant.h on a Mac so that Arduino will properly use them for the Redboard Turbo.

The following code is for changing the Pin 10-13 definitions in variant.cpp.

// *****  SPI2 definitions for Pins 10-13 for Sparkfun Redboard Turbo
/*
  { PORTA, 18, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM3_CH0, TC3_CH0, EXTERNAL_INT_2 }, // TC3/WO[0]
  { PORTA, 16, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_0 }, // TCC2/WO[0]
  { PORTA, 19, PIO_TIMER_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), No_ADC_Channel, PWM0_CH3, TCC0_CH3, EXTERNAL_INT_3 }, // TCC0/WO[3]

  // 13 (LED)
  { PORTA, 17, PIO_PWM, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM2_CH1, TCC2_CH1, EXTERNAL_INT_1 }, // TCC2/WO[1]
 */
  { PORTA, 18, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, // 10 SPI2 CS (SS), not on SERCOM
  { PORTA, 16, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // 11 MOSI2 : SERCOM1/PAD[0]
  { PORTA, 19, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // 12 MISO2 : SERCOM1/PAD[3]
  { PORTA, 17, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // 13 SCK2 : SERCOM1/PAD[1]
// ***** SPI2 End

The following code adds the SPI2 to variant.h. An additional change in variant.h is to update the define for SPI_INTERFACES_COUNT to be 3 (assuming one includes the SPI1 for the Flash).

// ***** SPI2:  Use D10, D11, D12, D13 for SPI2 bus on Sparkfun Redboard Turbo
#define PIN_SPI2_MISO         (12u)
#define PIN_SPI2_MOSI         (11u)
#define PIN_SPI2_SCK          (13u)
#define PERIPH_SPI2           sercom1
#define PAD_SPI2_TX           SPI_PAD_0_SCK_1
#define PAD_SPI2_RX           SERCOM_RX_PAD_3

static const uint8_t SS2   = 10 ;	// heritage Arduino SS is D10
static const uint8_t MOSI2 = PIN_SPI2_MOSI ;
static const uint8_t MISO2 = PIN_SPI2_MISO ;
static const uint8_t SCK2  = PIN_SPI2_SCK ;
// ***** SPI2 end

The place to put the updated variant.cpp and variant.h files on a Mac is in:

/Users/USERNAME/Library/Arduino15/packages/SparkFun/hardware/samd/1.6.1/variants/SparkFun_RedBoard_Turbo/

I cannot get the code to compile with out taking the comments off of the //Uart Serial from with in the variant.cpp file

Uart Serial1( &sercom0, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX ) ;
//Uart Serial( &sercom5, PIN_SERIAL_RX, PIN_SERIAL_TX, PAD_SERIAL_RX, PAD_SERIAL_TX ) ;
void SERCOM0_Handler()
{
  Serial1.IrqHandler();
}

//void SERCOM5_Handler()
//{
//  Serial.IrqHandler();
//}

OK - I need help the piece compiles and I follow the paths but all I get is

Initialising…

SPIMemory Library version: < 2.5.0

No comms. Check wiring. Is chip supported? If unable to fix, raise an issue on Github