One often requested feature is to remap SPI1 from D2, D3, D4 to other pins. The most common reason is that pins D3 and D4 are used by the Ethernet FeatherWing, and are not easily reconfigured. This prevents using the SPI1 secondary SPI interface when using Ethernet.
As it turns out, you can map most ports on the nRF52840 on Gen 3 devices (Argon, Boron, Xenon) from user firmware!
This was also made worse because prior to Device OS 1.5.0, you could not share the Primary SPI interface used by the Ethernet FeatherWing with other SPI peripherals. With newer Device OS, you can, but sometimes you still might want to have two separate SPI busses.
Here’s the Ethernet FeatherWing with a Xenon (no mesh configured), connected and breathing cyan with Device OS 1.4.2, while a LIS3DH accelerometer is connected to SPI1 on alternative pins!
All of the magic is in the reconfigureSpi()
function:
void reconfigureSpi() {
// This is the magic for reconfiguring SPI.
// This code only works on Gen 3 devices (Argon, Boron, Xenon, B Series SoM)
// This is what we want to remap to, but you can use different pins.
// SPI Pin Hardware Pin
// SCK A0 P0.3
// MOSI A1 P0.4
// MISO A2 P0.28
// The pin mapping table is handy for finding the hardware pin numbers:
// https://docs.particle.io/reference/hardware/pin-info/?m=table&sort=num
// You must bring up SPI1 on the original pins first, because otherwise SPI1.begin() will
// overwrite the reconfiguration and revert it back to the old pins.
SPI1.begin();
// SCK and MOSI need to be configured as OUTPUT. MISO is INPUT.
pinMode(A0, OUTPUT); // SCK
pinMode(A1, OUTPUT); // MOSI
pinMode(A2, INPUT); // MISO
// CS is configured above in the LIS3DHSPI object construction. It doesn't affect pin reconfiguration.
// We reconfigure SPI1, which is nRF52 SPIM2. The addresses are in the nRF52 Product Specification.
uint8_t *pBase = (uint8_t *)0x40023000;
// ENABLE offset 0x500
uint32_t *pENABLE = (uint32_t *)&pBase[0x500];
// PSEL.SCK offset 0x508
uint32_t *pPSEL_SCK = (uint32_t *)&pBase[0x508];
// PSEL.MOSI offset 0x50c
uint32_t *pPSEL_MOSI = (uint32_t *)&pBase[0x50c];
// PSEL.MISO OFFSET 0x510
uint32_t *pPSEL_MISO = (uint32_t *)&pBase[0x510];
// Standard pin config for SPI1
// SCK D2 P1.01
// MOSI D3 P1.02
// MISO D4 P1.08
// Disconnect the old pins
*pPSEL_SCK = pselConfig(false, 1, 1); // D2
*pPSEL_MOSI = pselConfig(false, 1, 2); // D3
*pPSEL_MISO = pselConfig(false, 1, 8); // D4
// Restore the old pins back to INPUT mode (MISO was already input)
pinMode(D2, INPUT); // SCK
pinMode(D3, INPUT); // MOSI
// Disable SPIM
*pENABLE = 0;
// Reconnect to the new pins
*pPSEL_SCK = pselConfig(true, 0, 3); // A0
*pPSEL_MOSI = pselConfig(true, 0, 4); // A1
*pPSEL_MISO = pselConfig(true, 0, 28); // A2
// Reenable SPIM
*pENABLE = 0x7;
// Log.info("SCK=%lx MOSI=%lx MISO=%lx", *pPSEL_SCL, *pPSEL_MOSI, *pPSEL_MISO);
}
This is unsupported and may break in the future. Beware!
Find out more here: