2026-02-19 11:44 PM - edited 2026-02-19 11:54 PM
Hello,
I have STM32F7 controller, using Free RTOS, trying to interface external NOR flash, S29GL128S10TFIV13.
So at first, I was trying to do normal read/write operation, but here what I observed,
void FMC_Init()
{
hnor2.Instance = FMC_NORSRAM_DEVICE;
hnor2.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
/* hnor2.Init */
hnor2.Init.NSBank = FMC_NORSRAM_BANK2;
hnor2.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
hnor2.Init.MemoryType = FMC_MEMORY_TYPE_NOR;
hnor2.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;
hnor2.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;
hnor2.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
hnor2.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
hnor2.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
hnor2.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
hnor2.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
hnor2.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
hnor2.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
hnor2.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
hnor2.Init.WriteFifo = FMC_WRITE_FIFO_ENABLE;
hnor2.Init.PageSize = FMC_PAGE_SIZE_NONE;
/* Timing */
Timing.AddressSetupTime = 3;
Timing.AddressHoldTime = 1;
Timing.DataSetupTime = 6;
Timing.BusTurnAroundDuration = 1;
Timing.CLKDivision = 16;
Timing.DataLatency = 17;
Timing.AccessMode = FMC_ACCESS_MODE_A;
/* ExtTiming */
if (HAL_NOR_Init(&hnor2, &Timing, NULL) != HAL_OK)
{
Error_Handler( );
}
}
void Verify_NOR_Flash(void)
{
uint16_t test_data_write[4] = {0x1234, 0x5678, 0x9ABC, 0xDEF0};
uint16_t test_data_read[4] = {0};
if (NOR_Write(0x0150, test_data_write, 4)) {
if (NOR_Read(0x0150, test_data_read, 4)) {
if (test_data_read[0] == 0x1234) {
// SUCCESS: Hardware, Timing, and Logic are all verified.
}
}
}
}
main()
{
MX_FMC_Init();
Verify_NOR_Flash();
}Result:
test_data_read[] = {336, 338, 340, 342} // equivalent to {0x150, 0x152, 0x154, 0x156}So tried to change address but it always read address line instead of data.
Here is code for reference:
// Helper to wait for NOR ready
bool NOR_WaitReady(uint32_t addr, uint32_t timeout_ms)
{
uint32_t tickStart = HAL_GetTick();
HAL_NOR_StatusTypeDef status;
do {
status = HAL_NOR_GetStatus(&hnor2, addr, 1);
if (status == HAL_NOR_STATUS_SUCCESS)
return true;
} while ((HAL_GetTick() - tickStart) < timeout_ms);
return false;
}
// Your sector erase wrapper
bool NOR_EraseSector(uint32_t absSectorAddr)
{
return (HAL_NOR_Erase_Block(&hnor2, absSectorAddr, NOR_BASE_ADDR) == HAL_OK) &&
NOR_WaitReady(absSectorAddr, 5000); // 5s timeout, adjust as needed
}
// Get sector start address (example for uniform sectors)
uint32_t NOR_GetSectorStart(uint32_t addr)
{
return (addr / NOR_SECTOR_SIZE) * NOR_SECTOR_SIZE;
}
// Check address range
bool NOR_IsValidRange(uint32_t addr, uint32_t length_bytes)
{
return (addr + length_bytes) <= 0x00800000; // 8MB for S29GL128S
}
// Read function: reads bytes from NOR using HAL_NOR_Read
bool NOR_Read(uint32_t addr, uint16_t *buffer, uint32_t length_bytes)
{
// hnor2.State = HAL_NOR_STATE_READY;
// HAL_NOR_ReturnToReadMode(&hnor2);
if (!buffer) return false;
// Must be within NOR range
if (!NOR_IsValidRange(addr, length_bytes)) return false;
// NOR is 16-bit word, so check alignment
if (addr % 2 != 0 || length_bytes % 2 != 0)
return false; // enforce word-alignment
// uint16_t *buf16 = (uint16_t *)buffer;
for (uint32_t i = 0; i < length_bytes; i++)
{
uint32_t current_abs_addr = NOR_BASE_ADDR + addr + (i * 2);
if (HAL_NOR_Read(&hnor2,
¤t_abs_addr,
&buffer[i]) != HAL_OK)
{
return false;
}
}
return true;
}
// ----------------- MAIN WRITE FUNCTION -----------------
bool NOR_Write(uint32_t addr, const uint16_t *data, uint32_t length_words)
{
if (!data) return false;
if (addr % 2 != 0) return false;
if (!NOR_IsValidRange(addr, length_words * 2)) return false;
static uint16_t sectorBuffer[NOR_SECTOR_SIZE / 2];
// HAL_NOR_WriteOperation_Enable(&hnor2);
uint32_t remaining = length_words;
uint32_t currentAddr = addr;
uint32_t bufOffset = 0;
while (remaining > 0)
{
uint32_t sectorStart = NOR_GetSectorStart(currentAddr);
uint32_t sectorOffset = currentAddr - sectorStart;
uint32_t absSectorAddr = NOR_BASE_ADDR + sectorStart;
uint32_t wordsInSector = (NOR_SECTOR_SIZE - sectorOffset) / 2;
uint32_t chunkWords = (remaining < wordsInSector) ? remaining : wordsInSector;
/* Read full sector (absolute) */
for (uint32_t i = 0; i < NOR_SECTOR_SIZE / 2; i++)
{
uint32_t absAddr = absSectorAddr + (i * 2);
if (HAL_NOR_Read(&hnor2,
(uint32_t *)absAddr,
§orBuffer[i]) != HAL_OK)
return false;
}
/* Modify buffer */
uint32_t sectorWordOffset = sectorOffset / 2;
for (uint32_t i = 0; i < chunkWords; i++)
{
sectorBuffer[sectorWordOffset + i] = data[bufOffset + i];
}
HAL_NOR_WriteOperation_Enable(&hnor2);
/* 3️⃣ Erase sector (relative) */
if (!NOR_EraseSector(sectorStart))
return false;
/* Program sector */
uint32_t totalWords = NOR_SECTOR_SIZE / 2;
uint32_t wordIndex = 0;
while (wordIndex < totalWords)
{
uint32_t remainingWords = totalWords - wordIndex;
uint32_t programChunk =
(remainingWords > NOR_BUFFER_WORDS) ?
NOR_BUFFER_WORDS : remainingWords;
uint32_t relativeAddr = sectorStart + (wordIndex * 2);
uint32_t absoluteAddr = NOR_BASE_ADDR + relativeAddr;
if (programChunk == 1)
{
// HAL_NOR_Program expects absolute pointer
if (HAL_NOR_Program(&hnor2,
(uint32_t *)absoluteAddr,
§orBuffer[wordIndex]) != HAL_OK)
return false;
}
else
{
// HAL_NOR_ProgramBuffer expects relative offset
if (HAL_NOR_ProgramBuffer(&hnor2,
relativeAddr,
§orBuffer[wordIndex],
programChunk) != HAL_OK)
return false;
}
if (!NOR_WaitReady(absoluteAddr, 5000))
return false;
wordIndex += programChunk;
}
remaining -= chunkWords;
currentAddr += chunkWords * 2;
bufOffset += chunkWords;
}
//HAL_NOR_WriteOperation_Disable(&hnor2);
HAL_NOR_ReturnToReadMode(&hnor2);
return true;
}Note: if I enable "HAL_NOR_WriteOperation_Disable(&hnor2);" after NOR_Write() then in NOR_Read() got this error "state == HAL_NOR_STATE_PROTECTED" so removed it.
Please help me understand what is missing here.
Thanks,
Nitin
Solved! Go to Solution.
2026-03-04 10:14 PM
Hello @mƎALLEm ,
I found the issue behind CommandSet reading garbage value. I have custom board in which it has external SRAM and external FLASH using FMC interface, so previously I have configured only external Flash, but now I have configured both SRAM and FLASH which indirectly solved my issue of getting correct value for CommandSet = 0x2.
Where I still need to figure it out, what is impact of not interfacing external SRAM.
Also, I have design function for FLASH Read/Write and both are working as expected.
Thanks,
Nitin
2026-02-20 4:00 AM
Hello,
I have used this test code to confirm Flash is integrated correctly or not.
#define NOR_BANK_ADDR ((uint32_t)0x64000000) // Change to 0x60000000 if using NE1
#define ADDR_16(offset) ((__IO uint16_t *)(NOR_BANK_ADDR + ((offset) << 1)))
uint16_t q_char = 0, r_char = 0, y_char = 0;
uint16_t cmd;
*ADDR_16(0x000) = 0x00F0;
HAL_Delay(10);
*ADDR_16(0x55) = 0x0098;
HAL_Delay(10);
// 3. Simple Direct Reads (To rule out your NOR_Read function logic)
q_char = *(__IO uint16_t *)(NOR_BANK_ADDR + (0x10 << 1));
r_char = *(__IO uint16_t *)(NOR_BANK_ADDR + (0x11 << 1));
y_char = *(__IO uint16_t *)(NOR_BANK_ADDR + (0x12 << 1));
RESULT = q_char = 'Q', r_char = 'R', y_char = 'Y'Now I can say flash is integrated correctly, so just there is some logical issue in the code, which may be needed to identify.
2026-02-20 4:13 AM - edited 2026-02-20 4:14 AM
So it does not seem to be a memory timing access issue but rather to the way haw you are accessing that memory.
Did you do a complete test of the complete 64MB of that Nor Flash?
2026-02-20 4:18 AM
I may need to check does I missed any left shifting like this in example
#define ADDR_16(offset) ((__IO uint16_t *)(NOR_BANK_ADDR + ((offset) << 1)))
2026-02-27 8:01 AM
Hello @mƎALLEm ,
Checked the HAL_NOR_Init() and it got issue here, while update varaible hnor->CommandSet = 0x4042
HAL_StatusTypeDef HAL_NOR_Init(NOR_HandleTypeDef *hnor, FMC_NORSRAM_TimingTypeDef *Timing,
FMC_NORSRAM_TimingTypeDef *ExtTiming)
{
uint32_t deviceaddress;
HAL_StatusTypeDef status = HAL_OK;
/* Check the NOR handle parameter */
if (hnor == NULL)
{
return HAL_ERROR;
}
if (hnor->State == HAL_NOR_STATE_RESET)
{
/* Allocate lock resource and initialize it */
hnor->Lock = HAL_UNLOCKED;
#if (USE_HAL_NOR_REGISTER_CALLBACKS == 1)
if (hnor->MspInitCallback == NULL)
{
hnor->MspInitCallback = HAL_NOR_MspInit;
}
/* Init the low level hardware */
hnor->MspInitCallback(hnor);
#else
/* Initialize the low level hardware (MSP) */
HAL_NOR_MspInit(hnor);
#endif /* (USE_HAL_NOR_REGISTER_CALLBACKS) */
}
/* Initialize NOR control Interface */
(void)FMC_NORSRAM_Init(hnor->Instance, &(hnor->Init));
/* Initialize NOR timing Interface */
(void)FMC_NORSRAM_Timing_Init(hnor->Instance, Timing, hnor->Init.NSBank);
/* Initialize NOR extended mode timing Interface */
(void)FMC_NORSRAM_Extended_Timing_Init(hnor->Extended, ExtTiming,
hnor->Init.NSBank, hnor->Init.ExtendedMode);
/* Enable the NORSRAM device */
__FMC_NORSRAM_ENABLE(hnor->Instance, hnor->Init.NSBank);
/* Initialize NOR Memory Data Width*/
if (hnor->Init.MemoryDataWidth == FMC_NORSRAM_MEM_BUS_WIDTH_8)
{
uwNORMemoryDataWidth = NOR_MEMORY_8B;
}
else
{
uwNORMemoryDataWidth = NOR_MEMORY_16B;
}
/* Initialize the NOR controller state */
hnor->State = HAL_NOR_STATE_READY;
/* Select the NOR device address */
if (hnor->Init.NSBank == FMC_NORSRAM_BANK1)
{
deviceaddress = NOR_MEMORY_ADRESS1;
}
else if (hnor->Init.NSBank == FMC_NORSRAM_BANK2)
{
deviceaddress = NOR_MEMORY_ADRESS2;
}
else if (hnor->Init.NSBank == FMC_NORSRAM_BANK3)
{
deviceaddress = NOR_MEMORY_ADRESS3;
}
else /* FMC_NORSRAM_BANK4 */
{
deviceaddress = NOR_MEMORY_ADRESS4;
}
if (hnor->Init.WriteOperation == FMC_WRITE_OPERATION_DISABLE)
{
(void)FMC_NORSRAM_WriteOperation_Disable(hnor->Instance, hnor->Init.NSBank);
/* Update the NOR controller state */
hnor->State = HAL_NOR_STATE_PROTECTED;
}
else
{
/* Get the value of the command set */
if (uwNORMemoryDataWidth == NOR_MEMORY_8B)
{
NOR_WRITE(NOR_ADDR_SHIFT(deviceaddress, uwNORMemoryDataWidth, NOR_CMD_ADDRESS_FIRST_CFI_BYTE),
NOR_CMD_DATA_CFI);
}
else
{
NOR_WRITE(NOR_ADDR_SHIFT(deviceaddress, uwNORMemoryDataWidth, NOR_CMD_ADDRESS_FIRST_CFI), NOR_CMD_DATA_CFI);
}
hnor->CommandSet = *(__IO uint16_t *) NOR_ADDR_SHIFT(deviceaddress, uwNORMemoryDataWidth, NOR_ADDRESS_COMMAND_SET);
status = HAL_NOR_ReturnToReadMode(hnor);
}
return status;
}Looks like it doesnt match with any of command set available
#define NOR_INTEL_SHARP_EXT_COMMAND_SET (uint16_t)0x0001 /* Supported in this driver */
#define NOR_AMD_FUJITSU_COMMAND_SET (uint16_t)0x0002 /* Supported in this driver */
#define NOR_INTEL_STANDARD_COMMAND_SET (uint16_t)0x0003 /* Not Supported in this driver */
#define NOR_AMD_FUJITSU_EXT_COMMAND_SET (uint16_t)0x0004 /* Not Supported in this driver */
#define NOR_WINDBOND_STANDARD_COMMAND_SET (uint16_t)0x0006 /* Not Supported in this driver */
#define NOR_MITSUBISHI_STANDARD_COMMAND_SET (uint16_t)0x0100 /* Not Supported in this driver */
#define NOR_MITSUBISHI_EXT_COMMAND_SET (uint16_t)0x0101 /* Not Supported in this driver */
#define NOR_PAGE_WRITE_COMMAND_SET (uint16_t)0x0102 /* Not Supported in this driver */
#define NOR_INTEL_PERFORMANCE_COMMAND_SET (uint16_t)0x0200 /* Not Supported in this driver */
#define NOR_INTEL_DATA_COMMAND_SET (uint16_t)0x0210 /* Not Supported in this driver */Ideally as per this flash S29, it has to return NOR_AMD_FUJITSU_COMMAND_SET, but getting some garbage value.
Please help me what I was missing, its quit long time I'm struggling with this.
Here below is some basic reference
static void MX_FMC_Init(void)
{
FMC_NORSRAM_TimingTypeDef Timing = {0};
hnor2.Instance = FMC_NORSRAM_DEVICE;
hnor2.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
/* hnor2.Init */
hnor2.Init.NSBank = FMC_NORSRAM_BANK2;
hnor2.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
hnor2.Init.MemoryType = FMC_MEMORY_TYPE_NOR;
hnor2.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;
hnor2.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;
hnor2.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
hnor2.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
hnor2.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
hnor2.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
hnor2.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
hnor2.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
hnor2.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
hnor2.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
hnor2.Init.WriteFifo = FMC_WRITE_FIFO_DISABLE;
hnor2.Init.PageSize = FMC_PAGE_SIZE_NONE;
/* Timing */
Timing.AddressSetupTime = 15;
Timing.AddressHoldTime = 15;
Timing.DataSetupTime = 255;
Timing.BusTurnAroundDuration = 15;
Timing.CLKDivision = 16;
Timing.DataLatency = 17;
Timing.AccessMode = FMC_ACCESS_MODE_A;
if (HAL_NOR_Init(&hnor2, &Timing, NULL) != HAL_OK)
{
Error_Handler( );
}
}
static void HAL_FMC_MspInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct ={0};
if (FMC_Initialized) {
return;
}
FMC_Initialized = 1;
/* Peripheral clock enable */
__HAL_RCC_FMC_CLK_ENABLE();
/** FMC GPIO Configuration
PE2 ------> FMC_A23
PE3 ------> FMC_A19
PE4 ------> FMC_A20
PE5 ------> FMC_A21
PE6 ------> FMC_A22
PF0 ------> FMC_A0
PF1 ------> FMC_A1
PF2 ------> FMC_A2
PF3 ------> FMC_A3
PF4 ------> FMC_A4
PF5 ------> FMC_A5
PF12 ------> FMC_A6
PF13 ------> FMC_A7
PF14 ------> FMC_A8
PF15 ------> FMC_A9
PG0 ------> FMC_A10
PG1 ------> FMC_A11
PE7 ------> FMC_D4
PE8 ------> FMC_D5
PE9 ------> FMC_D6
PE10 ------> FMC_D7
PE11 ------> FMC_D8
PE12 ------> FMC_D9
PE13 ------> FMC_D10
PE14 ------> FMC_D11
PE15 ------> FMC_D12
PD8 ------> FMC_D13
PD9 ------> FMC_D14
PD10 ------> FMC_D15
PD11 ------> FMC_A16
PD12 ------> FMC_A17
PD13 ------> FMC_A18
PD14 ------> FMC_D0
PD15 ------> FMC_D1
PG2 ------> FMC_A12
PG3 ------> FMC_A13
PG4 ------> FMC_A14
PG5 ------> FMC_A15
PC6 ------> FMC_NWAIT
PD0 ------> FMC_D2
PD1 ------> FMC_D3
PD4 ------> FMC_NOE
PD5 ------> FMC_NWE
PG9 ------> FMC_NE2
*/
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9
|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13
|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_12|GPIO_PIN_13
|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15
|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_FMC;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
2026-03-04 10:14 PM
Hello @mƎALLEm ,
I found the issue behind CommandSet reading garbage value. I have custom board in which it has external SRAM and external FLASH using FMC interface, so previously I have configured only external Flash, but now I have configured both SRAM and FLASH which indirectly solved my issue of getting correct value for CommandSet = 0x2.
Where I still need to figure it out, what is impact of not interfacing external SRAM.
Also, I have design function for FLASH Read/Write and both are working as expected.
Thanks,
Nitin