diff --git a/.gitignore b/.gitignore index 6229a7f..bb4d751 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Ignore .pre-commit-config.yaml .pre-commit-config.yaml +#Ignore Makefile +Makefile # Ignore other Branch specific dirs PeripheralDriversSubmodule/NAU7802/* PeripheralDriversSubmodule/MX66L1G45GMI/* diff --git a/DMA/.gitignore b/DMA/.gitignore new file mode 100644 index 0000000..8b3f240 --- /dev/null +++ b/DMA/.gitignore @@ -0,0 +1,2 @@ +# Ignore txt files containing notes about DMA +DMA-NOTES.txt \ No newline at end of file diff --git a/MX66L1G45GMI/Inc/MX66L1G45GMI.hpp b/MX66L1G45GMI/Inc/MX66L1G45GMI.hpp index 6800bd1..d659311 100644 --- a/MX66L1G45GMI/Inc/MX66L1G45GMI.hpp +++ b/MX66L1G45GMI/Inc/MX66L1G45GMI.hpp @@ -3,7 +3,7 @@ * @file MX66L1G45GMI.hpp * @author shiva * @date Mar 26, 2025 - * @brief + * @brief Header for MX66L1G45GMI Flash Memory Driver. ******************************************************************************** */ @@ -28,6 +28,19 @@ * TYPEDEFS ************************************/ +// ---< Config Struct >--- +typedef void (*MX66_WriteFunc)(const uint8_t* data, uint16_t len); // Write function pointer +typedef void (*MX66_ReadFunc)(uint8_t* data, uint16_t len); // Read function pointer +typedef void (*MX66_CSFunc)(bool state); // Chip Select function pointer +typedef void (*MX66_DelayFunc)(uint32_t ms); // Delay function pointer + +struct MX66_Config { + MX66_WriteFunc Write; + MX66_ReadFunc Read; + MX66_CSFunc SetCS; + MX66_DelayFunc Delay; +}; + /************************************ * CLASS DEFINITIONS ************************************/ @@ -36,10 +49,12 @@ * FUNCTION DECLARATIONS ************************************/ -uint32_t MX66_ReadID(void); +uint32_t MX66_ReadID(void); // Read Manufacturer and Device ID uint8_t MX66_ReadStatus(int reg); // Read status reg1,2,3 +void MX66_Init(MX66_Config* config); + void MX66_WriteStatus(int reg, uint8_t newstatus); void MX66_ReadSFDP(uint8_t *rData); diff --git a/MX66L1G45GMI/MX66L1G45GMI.cpp b/MX66L1G45GMI/MX66L1G45GMI.cpp index 45e75a3..8d893da 100644 --- a/MX66L1G45GMI/MX66L1G45GMI.cpp +++ b/MX66L1G45GMI/MX66L1G45GMI.cpp @@ -42,29 +42,65 @@ extern SPI_HandleTypeDef hspi1; //------------------------------------------------------------------------------------------------- // STM32 SPI Driver //------------------------------------------------------------------------------------------------- -void MX66_Delay(uint32_t time) -{ - HAL_Delay(time); + +// Create a static instance of the config +static MX66_Config io_driver = {nullptr, nullptr, nullptr, nullptr}; + +// Init function +void MX66_Init(MX66_Config* config) { + if(config != nullptr) { + io_driver = *config; + } + else { + SOAR_PRINT("MX66_Init: config is nullptr!\n"); + } } -void csLOW(void) -{ - HAL_GPIO_WritePin(SPI_FLASH_CS_GPIO_Port, SPI_FLASH_CS_Pin, GPIO_PIN_RESET); +// Internal Helper Wrappers + + +void MX66_Delay(uint32_t ms) { + if(io_driver.Delay) { + io_driver.Delay(ms); + } else { + SOAR_PRINT("[MX66 ERROR] MX66_Delay called before MX66_Init!\n"); + } } -void csHIGH(void) -{ - HAL_GPIO_WritePin(SPI_FLASH_CS_GPIO_Port, SPI_FLASH_CS_Pin, GPIO_PIN_SET); + +void csLOW(void) { + if(io_driver.SetCS) { + io_driver.SetCS(false); + } else { + SOAR_PRINT("[MX66 ERROR] csLOW called before MX66_Init!\n"); + } } -void SPI_Write(uint8_t *data, uint16_t len) -{ - HAL_SPI_Transmit(&MX66_SPI, data, len, 2000); + +void csHIGH(void) { + if(io_driver.SetCS) { + io_driver.SetCS(true); + } else { + SOAR_PRINT("[MX66 ERROR] csHIGH called before MX66_Init!\n"); + } } -void SPI_Read(uint8_t *data, uint16_t len) -{ - HAL_SPI_Receive(&MX66_SPI, data, len, 5000); + +void SPI_Write(uint8_t *data, uint16_t len) { + if(io_driver.Write) { + io_driver.Write(data, len); + } else { + SOAR_PRINT("[MX66 ERROR] SPI_Write called before MX66_Init!\n"); + } +} + + +void SPI_Read(uint8_t *data, uint16_t len) { + if(io_driver.Read) { + io_driver.Read(data, len); + } else { + SOAR_PRINT("[MX66 ERROR] SPI_Read called before MX66_Init!\n"); + } } /************************************ @@ -173,7 +209,7 @@ void write_enable(void) csLOW(); SPI_Write(&tData, 1); csHIGH(); - HAL_Delay(5); + MX66_Delay(5); } void write_disable(void) @@ -182,7 +218,7 @@ void write_disable(void) csLOW(); SPI_Write(&tData, 1); csHIGH(); - HAL_Delay(5); + MX66_Delay(5); } void MX66_Erase_Chip(void) diff --git a/MX66L1G45GMI/MX66L1G45GMI_Interface.cpp b/MX66L1G45GMI/MX66L1G45GMI_Interface.cpp new file mode 100644 index 0000000..58af8bd --- /dev/null +++ b/MX66L1G45GMI/MX66L1G45GMI_Interface.cpp @@ -0,0 +1,93 @@ +/** + ******************************************************************************** + * @file MX66L1G45GMI_Interface.cpp + * @author Javier + * @date Jan 28, 2026 + * @brief Interface Implementation for MX66L1G45GMI Flash Memory Driver. + ******************************************************************************** + */ + +/************************************ + * INCLUDES + ************************************/ +#include "MX66L1G45GMI.hpp" +#include "DMATransfer.hpp" +#include "stm32g4xx_hal.h" + +#include "main.h" + +/************************************ + * PRIVATE MACROS AND DEFINES + ************************************/ +extern SPI_HandleTypeDef hspi1; + + +/************************************ + * VARIABLES + ************************************/ + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + + /************************************ + * FUNCTION DEFINITIONS + ************************************/ + + // Chip Select Implementation +void IMPL_SetCS(bool high) { + HAL_GPIO_WritePin(SPI_FLASH_CS_GPIO_Port, SPI_FLASH_CS_Pin, high ? GPIO_PIN_SET : GPIO_PIN_RESET); +} + +// Delay Implementation +void IMPL_Delay(uint32_t ms) { + HAL_Delay(ms); +} + +void IMPL_Write(const uint8_t* data, uint16_t len) { + // Use Polling for tiny commands (<=32 bytes) to avoid DMA overhead + if (len <= 32) { + HAL_SPI_Transmit(&hspi1, (uint8_t*)data, len, 100); + } else { + // Use DMA for larger data transfers (TX-only) + if (DMAControl::Transfer(&hspi1, 0, (uint8_t*)data, nullptr, len) != HAL_OK) + { + SOAR_PRINT("IMPL_Write: DMA Transfer Error\n"); + return; + } + + // BLOCKING WAIT: Protects the stack buffer 'tData' inside the driver + while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) + ; + } +} + +// Read Implementation +void IMPL_Read(uint8_t* data, uint16_t len) { + // Use DMA Tool + DMAControl::Transfer(&hspi1, 0, nullptr, data, len); + // BLOCKING WAIT + int tick1 = HAL_GetTick(); + + while (HAL_GetTick() - tick1 < 500) { // 500ms timeout + if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY) break; + MX66_Delay(1); + } + if (HAL_GetTick() - tick1 >= 500) { + SOAR_PRINT("IMPL_Read: DMA Transfer Timeout\n"); + return; + } +} +// Setup Function +void Setup_Flash_Interface() { + static MX66_Config cfg; + + cfg.Write = IMPL_Write; + cfg.Read = IMPL_Read; + cfg.SetCS = IMPL_SetCS; + cfg.Delay = IMPL_Delay; + + // Inject dependencies into the abstract driver + MX66_Init(&cfg); +} +// End of MX66L1G45GMI_Interface.cpp \ No newline at end of file diff --git a/MX66L1G45GMI/README.md b/MX66L1G45GMI/README.md new file mode 100644 index 0000000..07d7fd4 --- /dev/null +++ b/MX66L1G45GMI/README.md @@ -0,0 +1,170 @@ + +# MX66L1G45GMI Flash Memory Driver + +**Authors:** Shiva (Core Logic), Javier (Interface & DMA) +**Date:** 31 Jan 2026 +**Target:** STM32G4 / STM32H7 + +--- + +## 1. Overview + +This driver provides a full-featured interface for the Macronix MX66L1G45GMI SPI Flash memory. It utilizes a layered architecture to decouple high-level flash commands from hardware-specific SPI/DMA implementations. + +--- + +## 2. Architecture + +The driver is split into three distinct layers: + +### A. Abstract Driver (`MX66L1G45GMI.cpp/.hpp`) + +- Hardware-agnostic logic for the flash memory +- Handles command opcode sequencing (Read, Write, Erase) +- Address calculation +- Status register polling (Checking the BUSY bit) +- Multi-page write logic (`MX66_Write_Block`) + +### B. Interface Implementation (`MX66L1G45GMI_Interface.cpp`) + +- Connects the Abstract Driver to the actual STM32 hardware +- Implements `IMPL_Write`, `IMPL_Read`, `IMPL_SetCS`, and `IMPL_Delay` +- Uses a hybrid strategy: Polling for small commands (≤ 32 bytes) and DMA for large data payloads +- note: Delay implementation on `MX66L1G45GMI_Interface.cpp` is subject to change via editing `IMPL_Delay` below. + +```cpp +// ... code ... + +// Delay Implementation +void IMPL_Delay(uint32_t ms) { + // Change this line to switch between HAL_Delay, osDelay, vTaskDelay, etc + HAL_Delay(ms); +} + +// ... code ... +``` + +### C. DMA Tool (`DMATransfer.hpp`) + +- Template-based utility +- Automated Cache Coherency (D-Cache cleaning/invalidating for G4/H7) +- Protocol-specific DMA calls (e.g., `HAL_SPI_TransmitReceive_DMA` for SPI) + +--- + +## 3. Hardware Specifications + +| Feature | Value | Details | +|-----------------|-----------------|-----------------------------------------| +| Total Capacity | 128 MB | 1 Gbit | +| Page Size | 256 Bytes | Basic programming unit | +| Sector Size | 4 KB | 16 pages × 256 bytes | +| Block Size | 64 KB | 16 sectors × 4 KB | + +--- + +## 4. Usage & Initialization + +> **Critical Setup Note:** +> To ensure the DMA channels are ready before the SPI peripheral attempts to link to them, the initialization order in `main.c` is necessary for correct functioning. + +### Example `main.cpp` + +```cpp +void Setup_Flash_Interface(); +#include "MX66L1G45GMI.hpp" + +int main(void) +{ + HAL_Init(); + SystemClock_Config(); + MX_GPIO_Init(); + + // CRITICAL: Initialize DMA before SPI + MX_DMA_Init(); // <--- DMA MUST be initialized BEFORE SPI + MX_SPI1_Init(); + + // Inject the hardware dependencies into the driver + Setup_Flash_Interface(); + + // Verify Connection + uint32_t flashID = MX66_ReadID(); + + // Expected ID for MX66L1G45GMI is 0xC2201B + // Manufacturer: 0xC2 (Macronix) | Type: 0x20 | Density: 0x1B (1Gb) + if (flashID == 0xC2201B) { + HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); + } + + while (1) + { + // main loop + } +} +``` + +--- + +## 5. API Reference + +### 5.1. Identification + +```c +uint32_t MX66_ReadID(void); +``` + +Returns the 3-byte Manufacturer and Device ID. + +### 5.2. Read Operations + +```c +void MX66_Read(uint32_t block, uint16_t offset, uint32_t size, uint8_t *rData); +``` + +Standard read starting at a specific block and offset. + +```c +void MX66_FastRead(...); +``` + +Uses the 0x0B command with dummy cycles for higher clock frequencies. + +### 5.3. Write Operations + +```c +void MX66_Write_Page(uint32_t page, uint16_t offset, uint32_t size, const uint8_t *data); +``` + +Writes data within a single 256-byte page. **Note:** Does not handle page roll-over. + +```c +void MX66_Write_Block(uint32_t block, uint16_t offset, uint32_t size, const uint8_t *data); +``` + +**Recommended.** Automatically handles data splitting across multiple pages if the data exceeds page boundaries. + +### 5.4. Erase Operations + +```c +void MX66_Erase_Sector(uint16_t numsector); // Erases a 4KB sector +void MX66_Erase_Block(uint32_t block); // Erases a 64KB block +void MX66_Erase_Chip(void); // Erases the entire chip (blocking, can take several minutes) +``` + +--- + +## 6. Implementation Details + +### Hybrid Transmission Strategy + +The Interface (`IMPL_Write`) intelligently switches methods based on data length: + +- **Length ≤ 32 Bytes:** Uses Polling (`HAL_SPI_Transmit`). This avoids the setup overhead of DMA for command headers and small register writes. +- **Length > 32 Bytes:** Uses DMA (`DMAControl::Transfer`). This frees the CPU during bulk data transfer, though the current implementation blocks to ensure buffer safety. + +### DMA & Cache Safety + +The `DMATransfer` tool automatically manages cache coherency for G4/H7 processors: + +- **TX:** Cleans D-Cache (flushes cache to RAM) before transmission. +- **RX:** Invalidates D-Cache (forces read from RAM) after reception.