mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-26 12:57:57 +00:00
Connect GPIO MISO during RAK NOR identification
This commit is contained in:
@@ -742,10 +742,16 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
|
||||
c.fetch_store.last_stage(), c.fetch_store.last_error()[0] ? " error=" : "",
|
||||
c.fetch_store.last_error());
|
||||
} else if (c.fetch_store.usesInternal()) {
|
||||
snprintf(reply, 160, "OTA storage: %s; internal capacity=%luK",
|
||||
c.fetch_store.selectionReason(), (unsigned long)(c.fetch_store.capacity() / 1024));
|
||||
uint32_t rak_id = 0, w25_id = 0;
|
||||
OtaStoreQspiNrf52::autoProbeIds(rak_id, w25_id);
|
||||
snprintf(reply, 160, "OTA storage: %s; internal capacity=%luK; probe rak=%06lX w25=%06lX",
|
||||
c.fetch_store.selectionReason(), (unsigned long)(c.fetch_store.capacity() / 1024),
|
||||
(unsigned long)rak_id, (unsigned long)w25_id);
|
||||
} else {
|
||||
snprintf(reply, 160, "OTA storage unsafe: %s", c.fetch_store.selectionReason());
|
||||
uint32_t rak_id = 0, w25_id = 0;
|
||||
OtaStoreQspiNrf52::autoProbeIds(rak_id, w25_id);
|
||||
snprintf(reply, 160, "OTA storage unsafe: %s; probe rak=%06lX w25=%06lX",
|
||||
c.fetch_store.selectionReason(), (unsigned long)rak_id, (unsigned long)w25_id);
|
||||
}
|
||||
#elif defined(NRF52_PLATFORM) && defined(OTA_QSPI_STORE)
|
||||
// This probe only reads JEDEC/SR1. capacity() deliberately preserves a
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace {
|
||||
static uint8_t auto_cs_pin = NRF_QSPI_PIN_NOT_CONNECTED;
|
||||
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;
|
||||
#endif
|
||||
|
||||
#if defined(OTA_QSPI_SCK_PHYSICAL_PIN) && defined(OTA_QSPI_SCK_ARDUINO_PIN)
|
||||
@@ -277,6 +279,10 @@ static uint32_t probe_nor(uint8_t cs) {
|
||||
// A previous reset may have left the NOR in deep power-down. This GPIO
|
||||
// wake precedes the nRF QSPI ACTIVATE for the same reason as ensureFlash().
|
||||
wake_qspi_flash_before_activate(pins);
|
||||
// The wake helper leaves GPIO input buffers disconnected for QSPI handoff.
|
||||
// This bit-banged JEDEC read instead samples IO1 through GPIO->IN, which
|
||||
// requires the MISO input buffer to be connected.
|
||||
nrf_gpio_cfg_input(pins.io1_pin, NRF_GPIO_PIN_NOPULL);
|
||||
nrf_gpio_pin_clear(cs);
|
||||
delayMicroseconds(1);
|
||||
(void)gpio_spi_byte(pins, 0x9Fu);
|
||||
@@ -284,6 +290,7 @@ static uint32_t probe_nor(uint8_t cs) {
|
||||
((uint32_t)gpio_spi_byte(pins, 0xFFu) << 8) |
|
||||
gpio_spi_byte(pins, 0xFFu);
|
||||
nrf_gpio_pin_set(cs);
|
||||
nrf_gpio_input_disconnect(pins.io1_pin);
|
||||
delayMicroseconds(1);
|
||||
if (id == 0xC84015UL || id == 0xEF4015UL) {
|
||||
// Detection is read-only. Return a recognized NOR to low-power standby;
|
||||
@@ -314,6 +321,7 @@ uint8_t OtaStoreQspiNrf52::autoDetect() {
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
SPI1.end();
|
||||
const uint32_t w25_id = probe_nor(w25_cs);
|
||||
auto_w25_probe_id = w25_id;
|
||||
SPI1.begin();
|
||||
auto_detection = w25_id == 0xEF4015UL ? 2u : 0u;
|
||||
#else
|
||||
@@ -322,6 +330,8 @@ uint8_t OtaStoreQspiNrf52::autoDetect() {
|
||||
nrf_gpio_cfg_output(rak_cs);
|
||||
const uint32_t rak_id = probe_nor(rak_cs);
|
||||
const uint32_t w25_id = probe_nor(w25_cs);
|
||||
auto_rak_probe_id = rak_id;
|
||||
auto_w25_probe_id = w25_id;
|
||||
const bool rak = rak_id == 0xC84015UL;
|
||||
const bool w25 = w25_id == 0xEF4015UL;
|
||||
auto_detection = rak && w25 ? 3u : rak ? 1u : w25 ? 2u : 0u;
|
||||
@@ -335,6 +345,12 @@ uint8_t OtaStoreQspiNrf52::autoDetect() {
|
||||
}
|
||||
return auto_detection;
|
||||
}
|
||||
|
||||
void OtaStoreQspiNrf52::autoProbeIds(uint32_t& rak15001, uint32_t& w25q16) {
|
||||
autoDetect();
|
||||
rak15001 = auto_rak_probe_id;
|
||||
w25q16 = auto_w25_probe_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
OtaStoreQspiNrf52::OtaStoreQspiNrf52() {
|
||||
|
||||
@@ -170,6 +170,9 @@ 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();
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
bool begin(uint32_t total_size) override;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check a real RAK4631/RAK3401 OTA store and its matching bootloader over USB.
|
||||
|
||||
Example: python3 tools/hil/verify_rak_ota_storage.py --port /dev/serial/by-id/...
|
||||
--serial 9AB3B64C641BA927 --expect w25q16
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
|
||||
from verified_nrf_flash import verify_port
|
||||
|
||||
|
||||
def command(port, text):
|
||||
port.reset_input_buffer()
|
||||
port.write((text + "\r").encode("ascii"))
|
||||
end = time.monotonic() + 5
|
||||
data = bytearray()
|
||||
while time.monotonic() < end:
|
||||
data.extend(port.read(4096))
|
||||
decoded = data.decode("utf-8", "replace")
|
||||
if re.search(r"(?:^|\r?\n)\s*->\s*[^\r\n]+", decoded):
|
||||
return decoded
|
||||
raise RuntimeError(f"no reply to {text!r}: {data.decode('utf-8', 'replace')!r}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--port", required=True)
|
||||
parser.add_argument("--serial", required=True)
|
||||
parser.add_argument("--expect", choices=("internal", "w25q16"), required=True)
|
||||
args = parser.parse_args()
|
||||
verify_port(args.port, args.serial)
|
||||
|
||||
import serial
|
||||
with serial.Serial(args.port, 115200, timeout=0.1, write_timeout=2) as port:
|
||||
time.sleep(0.3)
|
||||
replies = {cmd: command(port, cmd) for cmd in
|
||||
("ota storage", "ota self", "ota status")}
|
||||
|
||||
if args.expect == "w25q16":
|
||||
required = {
|
||||
"ota storage": ("QSPI W25Q16", "jedec=EF4015", "size=2048K"),
|
||||
"ota self": ("QSPI store:2048K", "bootloader: QSPI apply OK"),
|
||||
"ota status": ("bl:QSPI",),
|
||||
}
|
||||
else:
|
||||
required = {
|
||||
"ota storage": ("OTA storage: no external NOR", "internal capacity=256K"),
|
||||
"ota self": ("internal store:256K", "bootloader: delta apply OK"),
|
||||
"ota status": ("bl:internal",),
|
||||
}
|
||||
failures = [f"{cmd}: missing {needle!r}" for cmd, needles in required.items()
|
||||
for needle in needles if needle not in replies[cmd]]
|
||||
print(json.dumps({"serial": args.serial, "expected": args.expect,
|
||||
"passed": not failures, "replies": replies,
|
||||
"failures": failures}, indent=2))
|
||||
if failures:
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user