일단 사용하고자하는 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 |
---|