From 602f4432ec59bfb614a85132cfff4b6ca68f03fa Mon Sep 17 00:00:00 2001 From: Cristian Dragomir Date: Mon, 20 Oct 2025 13:07:22 +0300 Subject: [PATCH] added qspi flash functionality for the giga --- libraries/QSPI/CMakeLists.txt | 5 + libraries/QSPI/QSPI.cpp | 128 ++++++++++++++++++ libraries/QSPI/QSPI.h | 51 +++++++ libraries/QSPI/README.md | 50 +++++++ libraries/QSPI/examples/BasicQSPI.ino | 101 ++++++++++++++ .../arduino_giga_r1_stm32h747xx_m7.conf | 6 + .../arduino_giga_r1_stm32h747xx_m7.overlay | 17 +++ 7 files changed, 358 insertions(+) create mode 100644 libraries/QSPI/CMakeLists.txt create mode 100644 libraries/QSPI/QSPI.cpp create mode 100644 libraries/QSPI/QSPI.h create mode 100644 libraries/QSPI/README.md create mode 100644 libraries/QSPI/examples/BasicQSPI.ino diff --git a/libraries/QSPI/CMakeLists.txt b/libraries/QSPI/CMakeLists.txt new file mode 100644 index 000000000..9ac9b0e95 --- /dev/null +++ b/libraries/QSPI/CMakeLists.txt @@ -0,0 +1,5 @@ +zephyr_library() + +zephyr_library_sources(QSPI.cpp) + +zephyr_library_include_directories(.) \ No newline at end of file diff --git a/libraries/QSPI/QSPI.cpp b/libraries/QSPI/QSPI.cpp new file mode 100644 index 000000000..d6e7aef7f --- /dev/null +++ b/libraries/QSPI/QSPI.cpp @@ -0,0 +1,128 @@ +#include "QSPI.h" + +// Define the QSPI flash device - will be available when overlay is active +#if DT_NODE_EXISTS(DT_NODELABEL(qspi_flash)) +#define QSPI_FLASH_NODE DT_NODELABEL(qspi_flash) +#define QSPI_FLASH_DEVICE DEVICE_DT_GET(QSPI_FLASH_NODE) +#else +#define QSPI_FLASH_DEVICE NULL +#warning "QSPI flash device not found in device tree" +#endif + +QSPIClass::QSPIClass() : flash_dev(nullptr), initialized(false) { +} + +bool QSPIClass::begin() { + if (QSPI_FLASH_DEVICE == NULL) { + return false; + } + + flash_dev = QSPI_FLASH_DEVICE; + + if (!device_is_ready(flash_dev)) { + flash_dev = nullptr; + return false; + } + + initialized = true; + return true; +} + +bool QSPIClass::read(uint32_t address, void* data, size_t size) { + if (!initialized || !flash_dev) { + return false; + } + + int ret = flash_read(flash_dev, address, data, size); + return (ret == 0); +} + +bool QSPIClass::write(uint32_t address, const void* data, size_t size) { + if (!initialized || !flash_dev) { + return false; + } + + int ret = flash_write(flash_dev, address, data, size); + return (ret == 0); +} + +bool QSPIClass::erase(uint32_t address, size_t size) { + if (!initialized || !flash_dev) { + return false; + } + + int ret = flash_erase(flash_dev, address, size); + return (ret == 0); +} + +size_t QSPIClass::getFlashSize() { + if (!initialized || !flash_dev) { + return 0; + } + + uint64_t size = 0; + int ret = flash_get_size(flash_dev, &size); + if (ret != 0) { + return 0; + } + + return (size_t)size; +} + +size_t QSPIClass::getSectorSize() { + if (!initialized || !flash_dev) { + return 0; + } + + struct flash_pages_info page_info; + int ret = flash_get_page_info_by_offs(flash_dev, 0, &page_info); + if (ret != 0) { + return 0; + } + + return page_info.size; +} + +size_t QSPIClass::getPageSize() { + if (!initialized || !flash_dev) { + return 0; + } + + const struct flash_parameters *flash_params = flash_get_parameters(flash_dev); + if (!flash_params) { + return 0; + } + + return flash_params->write_block_size; +} + +bool QSPIClass::isReady() { + if (!flash_dev) { + return false; + } + + return device_is_ready(flash_dev); +} + +uint32_t QSPIClass::getFlashID() { + // This would require implementing JEDEC ID reading + // For now, return 0 as placeholder + return 0; +} + +bool QSPIClass::isValidAddress(uint32_t address, size_t size) { + if (!initialized || !flash_dev) { + return false; + } + + size_t flash_size = getFlashSize(); + return (address + size <= flash_size); +} + +void QSPIClass::end() { + flash_dev = nullptr; + initialized = false; +} + +// Create global instance +QSPIClass QSPI; \ No newline at end of file diff --git a/libraries/QSPI/QSPI.h b/libraries/QSPI/QSPI.h new file mode 100644 index 000000000..dc9cc0bc9 --- /dev/null +++ b/libraries/QSPI/QSPI.h @@ -0,0 +1,51 @@ +#ifndef QSPI_H +#define QSPI_H + +#include +#include +#include +#include +#include + +class QSPIClass { + +public: + QSPIClass(); + + // Initialize QSPI flash + bool begin(); + + // Read data from QSPI flash + bool read(uint32_t address, void* data, size_t size); + + // Write data to QSPI flash + bool write(uint32_t address, const void* data, size_t size); + + // Erase sector/block + bool erase(uint32_t address, size_t size); + + // Get flash information + size_t getFlashSize(); + size_t getSectorSize(); + size_t getPageSize(); + + // Check if flash is ready + bool isReady(); + + // Get flash ID + uint32_t getFlashID(); + + // Utility functions + bool isValidAddress(uint32_t address, size_t size = 1); + + // End/deinitialize + void end(); + +private: + const struct device *flash_dev; + bool initialized; +}; + +extern QSPIClass QSPI; + +#endif \ No newline at end of file diff --git a/libraries/QSPI/README.md b/libraries/QSPI/README.md new file mode 100644 index 000000000..bcf5033f7 --- /dev/null +++ b/libraries/QSPI/README.md @@ -0,0 +1,50 @@ +# QSPI Library + +This library provides a simple Arduino-style interface for accessing QSPI (Quad SPI) flash memory on Arduino Zephyr boards. + +## Features + +- Initialize and configure QSPI flash +- Read data from QSPI flash memory +- Write data to QSPI flash memory +- Erase sectors/blocks +- Get flash information (size, sector size, page size) + + +### Device Tree Setup + +Added to `arduino_giga_r1_stm32h747xx_m7.overlay` file: + +```dts +&quadspi { + pinctrl-0 = <&quadspi_clk_pf10 &quadspi_bk1_ncs_pb6 + &quadspi_bk1_io0_pf8 &quadspi_bk1_io1_pf9 + &quadspi_bk1_io2_pf7 &quadspi_bk1_io3_pf6>; + pinctrl-names = "default"; + status = "okay"; + + qspi_flash: qspi-nor-flash@90000000 { + compatible = "st,stm32-qspi-nor"; + reg = <0x90000000 DT_SIZE_M(16)>; + qspi-max-frequency = <80000000>; + size = ; + spi-bus-width = <4>; + status = "okay"; + }; +}; +``` + +### Configuration Setup + +Added to the `arduino_giga_r1_stm32h747xx_m7.conf`: + +```kconfig +CONFIG_SPI_STM32_QSPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_SFDP_DEVICETREE=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_PAGE_LAYOUT=y +``` + +No changes were needed to the llext. \ No newline at end of file diff --git a/libraries/QSPI/examples/BasicQSPI.ino b/libraries/QSPI/examples/BasicQSPI.ino new file mode 100644 index 000000000..9033c8022 --- /dev/null +++ b/libraries/QSPI/examples/BasicQSPI.ino @@ -0,0 +1,101 @@ +/* + Basic QSPI Flash Example + + This example demonstrates how to use the QSPI library to read and write + data to external QSPI flash memory on Arduino boards with QSPI support. + + Note: QSPI flash must be configured in the board's device tree overlay. +*/ + +#include + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(10); + } + + Serial.println("QSPI Flash Test"); + + // Initialize QSPI flash + if (!QSPI.begin()) { + Serial.println("Failed to initialize QSPI flash!"); + while (1) { + delay(1000); + } + } + + Serial.println("QSPI flash initialized successfully"); + + // Get flash information + Serial.print("Flash size: "); + Serial.print(QSPI.getFlashSize()); + Serial.println(" bytes"); + + Serial.print("Sector size: "); + Serial.print(QSPI.getSectorSize()); + Serial.println(" bytes"); + + Serial.print("Page size: "); + Serial.print(QSPI.getPageSize()); + Serial.println(" bytes"); + + // Test write and read + testWriteRead(); +} + +void loop() { + // Nothing to do in loop + delay(5000); + + Serial.println("Running periodic test..."); + testWriteRead(); +} + +void testWriteRead() { + const uint32_t test_address = 0x1000; // Test address (4KB offset) + const char test_data[] = "Hello QSPI Flash!"; + char read_buffer[32]; + + Serial.println("\n--- Testing Write/Read ---"); + + // Erase sector first + Serial.print("Erasing sector at 0x"); + Serial.print(test_address, HEX); + Serial.print("... "); + + if (QSPI.erase(test_address, QSPI.getSectorSize())) { + Serial.println("OK"); + } else { + Serial.println("FAILED"); + return; + } + + // Write test data + Serial.print("Writing data... "); + if (QSPI.write(test_address, test_data, strlen(test_data) + 1)) { + Serial.println("OK"); + } else { + Serial.println("FAILED"); + return; + } + + // Read back data + Serial.print("Reading data... "); + memset(read_buffer, 0, sizeof(read_buffer)); + if (QSPI.read(test_address, read_buffer, sizeof(read_buffer))) { + Serial.println("OK"); + + Serial.print("Read data: "); + Serial.println(read_buffer); + + // Verify data + if (strcmp(test_data, read_buffer) == 0) { + Serial.println("Data verification: PASSED"); + } else { + Serial.println("Data verification: FAILED"); + } + } else { + Serial.println("FAILED"); + } +} \ No newline at end of file diff --git a/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf b/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf index eaf599eaa..9ed660e2d 100644 --- a/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf +++ b/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf @@ -64,3 +64,9 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_BT_RX_STACK_SIZE=4096 CONFIG_STM32H7_BOOT_M4_AT_INIT=n + +# QSPI Flash Support +CONFIG_FLASH=y +CONFIG_FLASH_STM32_QSPI=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_PAGE_LAYOUT=y diff --git a/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.overlay b/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.overlay index 1bce0bdf3..0f07acfc1 100644 --- a/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.overlay +++ b/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.overlay @@ -334,6 +334,23 @@ }; +&quadspi { + pinctrl-0 = <&quadspi_clk_pf10 &quadspi_bk1_ncs_pg6 + &quadspi_bk1_io0_pd11 &quadspi_bk1_io1_pd12 + &quadspi_bk1_io2_pe2 &quadspi_bk1_io3_pf6>; + pinctrl-names = "default"; + status = "okay"; + + qspi_flash: qspi-nor-flash@0 { + compatible = "st,stm32-qspi-nor"; + reg = <0>; + size = ; /* 128 Mbits (16 MB) */ + qspi-max-frequency = <72000000>; + spi-bus-width = <4>; + status = "okay"; + }; +}; + &flash0 { partitions { user_sketch: partition@e0000 {