한남대 졸업작품

STM32 DRAM(IS42S32800J-6TL) Artwork와 Init (Frame buffer)

JUNGCHAN LEE 2024. 12. 1. 10:33

 

일단 사용하고자하는 DRAM의 데이터시트이다. 

 

https://www.mouser.kr/datasheet/2/198/42_45S32800J-706492.pdf

 

DRAM이 있어야 "800x480" 해상도의 프레임버퍼를 구동할 수 있고, 추가적인 AI 데이터 로딩을 위함이기도 하다. 

 

OV5640으로 취득한 데이터가 MCU를 거쳤다가 프레임버퍼로 DRAM에 자연스럽게 로딩될 수 있게 코딩을 작성했다. 

 

 일단 Artwork 는 단순하게 연결성위주로 따졌다. 실제로는 Line width 와 Line Length를 따지는 것이 좋은데 최대한 짧은 거리에 배치해 이 문제는 고려하지 않는것으로 했다. 

 

Decoupling Capacitor 들의 크기가 1608임을 고려하면 실제로 U8(DRAM)과 U6(MUC)와의 거리가 그렇게 멀지 않음을 금방 알 수 있다. 실제로도 제일 긴 routing 의 Line Length 가 3400mil정도였다. 최대한 Top과 Bottom만 사용하려고 했고, 나머지 Periphrials (Camera, PHY Chip, LCD)들이 다른 층을 사용하게 설계했다. 

나머지는 이제 코딩이다. DRAM은 속도가 빠른만큼 Timing이 중요하다고 들어왔다. 그래서 코드에서도 철저하게 Timing 을 고려해주는 방향으로 진행했다. 

아래는 기본적으로 CubeIDE가 생성하는 MX_FMC_Init 코드이다. Parameter 값들은 이 값들 보다 커도 동작하지만 속도가 느려질수는 있다.

static 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_12;
  hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;
  hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
  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_1;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 6;
  SdramTiming.SelfRefreshTime = 4;
  SdramTiming.RowCycleDelay = 6;
  SdramTiming.WriteRecoveryTime = 2;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 2;

  if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FMC_Init 2 */
  SDRAM_RESET();   // <----------------요 부분을 수정
  /* USER CODE END FMC_Init 2 */
}

 

마지막에 SDRAM_RESET() 함수를 넣어줬다. SDRAM_RESET 함수는 내가 직접 정의한 함수이다.  이는 데이터 시트를 참고해서 작성해주었다. 참고로 FMC Clock은 166MHz로 설정해서 진행해주었다. 

 

 

/**/
void SDRAM_RESET(void){

	FMC_SDRAM_CommandTypeDef Command;

	__IO uint32_t tmpmrd = 0;
	uint32_t SDRAM_TIMEOUT = (uint32_t)0xFFFF;

	/* Step 1: Configure a clock configuration enable command */
	Command.CommandMode            = FMC_SDRAM_CMD_CLK_ENABLE;
	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
	Command.AutoRefreshNumber      = 1;
	Command.ModeRegisterDefinition = 0;

	/* Send the command */
	HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

	/* Step 2: Insert 100 us minimum delay */
	/* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
	HAL_Delay(10);

	/* Step 3: Configure a PALL (precharge all) command */
	Command.CommandMode            = FMC_SDRAM_CMD_PALL;
	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
	Command.AutoRefreshNumber      = 1;
	Command.ModeRegisterDefinition = 0;

	/* Send the command */
	HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

	/* Step 4: Configure an Auto Refresh command */
	Command.CommandMode            = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
	Command.AutoRefreshNumber      = 8;
	Command.ModeRegisterDefinition = 0;

	/* Send the command */
	HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

	/* Step 5: Program the external memory mode register */
	tmpmrd = (uint32_t) 0x0237; //0000001000110111

	Command.CommandMode            = FMC_SDRAM_CMD_LOAD_MODE;
	Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
	Command.AutoRefreshNumber      = 1;
	Command.ModeRegisterDefinition = tmpmrd;

	/* Send the command */
	HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

	/* Step 6: Set the refresh rate counter */
	   /* Set the device refresh rate
	    * COUNT = [(SDRAM self refresh time / number of row) x  SDRAM CLK] - 20
	            EX) = [(64ms/4096) * 100MHz] - 20 = 1562.5 - 20 ~ 1542
	            THIS) = [(70ms/4096)]*83.25MHz - 20 = 1422.72 - 20 ~ 1403*/
                
	uint32_t RefreshCount = 1403;

	HAL_SDRAM_ProgramRefreshRate(&hsdram1, RefreshCount);

}

 

코드를 해설하자면, 클락을 Enable해주고 Delay하고 Precharge All 해주는데에는 이견이 없을것이다. Command.AutoRefreshNumber > 4 이면 문제 없는것또한 확인했다. 다만 memory mode registor 에 알맞은 값을 저장해주는것이 무엇보다도 중요하다는 점을 알았다. 

 

 

위의 코드를 참고해서 tmpmrd 를 알맞은 값으로 조정해주는것이 무엇보다도 중요하다. 나의 경우에는 Full page Burst Length 를 활성화해서 최대한 프레임 버퍼를 빨리 읽는 쪽으로 프로그래밍 해주었고, CAS Latency 또한 3으로 했기 때문에 그대로 진행해주었다. 

그리고 마찬가지로 중요한 부분이었던 것은 Autorefresh 설정이었는데 이는 주석으로 적어놓은 공식을 잘 따라서 계산해주면 된다. 나의 경우에는 Common Clock 이 2HCLK clock cycles로 되어있어서 FMC 클럭을 2분주비로 계산하고 있기 때문에 SDRAM Clock 을 166.5 / 2 = 83.25로 계산했고, self refresh time 은 데이터시트를 참조하면 알 수 있다. Autorefresh에서 RefreshCount가 10정도만 잘못되어도 아예 버퍼로써 작동하지 못함을 확인했으며, 매우 중요한 값이다. 

위 코드들을 전부 기입해주면 SDRAM이 작동하기 시작한다. 

 

 

'한남대 졸업작품' 카테고리의 다른 글

AI Camera 제작을 위한 Yolov5n custom train  (1) 2024.11.13