STM32F429 내장 메모리는 256KB이기 때문에 이미지 처리나 그래픽 효과를 사용하려면 내장 메모리 용량이 부족하여 외부 SDRAM을 사용해야 한다. STM32는 외부 메모리를 제어하는 컨트롤러(FMC)를 내장하고 있으며, 이것을 사용하여 PSRAM, NOR, NAND, SDRAM을 제어할 수 있다.
CubeIDE에서 FMC 활성화시키면 사용하는 기본 핀 배치로 설정되며, STM32F429 데모보드를 사용할 경우 핀설정은 그대로 사용하면 되지만, 램타이밍은 데이터시트를 참고하여 설정해야 한다.
위 SDRAM 데이터 시트를 참고하여 STM32 FMC 설정과 SDRAM 타이밍을 아래와 같이 설정할 수 있다. STM32F429 데모보드는 8MB SDRAM이 있으며, SDRAM Bank2에 연결되어 있다. 8MB SDRAM은 4Bank * 1M * 16bit로 구성되며 아래의 파라미터 설정은 약간의 학습이 필요하다.
램 타이밍설정은 아래 블로그를 참고하였으며, 아래 블로그는 STM32의 HCLK을 50MHz로 설정하였기 때문에 180MHz 설정한 내 데모보드와는 다른 값으로 설정되어 있는 상태이다.
STM32 HCLK = 180MHz, SDRAM Common Clock = 2HCLK cycle clock으로 설정한 경우 1clock cycle의 속도는 아래와 같다.
1clock cycle time = 1 / 90MHz = 11.11ns
따라서, FMC의 SDRAM 타이밍은 SDRAM 데이터시트에서 값에 비례하여 설정하도록 한다. STM32F429 데모보드 예제코드에서는 RowCycleDelay(TRC, 63ns)가 7clock cycles로 설정되어 있는데, 이것은 6으로 설정하는 맞는 것 같다.
참고로, 데이터시트에서 TWR 값은 TDPL로 표시되기도 하며, 위 파라미터 설정으로 FMC는 초기화 시킬 수 있지만, 아직 SDRAM을 사용할 수 없다. SDRAM을 사용하려면 SDRAM 초기화 과정이 필요하다. 이것은 STM32F429 데모보드 예제코드를 그대로 사용하였다.
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
static void MX_FMC_Init(void)
{
...
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
else
{
__IO uint32_t tmpmrd =0;
FMC_SDRAM_CommandTypeDef Command;
/* Step 1: Configure a clock configuration enable command */
Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000);
/* Step 2: Insert 100 us minimum delay */
/* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
HAL_Delay(100);
/* Step 3: Configure a PALL (precharge all) command */
Command.CommandMode = FMC_SDRAM_CMD_PALL;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000);
/* Step 4: Configure an Auto Refresh command */
Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 4;
Command.ModeRegisterDefinition = 0;
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000);
/* Step 5: Program the external memory mode register */
tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = tmpmrd;
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000);
/* Step 6: Set the refresh rate counter */
/**
*
* Set the device refresh rate
*
* refresh rate = refresh period / number of rows
* refresh rate = 64ms / 4096 = 15.62us
* refresh count = (refresh rate x SDRAM clock frequency) - 20
* refresh count = 15.62us x 90MHz - 20 = 1385.xx
*
*/
HAL_SDRAM_ProgramRefreshRate(&hsdram1, 1386);
}
}
이제 내장 메모리가 아닌 외부 SDRAM을 사용하기 때문에 ARGB8888을 사용할 수 있다. 이 포스트에서 사용한 전체코드는 아래 GitHub에서 받을 수 있다.