Detect header W25Q16 on unified RAK firmware
This commit is contained in:
1 parent
cecddf4bae
commit
9127f8fe25
11 files changed
+111
-8
No files matched your search
@@ -334,6 +334,14 @@ occupied. See the
|
||||
[nRF52 QSPI guide](ota_nrf52_qspi.md#one-w25q16-wiring-for-rak4631-or-rak3401--rak13302)
|
||||
before installing either image.
|
||||
|
||||
For a build that uses only the 2.54 mm headers, use the unified
|
||||
`RAK_4631_repeater_unified_lora_ota` or
|
||||
`RAK_3401_repeater_unified_lora_ota` image with its matching adaptive
|
||||
OTAFIX 2.4.8 bootloader. Wire `CLK` to J10 `TX1`, `DO` to J10 `RX1`, `DI` to
|
||||
J11 `IO1`, and `CS` to J11 `AIN1`; keep the `CS` pull-up and J12 power wiring.
|
||||
This gives up UART1 GPS/PPS but leaves I2C sensors and the RAK3401 radio
|
||||
available. See the [header-only wiring table](ota_nrf52_qspi.md#header-only-w25q16-wiring-for-the-unified-rak-images).
|
||||
|
||||
### 4. Build and check the in-place delta
|
||||
|
||||
```bash
|
||||
|
||||
@@ -143,6 +143,30 @@ core:
|
||||
The physical flash wiring is identical, but the two application and
|
||||
bootloader pairs are not interchangeable.
|
||||
|
||||
### Header-only W25Q16 wiring for the unified RAK images
|
||||
|
||||
The unified RAK repeater images and matching OTAFIX 2.4.8 adaptive bootloaders
|
||||
also support a W25Q16 through the RAK19007 2.54 mm headers. This arrangement
|
||||
does not need an IO-connector interposer or underside SPI solder points:
|
||||
|
||||
| W25Q16 breakout | RAK19007 header | nRF52840 / Arduino pin |
|
||||
| --- | --- | --- |
|
||||
| `CLK` | J10 `TX1` | P0.16 / `16` |
|
||||
| `DO` / MISO | J10 `RX1` | P0.15 / `15` |
|
||||
| `DI` / MOSI | J11 `IO1` | P0.17 / `17` |
|
||||
| `CS` | J11 `AIN1` | P0.31 / `31` |
|
||||
| `VCC` | J12 `VDD` | regulated 3.3 V |
|
||||
| `GND` | J12 `GND` | ground |
|
||||
|
||||
Keep the approximately 10 kOhm `CS`-to-`VDD` pull-up. This mapping gives up
|
||||
the UART1 GPS and PPS pins while the flash is fitted. The unified firmware
|
||||
detects the exact `EF4015` device before GPS discovery and leaves I2C sensor
|
||||
pins `SDA` and `SCL` available. RAK4631 can still use its UART2 bridge; its
|
||||
UART1 bridge is unavailable with header flash. RAK3401 retains its 1 W radio
|
||||
and shared sensor/radio power rail. Do not substitute `SCL` for flash clock or
|
||||
use `SWCLK`, which is the debug clock. The older dedicated W25Q16 images above
|
||||
expect the WisBlock SPI connector wiring and do not use this header mapping.
|
||||
|
||||
## One-time prerequisite
|
||||
|
||||
Install a QSPI-capable OTAFIX 2.4.1 preview.9 or newer bootloader for the exact
|
||||
|
||||
@@ -104,6 +104,11 @@ power the flash from VBAT. The three SPI signals require underside pads or an
|
||||
IO-connector interposer; the easy J10/J11/J12 headers do not expose them. The
|
||||
flash consumes no WisBlock slot, so a supported GPS can remain in slot A.
|
||||
|
||||
The unified RAK repeater images additionally support a header-only W25Q16
|
||||
layout, using J10 `TX1`/`RX1` and J11 `IO1`/`AIN1`. This layout needs the
|
||||
matching adaptive OTAFIX 2.4.8 bootloader and gives up UART1 GPS/PPS; I2C
|
||||
sensors remain available. See the [pin table](ota_nrf52_qspi.md#header-only-w25q16-wiring-for-the-unified-rak-images).
|
||||
|
||||
The ordinary full-sensor `RAK_4631_repeater` image remains too large for the
|
||||
safe internal in-place update limit. Without external flash, use
|
||||
`RAK_4631_repeater_lora_ota_no_external_sensors`; it trims selected optional
|
||||
|
||||
@@ -1197,6 +1197,11 @@ bool ota_apply_mota_nrf52(OtaStoreQspiNrf52& store, const SignerAllowlist& allow
|
||||
if (!ota_apply_mota_nrf52_external(store, allow, OTA_BL_STORAGE_QSPI, "QSPI", false, st, msg)) {
|
||||
return false;
|
||||
}
|
||||
#if defined(OTA_RAK_AUTO_STORE)
|
||||
if (OtaStoreQspiNrf52::headerW25Detected()) {
|
||||
g_nrf52_qspi_handoff = GPREGRET2_OTA_STAGE_HEADER_W25;
|
||||
}
|
||||
#endif
|
||||
#if defined(OTA_RAK_AUTO_STORE) && !defined(RAK_3401)
|
||||
if (store.jedec_id() == 0xC84015UL) {
|
||||
OtaBootloaderIdentity identity;
|
||||
|
||||
@@ -84,7 +84,8 @@ static const uint8_t OTA_BL_STORAGE_SD = 0x01;
|
||||
static const uint8_t OTA_BL_STORAGE_STAGE_CEILING = 0x02;
|
||||
static const uint8_t OTA_BL_STORAGE_QSPI = 0x04;
|
||||
static const uint8_t OTA_BL_STORAGE_BOOT_UPDATE = 0x08;
|
||||
static const uint8_t OTA_BL_STORAGE_KNOWN = 0x0F;
|
||||
static const uint8_t OTA_BL_STORAGE_HEADER_W25 = 0x10;
|
||||
static const uint8_t OTA_BL_STORAGE_KNOWN = 0x1F;
|
||||
static const uint8_t OTA_BL_PROFILE_SD_BOOT_UPDATE =
|
||||
OTA_BL_STORAGE_SD | OTA_BL_STORAGE_BOOT_UPDATE;
|
||||
static const uint8_t OTA_BL_PROFILE_INTERNAL_BOOT_UPDATE =
|
||||
|
||||
@@ -56,6 +56,7 @@ static const uint8_t GPREGRET2_OTA_STAGE_LEGACY = 0xD4u;
|
||||
static const uint8_t GPREGRET2_OTA_STAGE_EXPANDED = 0xEDu;
|
||||
static const uint8_t GPREGRET2_OTA_STAGE_QSPI = 0x51u;
|
||||
static const uint8_t GPREGRET2_OTA_STAGE_RAK15001 = 0x52u;
|
||||
static const uint8_t GPREGRET2_OTA_STAGE_HEADER_W25 = 0x54u;
|
||||
static const uint8_t GPREGRET2_OTA_STAGE_SD = 0x53u;
|
||||
static const uint8_t GPREGRET2_OTA_STAGE_HYBRID = 0xA6u;
|
||||
|
||||
|
||||
@@ -50,6 +50,12 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
|
||||
}
|
||||
const bool qspi_bootloader = caps.present &&
|
||||
(caps.storage_flags & OTA_BL_STORAGE_QSPI) != 0;
|
||||
if (detected == 2u && OtaStoreQspiNrf52::headerW25Detected() &&
|
||||
(caps.storage_flags & OTA_BL_STORAGE_HEADER_W25) == 0) {
|
||||
activateInternal();
|
||||
_reason = "header W25 needs newer OTAFIX bootloader";
|
||||
return;
|
||||
}
|
||||
OtaBootloaderIdentity identity;
|
||||
const bool identity_valid = qspi_bootloader &&
|
||||
ota_installed_bootloader_identity(identity) && identity.crc_ok;
|
||||
@@ -69,7 +75,8 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
|
||||
}
|
||||
if (choice == RakStorageChoice::Qspi) {
|
||||
activateExternal();
|
||||
_reason = detected == 1u ? "RAK15001 C" : "W25Q16";
|
||||
_reason = detected == 1u ? "RAK15001 C" :
|
||||
OtaStoreQspiNrf52::headerW25Detected() ? "W25Q16 header" : "W25Q16 SPI";
|
||||
return;
|
||||
}
|
||||
_mode = UNSAFE;
|
||||
|
||||
@@ -25,6 +25,12 @@ static uint32_t auto_jedec_id = 0;
|
||||
static uint8_t auto_detection = 0xFF;
|
||||
static uint32_t auto_rak_probe_id = 0;
|
||||
static uint32_t auto_w25_probe_id = 0;
|
||||
// The header-connected W25Q16 uses J10 TX1/RX1 and J11 IO1/AIN1. It is
|
||||
// mutually exclusive with GPS on this board but leaves both I2C pins free.
|
||||
static bool auto_header_w25 = false;
|
||||
static const uint8_t HEADER_SCK_PIN = 16; // J10 TX1, P0.16
|
||||
static const uint8_t HEADER_IO0_PIN = 17; // J11 IO1, P0.17, W25 DI
|
||||
static const uint8_t HEADER_IO1_PIN = 15; // J10 RX1, P0.15, W25 DO
|
||||
#endif
|
||||
|
||||
#if defined(OTA_QSPI_SCK_PHYSICAL_PIN) && defined(OTA_QSPI_SCK_ARDUINO_PIN)
|
||||
@@ -100,6 +106,9 @@ static uint8_t arduino_to_physical(uint32_t arduino_pin) {
|
||||
}
|
||||
|
||||
static uint8_t qspi_sck_pin() {
|
||||
#if defined(OTA_RAK_AUTO_STORE)
|
||||
if (auto_header_w25) return arduino_to_physical(HEADER_SCK_PIN);
|
||||
#endif
|
||||
#ifdef OTA_QSPI_SCK_PHYSICAL_PIN
|
||||
return OTA_QSPI_SCK_PHYSICAL_PIN;
|
||||
#else
|
||||
@@ -120,6 +129,9 @@ static uint8_t qspi_cs_pin() {
|
||||
}
|
||||
|
||||
static uint8_t qspi_io0_pin() {
|
||||
#if defined(OTA_RAK_AUTO_STORE)
|
||||
if (auto_header_w25) return arduino_to_physical(HEADER_IO0_PIN);
|
||||
#endif
|
||||
#ifdef OTA_QSPI_IO0_PHYSICAL_PIN
|
||||
return OTA_QSPI_IO0_PHYSICAL_PIN;
|
||||
#else
|
||||
@@ -128,6 +140,9 @@ static uint8_t qspi_io0_pin() {
|
||||
}
|
||||
|
||||
static uint8_t qspi_io1_pin() {
|
||||
#if defined(OTA_RAK_AUTO_STORE)
|
||||
if (auto_header_w25) return arduino_to_physical(HEADER_IO1_PIN);
|
||||
#endif
|
||||
#ifdef OTA_QSPI_IO1_PHYSICAL_PIN
|
||||
return OTA_QSPI_IO1_PHYSICAL_PIN;
|
||||
#else
|
||||
@@ -320,18 +335,34 @@ uint8_t OtaStoreQspiNrf52::autoDetect() {
|
||||
pinMode(P_LORA_NSS, OUTPUT);
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
SPI1.end();
|
||||
const uint32_t w25_id = probe_nor(w25_cs);
|
||||
auto_w25_probe_id = w25_id;
|
||||
uint32_t w25_id = probe_nor(w25_cs);
|
||||
SPI1.begin();
|
||||
auto_detection = w25_id == 0xEF4015UL ? 2u : 0u;
|
||||
#else
|
||||
const uint8_t rak_cs = arduino_to_physical(26);
|
||||
nrf_gpio_pin_set(rak_cs);
|
||||
nrf_gpio_cfg_output(rak_cs);
|
||||
const uint32_t rak_id = probe_nor(rak_cs);
|
||||
const uint32_t w25_id = probe_nor(w25_cs);
|
||||
uint32_t w25_id = probe_nor(w25_cs);
|
||||
auto_rak_probe_id = rak_id;
|
||||
#endif
|
||||
if (w25_id != 0xEF4015UL) {
|
||||
// GPS discovery calls this one-time probe before it starts UART1. Ending
|
||||
// an unopened Adafruit UART can block indefinitely, so leave it alone.
|
||||
auto_header_w25 = true;
|
||||
const uint32_t header_id = probe_nor(w25_cs);
|
||||
if (header_id == 0xEF4015UL) {
|
||||
w25_id = header_id;
|
||||
} else {
|
||||
auto_header_w25 = false;
|
||||
nrf_gpio_cfg_default(arduino_to_physical(HEADER_SCK_PIN));
|
||||
nrf_gpio_cfg_default(arduino_to_physical(HEADER_IO0_PIN));
|
||||
nrf_gpio_cfg_default(arduino_to_physical(HEADER_IO1_PIN));
|
||||
}
|
||||
}
|
||||
auto_w25_probe_id = w25_id;
|
||||
#if defined(RAK_3401)
|
||||
auto_detection = w25_id == 0xEF4015UL ? 2u : 0u;
|
||||
#else
|
||||
const bool rak = rak_id == 0xC84015UL;
|
||||
const bool w25 = w25_id == 0xEF4015UL;
|
||||
auto_detection = rak && w25 ? 3u : rak ? 1u : w25 ? 2u : 0u;
|
||||
@@ -346,6 +377,11 @@ uint8_t OtaStoreQspiNrf52::autoDetect() {
|
||||
return auto_detection;
|
||||
}
|
||||
|
||||
bool OtaStoreQspiNrf52::headerW25Detected() {
|
||||
autoDetect();
|
||||
return auto_header_w25;
|
||||
}
|
||||
|
||||
void OtaStoreQspiNrf52::autoProbeIds(uint32_t& rak15001, uint32_t& w25q16) {
|
||||
autoDetect();
|
||||
rak15001 = auto_rak_probe_id;
|
||||
|
||||
@@ -170,6 +170,7 @@ public:
|
||||
// Probe exact supported NOR parts before activating the nRF QSPI block.
|
||||
// 0 = absent/unsupported, 1 = RAK15001 C, 2 = W25Q16, 3 = ambiguous.
|
||||
static uint8_t autoDetect();
|
||||
static bool headerW25Detected();
|
||||
// Raw read-only JEDEC results from the cached startup probe. Useful when a
|
||||
// connected breakout is not one of the exact supported parts.
|
||||
static void autoProbeIds(uint32_t& rak15001, uint32_t& w25q16);
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "I2CAddressClaimPolicy.h"
|
||||
#include "EnvironmentI2CConfig.h"
|
||||
#include "NmeaSentenceProbe.h"
|
||||
#if defined(OTA_RAK_AUTO_STORE) && defined(RAK_BOARD)
|
||||
#include "../ota/OtaStoreQspiNrf52.h"
|
||||
#endif
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
@@ -1226,6 +1229,18 @@ void EnvironmentSensorManager::initBasicGPS() {
|
||||
// or make a new location provider ...
|
||||
#ifdef RAK_WISBLOCK_GPS
|
||||
void EnvironmentSensorManager::rakGPSInit() {
|
||||
#if defined(OTA_RAK_AUTO_STORE) && defined(RAK_BOARD)
|
||||
// The 2.54 mm header W25Q16 uses the UART1 and PPS pads. Probe before
|
||||
// starting GPS discovery so UART1 never drives a NOR data/clock wire.
|
||||
if (mesh::ota::OtaStoreQspiNrf52::headerW25Detected()) {
|
||||
gps_active = false;
|
||||
gps_detected = false;
|
||||
gps_serial_transport = false;
|
||||
i2cGPSFlag = false;
|
||||
serialGPSFlag = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Preserve the established owner across repeated begin() calls. Clearing
|
||||
// this flag and starting Serial1 here would silently take UART1 back from an
|
||||
// active bridge.
|
||||
|
||||
@@ -47,8 +47,8 @@ void initVariant()
|
||||
ledOff(PIN_LED2);;
|
||||
|
||||
#if defined(OTA_RAK_AUTO_STORE)
|
||||
// Both candidates share SCK/MOSI/MISO. Hold both chip selects high until
|
||||
// the exact JEDEC probe chooses one; W25Q16 also needs its reset pull-up.
|
||||
// Keep both chip selects high through startup. RAK15001 and native W25Q16
|
||||
// use the WisBlock SPI bus; a header W25Q16 uses TX1/RX1/IO1 instead.
|
||||
pinMode(26, OUTPUT);
|
||||
digitalWrite(26, HIGH);
|
||||
pinMode(31, OUTPUT);
|
||||
|
||||
Reference in new issue
Block a user