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