wtorek, 27 czerwca 2017

[13] STM32F7 - Discovery - SDRAM, Generacja własnego projektu

Ten poświęcę na przygotowanie krótkiego programu pozwalającym na wprowadzanie danych do pamięci SDRAM. Na płytce Discovery F7 znajduje się zewnętrzna pamięć MT48LC4M32B2.

Pamięć SDRAM już opisywałem na tym blogu, w tym poście zajmę się przygotowaniem programu pozwalającego na wpisywanie do niej danych, bez użycia bibliotek BSP.


CubeMx:


Poniżej przejdę przez wszystkie elementy jakie muszą zostać uruchomione w celu poprawnej pracy układu.

Włączenie zegara RCC:


Uruchomienie Debugera:


Uruchomienie pamięci USARTu:


Uruchomienie FMC dla SDRAM:


Konfiguracja zegara:


USART zostawiam ze standardowymi ustawieniami. Nie potrzebuję włączać przerwań ani DMA dla tego układu. Jedyną rzeczą jaką należy zmienić jest podłączenie pinu TX, który w celu wykorzystywania bez zewnętrznego konwertera musi zostać podłączony pod pin PA9.

Należy jeszcze pamiętać o konfiguracji samego rdzenia a dokładnie pamięci ICache, DCache oraz włączeniu jednostki ochrony pamięci:



No i najważniejsze. Uruchomienie pamięci SDRAM prezentuje się w następujący sposób:


Rozmieszczenie pinów na mikrokontrolerze wygląda następująco:


Po wszystkich procedurach kod należy wygenerować dla wybranego środowiska programistycznego, ja wykorzystałem Keila.

Dodatkowym elementem o którym należałoby pamiętać jest zwiększenie pamięci stosu i sterty. Wykonuje się to podczas generacji projektu:


Programowanie:


Dla kontrolera FMC wygenerowana procedura inicjalizacji dla pamięci SDRAM powinna wyglądać w następujący sposób:

  1. static void MX_FMC_Init(void)
  2. {
  3.   FMC_SDRAM_TimingTypeDef SdramTiming;
  4.   /** Perform the SDRAM1 memory initialization sequence
  5.   */
  6.   hsdram1.Instance = FMC_SDRAM_DEVICE;
  7.   /* hsdram1.Init */
  8.   hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
  9.   hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
  10.   hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
  11.   hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
  12.   hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_2;
  13.   hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
  14.   hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  15.   hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
  16.   hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
  17.   hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
  18.   /* SdramTiming */
  19.   SdramTiming.LoadToActiveDelay = 2;
  20.   SdramTiming.ExitSelfRefreshDelay = 7;
  21.   SdramTiming.SelfRefreshTime = 4;
  22.   SdramTiming.RowCycleDelay = 6;
  23.   SdramTiming.WriteRecoveryTime = 2;
  24.   SdramTiming.RPDelay = 2;
  25.   SdramTiming.RCDDelay = 2;
  26.   if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  27.   {
  28.     _Error_Handler(__FILE__, __LINE__);
  29.   }
  30. }

Plik .h z inicjalizacją SDRAM oraz funkcją zapisującą dane wygląda następująco:

  1. #ifndef __MT48LC4M32B2_H
  2. #define __MT48LC4M32B2_H
  3. //---------------------------------------------
  4. #include "stm32f7xx_hal.h"
  5. //---------------------------------------------
  6. #define SDRAM_BANK_ADDR                             ((uint32_t)0xC0000000)
  7. //---------------------------------------------
  8. #define SDRAM_TIMEOUT                                           ((uint32_t)0xFFFF)
  9. #define SDRAM_MR_BURST_LENGTH_1                 ((uint16_t)0x0000)
  10. #define SDRAM_MR_BURST_LENGTH_2                 ((uint16_t)0x0001)
  11. #define SDRAM_MR_BURST_LENGTH_4                 ((uint16_t)0x0002)
  12. #define SDRAM_MR_BURST_LENGTH_8                 ((uint16_t)0x0004)
  13. #define SDRAM_MR_BURST_TYPE_SEQUENTIAL          ((uint16_t)0x0000)
  14. #define SDRAM_MR_BURST_TYPE_INTERLEAVED         ((uint16_t)0x0008)
  15. #define SDRAM_MR_CAS_LATENCY_2                  ((uint16_t)0x0020)
  16. #define SDRAM_MR_CAS_LATENCY_3                  ((uint16_t)0x0030)
  17. #define SDRAM_MR_OPERATING_MODE_STANDARD        ((uint16_t)0x0000)
  18. #define SDRAM_MR_WRITEBURST_MODE_PROGRAMMED     ((uint16_t)0x0000)
  19. #define SDRAM_MR_WRITEBURST_MODE_SINGLE         ((uint16_t)0x0200)
  20. //---------------------------------------------
  21. void F7_SDRAM_Init(SDRAM_HandleTypeDef *hsdram);
  22. void Fill_Buffer(uint32_t *bufferData, uint32_t bufferLenght, uint32_t offSet);
  23. #endif /* __MT48LC4M32B2_H */

Do tego pliku zostały też dodane deklaracje wszystkich adresów oraz rejestrów.

W tym momencie uruchamiany jest sam kontroler, należy jeszcze pamiętać, o drugiej procedurze inicjalizacyjnej dla samej kości RAM. Którą należy najpierw przygotować. Można ją znaleźć w jednym z przykładów udostępnionych przez firmę ST (przykład znajduje się w folderze Examples, w pakietach dla środowiska CubeMx).

Wspomniana funkcja wygląda następująco:

  1. void F7_SDRAM_Init(SDRAM_HandleTypeDef *hsdram)
  2. {
  3.     __IO uint32_t tmpmrd = 0;
  4.    
  5.     /* Configurate a clock config command */
  6.   command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
  7.   command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  8.   command.AutoRefreshNumber = 1;
  9.   command.ModeRegisterDefinition = 0;
  10.    
  11.     /* Send configurate command */
  12.   halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
  13.    
  14.     if(halStatus != HAL_OK){
  15.     _Error_Handler(__FILE__, __LINE__);
  16.   }
  17.    
  18.     /* Delay between commands need to be at least 100us */
  19.   FMC_SDRAM_DELAY(1);
  20.    
  21.     /* Configurate precharge command */
  22.     command.CommandMode = FMC_SDRAM_CMD_PALL;
  23.   command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  24.   command.AutoRefreshNumber = 1;
  25.   command.ModeRegisterDefinition = 0;
  26.    
  27.     /* Send configurate command */
  28.   halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
  29.    
  30.     if(halStatus != HAL_OK){
  31.     _Error_Handler(__FILE__, __LINE__);
  32.   }
  33.    
  34.     FMC_SDRAM_DELAY(1);
  35.     /* Configurate Autorefresh command */
  36.   command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
  37.   command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  38.   command.AutoRefreshNumber = 8;
  39.   command.ModeRegisterDefinition = 0;
  40.    
  41.     /* Send configurate command */
  42.     halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
  43.    
  44.     if(halStatus != HAL_OK){
  45.     _Error_Handler(__FILE__, __LINE__);
  46.   }
  47.    
  48.     FMC_SDRAM_DELAY(1);
  49.     /* Program external memory mode register */
  50.     tmpmrd = (uint32_t) SDRAM_MR_BURST_LENGTH_1          |
  51.                                             SDRAM_MR_BURST_TYPE_SEQUENTIAL   |
  52.                                             SDRAM_MR_CAS_LATENCY_2           |
  53.                                             SDRAM_MR_OPERATING_MODE_STANDARD |
  54.                                             SDRAM_MR_WRITEBURST_MODE_SINGLE;
  55.   command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
  56.   command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
  57.   command.AutoRefreshNumber = 1;
  58.   command.ModeRegisterDefinition = tmpmrd;
  59.    
  60.     /* Send configurate command */
  61.   halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
  62.     if(halStatus != HAL_OK){
  63.     _Error_Handler(__FILE__, __LINE__);
  64.   }
  65.    
  66.     /* Set the refresh rate counter (15.62 us x Freq) - 20 */
  67.   /* Set the device refresh counter */
  68.     hsdram->Instance->SDRTR |= ((uint32_t)((1292)<< 1));   
  69. }

Funkcja wpisująca dane jak i odczytująca dane z niego znajduje się w głównej pętli programu:

  1. #include "main.h"
  2. #include "stm32f7xx_hal.h"
  3. /* USER CODE BEGIN Includes */
  4. #include "f7_sdram_mt48lc4m32b2.h"
  5. #include "string.h"
  6. /* USER CODE END Includes */
  7. /* Private variables ---------------------------------------------------------*/
  8. UART_HandleTypeDef huart1;
  9. SDRAM_HandleTypeDef hsdram1;
  10. /* USER CODE BEGIN PV */
  11. /* Private variables ---------------------------------------------------------*/
  12. #define BUFFER_SIZE         ((uint32_t)0x0400)
  13. #define WRITE_READ_ADDR     ((uint32_t)0x0800)
  14. uint32_t BufferSend[BUFFER_SIZE];
  15. uint32_t BufferReceive[BUFFER_SIZE];
  16. uint32_t iLoop = 0;
  17. __IO uint32_t statusData = 0;
  18. /* USER CODE END PV */
  19. /* Private function prototypes -----------------------------------------------*/
  20. void SystemClock_Config(void);
  21. static void MPU_Config(void);
  22. static void MX_GPIO_Init(void);
  23. static void MX_USART1_UART_Init(void);
  24. static void MX_FMC_Init(void);
  25. /* USER CODE BEGIN PFP */
  26. /* Private function prototypes -----------------------------------------------*/
  27. /* USER CODE END PFP */
  28. /* USER CODE BEGIN 0 */
  29. /* USER CODE END 0 */
  30. int main(void)
  31. {
  32.   /* USER CODE BEGIN 1 */
  33.   char bufferUSART[20]={0};
  34.   /* USER CODE END 1 */
  35.   /* MPU Configuration----------------------------------------------------------*/
  36.   MPU_Config();
  37.   /* Enable I-Cache-------------------------------------------------------------*/
  38.   SCB_EnableICache();
  39.   /* Enable D-Cache-------------------------------------------------------------*/
  40.   SCB_EnableDCache();
  41.   /* MCU Configuration----------------------------------------------------------*/
  42.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  43.   HAL_Init();
  44.   /* USER CODE BEGIN Init */
  45.   /* USER CODE END Init */
  46.   /* Configure the system clock */
  47.   SystemClock_Config();
  48.   /* USER CODE BEGIN SysInit */
  49.   /* USER CODE END SysInit */
  50.   /* Initialize all configured peripherals */
  51.   MX_GPIO_Init();
  52.   MX_USART1_UART_Init();
  53.   MX_FMC_Init();
  54.   /* USER CODE BEGIN 2 */
  55.   F7_SDRAM_Init(&hsdram1);
  56.     Fill_Buffer(BufferSend, BUFFER_SIZE, 0x00000001);
  57.    
  58.     /* Write data to SDRAM */
  59.     for (iLoop = 0; iLoop < BUFFER_SIZE; iLoop++)
  60.   {
  61.     *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*iLoop) = BufferSend[iLoop];
  62.   }
  63.    
  64.     /* Read data from SDRAM */
  65.     for (iLoop = 0; iLoop < BUFFER_SIZE; iLoop++)
  66.   {
  67.     BufferReceive[iLoop] = *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*iLoop);
  68.   }
  69.    
  70.     for (iLoop = 0; iLoop < BUFFER_SIZE; iLoop++)
  71.   {
  72.         sprintf(bufferUSART,"%04ld: 0x%08lX \r\n",(unsigned long)iLoop,(unsigned long)BufferReceive[iLoop]);
  73.         HAL_UART_Transmit(&huart1, (uint8_t*)bufferUSART,strlen(bufferUSART),0x1000);
  74.         HAL_Delay(100);  
  75.   }
  76.   for (iLoop = 0; (iLoop < BUFFER_SIZE) && (statusData == 0); iLoop++)
  77.   {
  78.     if (BufferReceive[iLoop] != BufferSend[iLoop])
  79.     {
  80.       statusData++;
  81.     }
  82.   }
  83.     /* Printf 32 bit status data */
  84.     sprintf(bufferUSART,"Data veryfication: %zu \r\n",statusData);
  85.     HAL_UART_Transmit(&huart1, (uint8_t*)bufferUSART,strlen(bufferUSART),0x1000);
  86.    
  87.     /* Check data */
  88.     if (statusData > 0x0000)
  89.   {
  90.       HAL_UART_Transmit(&huart1, (uint8_t*)"Error\r\n",7,0x1000);
  91.       HAL_Delay(200);
  92.   }
  93.   else if(statusData == 0x0000)
  94.   {
  95.     /* OK, turn on LED1 */
  96.         HAL_UART_Transmit(&huart1, (uint8_t*)"Passed\r\n",8,0x1000);
  97.   }
  98.   /* USER CODE END 2 */
  99.   /* Infinite loop */
  100.   /* USER CODE BEGIN WHILE */
  101.   while (1)
  102.   {
  103.   /* USER CODE END WHILE */
  104.   /* USER CODE BEGIN 3 */
  105.     HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_RESET);
  106.     HAL_Delay(1000);
  107.     HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_SET);
  108.     HAL_Delay(1000);
  109.   }
  110.   /* USER CODE END 3 */
  111. }

Pliki z projektem można znaleźć na dysku Google pod tym linkiem.