-
-
Notifications
You must be signed in to change notification settings - Fork 31
Added raw QSPI functionality for the Arduino Giga #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
zephyr_library() | ||
|
||
zephyr_library_sources(QSPI.cpp) | ||
|
||
zephyr_library_include_directories(.) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use either |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not verify the device can accept more commands, but simply that it's enabled and has passed |
||
} | ||
|
||
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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
#ifndef QSPI_H | ||
#define QSPI_H | ||
|
||
#include <Arduino.h> | ||
#include <zephyr/kernel.h> | ||
#include <zephyr/device.h> | ||
#include <zephyr/drivers/flash.h> | ||
#include <zephyr/devicetree.h> | ||
|
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure the following part is relevant for Arduino users, only a selected few know about any of this 🙂 |
||
|
||
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 = <DT_SIZE_M(16) * 8>; | ||
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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <QSPI.h> | ||
|
||
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"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.