How to use external SDRAM on StarSOM-STM32H757
From SomLabs Wiki
How to use external SDRAM on the StarSOM-STM32H757
This tutorial explains how to configure the external SDRAM memory on StarSOM-STM32H757 module. Please note, that it does not allow to execute code directly from it. It may serve as a large temporary data storage or buffer memory for graphics library etc.
This tutorial was prepared for the STM32CubeIDE (Version: 1.17.0). The base project can be created using this example:
Adding FMC configuration in CubeMX
The base configuration of the FMC peripheral can be generated by CubeMX tool available in STM32CubeIDE. After adding the FMC to the Cortex-M7 context it should be configured as shown below:

with the following control and timing parameters:

The GPIO lines should be configured automatically. Below is the full IO list for reference:

Finally the FMC clock should also be enabled:

SDRAM initialization code
The CubeMX creates a new MX_FMC_Init function in main.c, that requires custom initialization. The full code of this function should look like this:
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
#define SDRAM_MR_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MR_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MR_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MR_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MR_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
#define REFRESH_COUNT ((uint32_t)0x0603)
void MX_FMC_Init(void)
{
/* USER CODE BEGIN FMC_Init 0 */
/* USER CODE END FMC_Init 0 */
FMC_SDRAM_TimingTypeDef SdramTiming = {0};
/* USER CODE BEGIN FMC_Init 1 */
/* USER CODE END FMC_Init 1 */
/** Perform the SDRAM1 memory initialization sequence
*/
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 7;
SdramTiming.SelfRefreshTime = 4;
SdramTiming.RowCycleDelay = 7;
SdramTiming.WriteRecoveryTime = 3;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
/* USER CODE BEGIN FMC_Init 2 */
__IO uint32_t tmpmrd = 0;
FMC_SDRAM_CommandTypeDef command;
HAL_StatusTypeDef halStatus;
/* Configure a clock config command */
command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
command.AutoRefreshNumber = 1;
command.ModeRegisterDefinition = 0;
/* Send configure command */
halStatus = HAL_SDRAM_SendCommand(&hsdram1, &command, SDRAM_TIMEOUT);
if(halStatus != HAL_OK){
Error_Handler();
}
/* Delay between commands need to be at least 100us */
HAL_Delay(1);
/* Configure precharge command */
command.CommandMode = FMC_SDRAM_CMD_PALL;
command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
command.AutoRefreshNumber = 1;
command.ModeRegisterDefinition = 0;
/* Send configure command */
halStatus = HAL_SDRAM_SendCommand(&hsdram1, &command, SDRAM_TIMEOUT);
if(halStatus != HAL_OK){
Error_Handler();
}
/* Configure Autorefresh command */
command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
command.AutoRefreshNumber = 8;
command.ModeRegisterDefinition = 0;
/* Send configure command */
halStatus = HAL_SDRAM_SendCommand(&hsdram1, &command, SDRAM_TIMEOUT);
if(halStatus != HAL_OK){
Error_Handler();
}
HAL_Delay(1);
/* Program external memory mode register */
tmpmrd = (uint32_t) SDRAM_MR_BURST_LENGTH_1 |\
SDRAM_MR_BURST_TYPE_SEQUENTIAL |\
SDRAM_MR_CAS_LATENCY_2 |\
SDRAM_MR_OPERATING_MODE_STANDARD |\
SDRAM_MR_WRITEBURST_MODE_SINGLE;
command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
command.AutoRefreshNumber = 1;
command.ModeRegisterDefinition = tmpmrd;
/* Send configure command */
halStatus = HAL_SDRAM_SendCommand(&hsdram1, &command, SDRAM_TIMEOUT);
if(halStatus != HAL_OK){
Error_Handler();
}
/* Set the refresh rate counter (15.62 us x Freq) - 20 */
/* Set the device refresh counter */
halStatus = HAL_SDRAM_ProgramRefreshRate(&hsdram1, REFRESH_COUNT);
if(halStatus != HAL_OK){
Error_Handler();
}
/* USER CODE END FMC_Init 2 */
}
This function is called in main() after HAL initialization.
Testing SDRAM access
The SDRAM memory is accessible starting from 0xC0000000 address. This example function can be used for testing each address in the external memory. The USART1 peripheral connected to ST-Link console on StarCB-STM32H757-STD was configured as shown in the example project
static inline void print(const char* msg) {
HAL_UART_Transmit(&huart1, (const uint8_t*)msg, strlen(msg), 100);
}
static uint8_t testMem(uint32_t memAddr, uint32_t memLen, uint32_t step)
{
uint32_t* memPtr = (uint32_t*)memAddr;
for(int i = 0; i < 1000000; i++)
__NOP();
print("\r\nSDRAM testing\r\n");
print("SDRAM testing...0xAA...");
// Test AA
for(uint32_t i = 0; i < (memLen >> 2); i += step) {
memPtr[i] = 0xAAAAAAAA;
}
for(uint32_t i = 0; i < (memLen >> 2); i += step) {
if(memPtr[i] != 0xAAAAAAAA) {
print("FAILED\r\n");
return 1;
}
}
print("DONE\r\n");
print("SDRAM testing...0x55...");
// Test 55
for(uint32_t i = 0; i < (memLen >> 2); i += step) {
memPtr[i] = 0x55555555;
}
for(uint32_t i = 0; i < (memLen >> 2); i += step) {
if(memPtr[i] != 0x55555555) {
print("FAILED\r\n");
return 1;
}
}
print("DONE\r\n");
print("SDRAM testing...0x00...");
// Test zero
for(uint32_t i = 0; i < (memLen >> 2); i += 1) {
memPtr[i] = 0;
}
for(uint32_t i = 0; i < (memLen >> 2); i += step) {
if(memPtr[i] != 0) {
print("FAILED\r\n");
return 1;
}
}
print("DONE\r\n");
return 0;
}
The testing function can be called for the 32MB memory after HAL and MX_FMC_Init initialization functions:
testMem(0xC0000000, 0x2000000, 1);