środa, 4 października 2023

[2.1] STM32F7 - SDRAM

W tym poście dodatkowo opiszę konfigurację pamięci SDRAM w układzie STM32F7.

Pamięć SDRAM jest dosyć często wykorzystywana do przechowywania danych z buforem dla ekranów LCD. Dla przykładu weźmy ekran 480x480, gdzie kolory reprezentowane są na 16 bitach (RGB565). 

=> 480*480*(16/8) = 460800 bajtów.

W programie CubeMx należy wprowadzić następujące ustawienia w kontrolerze FMC:


Pamięć SDRAM jaka jest stosowana na płycie Discovery to MT48LC4M32B2. 
Sterownik można pobrać ze strony ST na githubie (link). 

Inicjalizacja sterownika FMC zostaje wygenerowana w następujący sposób:

  1. static void MX_FMC_Init(void)
  2. {
  3.   /* USER CODE BEGIN FMC_Init 0 */
  4.   /* USER CODE END FMC_Init 0 */
  5.  
  6.   FMC_SDRAM_TimingTypeDef SdramTiming = {0};
  7.  
  8.   /* USER CODE BEGIN FMC_Init 1 */
  9.   /* USER CODE END FMC_Init 1 */
  10.  
  11.   /** Perform the SDRAM1 memory initialization sequence
  12.   */
  13.   hsdram1.Instance = FMC_SDRAM_DEVICE;
  14.   /* hsdram1.Init */
  15.   hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
  16.   hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
  17.   hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
  18.   hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
  19.   hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  20.   hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
  21.   hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  22.   hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
  23.   hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
  24.   hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
  25.   /* SdramTiming */
  26.   SdramTiming.LoadToActiveDelay = 2;
  27.   SdramTiming.ExitSelfRefreshDelay = 7;
  28.   SdramTiming.SelfRefreshTime = 4;
  29.   SdramTiming.RowCycleDelay = 7;
  30.   SdramTiming.WriteRecoveryTime = 3;
  31.   SdramTiming.RPDelay = 2;
  32.   SdramTiming.RCDDelay = 2;
  33.  
  34.   if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  35.   {
  36.     Error_Handler( );
  37.   }
  38.  
  39.   /* USER CODE BEGIN FMC_Init 2 */
  40.   /* USER CODE END FMC_Init 2 */
  41. }

Do inicjalizacji należy jeszcze dołożyć konfigurację struktury z konfiguracją:

  1. typedef struct
  2. {
  3.   uint32_t TargetBank;           /*!< Target Bank                             */
  4.   uint32_t RefreshMode;          /*!< Refresh Mode                            */
  5.   uint32_t RefreshRate;          /*!< Refresh Rate                            */
  6.   uint32_t BurstLength;          /*!< Burst Length                            */
  7.   uint32_t BurstType;            /*!< Burst Type                              */
  8.   uint32_t CASLatency;           /*!< CAS Latency                             */
  9.   uint32_t OperationMode;        /*!< Operation Mode                          */
  10.   uint32_t WriteBurstMode;       /*!< Write Burst Mode                        */
  11. } MT48LC4M32B2_Context_t;

Proces konfiguracji wygląda następująco:

  1. MT48LC4M32B2_Context_t MT48LC4M32B2_t;
  2. MT48LC4M32B2_t.TargetBank      = FMC_SDRAM_CMD_TARGET_BANK1;
  3. MT48LC4M32B2_t.RefreshMode     = MT48LC4M32B2_AUTOREFRESH_MODE_CMD;
  4. MT48LC4M32B2_t.RefreshRate     = REFRESH_COUNT;
  5. MT48LC4M32B2_t.BurstLength     = MT48LC4M32B2_BURST_LENGTH_1;
  6. MT48LC4M32B2_t.BurstType       = MT48LC4M32B2_BURST_TYPE_SEQUENTIAL;
  7. MT48LC4M32B2_t.CASLatency      = MT48LC4M32B2_CAS_LATENCY_3;
  8. MT48LC4M32B2_t.OperationMode   = MT48LC4M32B2_OPERATING_MODE_STANDARD;
  9. MT48LC4M32B2_t.WriteBurstMode  = MT48LC4M32B2_WRITEBURST_MODE_SINGLE;
  10.  
  11. if (MT48LC4M32B2_Init(&hsdram1, &MT48LC4M32B2_t)!=MT48LC4M32B2_OK)
  12. {
  13.  Error_Handler();
  14. }

Mapa adresów dla pamięci zewnętrznej wygląda następująco:


W tym przypadku wykorzystujemy SDRAM Bank1, wobec tego adresy w pamieci są z przedziału 0xC0000000 do 0xCFFFFFFF. 

Dane w pamięci można zapisać np. przez użycie funkcji memcpy:

  1. memcpy((uint32_t *)SDRAM_BANK1_START_ADDR, writeBuff, 1000);
  2. memcpy(readBuff, (uint32_t *)SDRAM_BANK1_START_ADDR, 1000);

Czy za pomocą jednej z funkcji dołączonej do biblioteki:

  1. HAL_StatusTypeDef HAL_SDRAM_Read_8b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint8_t *pDstBuffer, uint32_t BufferSize);
  2. HAL_StatusTypeDef HAL_SDRAM_Write_8b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint8_t *pSrcBuffer, uint32_t BufferSize);
  3. HAL_StatusTypeDef HAL_SDRAM_Read_16b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint16_t *pDstBuffer, uint32_t BufferSize);
  4. HAL_StatusTypeDef HAL_SDRAM_Write_16b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint16_t *pSrcBuffer, uint32_t BufferSize);
  5. HAL_StatusTypeDef HAL_SDRAM_Read_32b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint32_t *pDstBuffer, uint32_t BufferSize);
  6. HAL_StatusTypeDef HAL_SDRAM_Write_32b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint32_t *pSrcBuffer, uint32_t BufferSize);
  7. HAL_StatusTypeDef HAL_SDRAM_Read_DMA(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint32_t *pDstBuffer, uint32_t BufferSize);
  8. HAL_StatusTypeDef HAL_SDRAM_Write_DMA(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint32_t *pSrcBuffer, uint32_t BufferSize);

Po rozwinięciu funkcja obsługująca zapis 8 bitowej zmiennej wygląda następująco:

  1. HAL_StatusTypeDef HAL_SDRAM_Write_8b(SDRAM_HandleTypeDef *hsdram, uint32_t *pAddress, uint8_t *pSrcBuffer,
  2.                                      uint32_t BufferSize)
  3. {
  4.   uint32_t size;
  5.   __IO uint8_t *pSdramAddress = (uint8_t *)pAddress;
  6.   uint8_t *psrcbuff = pSrcBuffer;
  7.  
  8.   /* Check the SDRAM controller state */
  9.   if (hsdram->State == HAL_SDRAM_STATE_BUSY)
  10.   {
  11.     return HAL_BUSY;
  12.   }
  13.   else if (hsdram->State == HAL_SDRAM_STATE_READY)
  14.   {
  15.     /* Process Locked */
  16.     __HAL_LOCK(hsdram);
  17.  
  18.     /* Update the SDRAM controller state */
  19.     hsdram->State = HAL_SDRAM_STATE_BUSY;
  20.  
  21.     /* Write data to memory */
  22.     for (size = BufferSize; size != 0U; size--)
  23.     {
  24.       *(__IO uint8_t *)pSdramAddress = *psrcbuff;
  25.       psrcbuff++;
  26.       pSdramAddress++;
  27.     }
  28.  
  29.     /* Update the SDRAM controller state */
  30.     hsdram->State = HAL_SDRAM_STATE_READY;
  31.  
  32.     /* Process Unlocked */
  33.     __HAL_UNLOCK(hsdram);
  34.   }
  35.   else
  36.   {
  37.     return  HAL_ERROR;
  38.   }
  39.  
  40.   return HAL_OK;
  41. }

Na samym początku sprawdzany jest stan kontrolera. Jeśli kontroler nie jest zajęty, to przechodzimy do ustawienia blokady, zmianę stanu na zajęty. Następnie pod podany do funkcji adres wprowadzamy dane do pamięci. Następnie zmieniamy stan na wolny i odblokowujemy kontroler. 

Powyższe funkcje zapisu i odczytu mogą zostać okrojone do takiej postaci:

  1. void SDRAM_Write_8b_Data(uint32_t *pAddress, uint8_t *pSrcBuffer, uint32_t BufferSize)
  2. {
  3.   uint32_t size;
  4.   __IO uint8_t *pSdramAddress = (uint8_t *)pAddress;
  5.   uint8_t *psrcbuff = pSrcBuffer;
  6.  
  7.   /* Write data to memory */
  8.   for (size = BufferSize; size != 0U; size--)
  9.   {
  10.     *(__IO uint8_t *)pSdramAddress = *psrcbuff;
  11.     psrcbuff++;
  12.     pSdramAddress++;
  13.   }
  14. }
  15.  
  16. void SDRAM_Read_8b_Data(uint32_t *pAddress, uint8_t *pDstBuffer, uint32_t BufferSize)
  17. {
  18.   uint32_t size;
  19.   __IO uint8_t *pSdramAddress = (uint8_t *)pAddress;
  20.   uint8_t *pdestbuff = pDstBuffer;
  21.  
  22.   for (size = BufferSize; size != 0U; size--)
  23.   {
  24.     *pdestbuff = *(__IO uint8_t *)pSdramAddress;
  25.     pdestbuff++;
  26.     pSdramAddress++;
  27.   }
  28. }

Brak komentarzy:

Prześlij komentarz