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.
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:
Dla kontrolera FMC wygenerowana procedura inicjalizacji dla pamięci SDRAM powinna wyglądać w następujący sposób:
Plik .h z inicjalizacją SDRAM oraz funkcją zapisującą dane wygląda następująco:
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:
Funkcja wpisująca dane jak i odczytująca dane z niego znajduje się w głównej pętli programu:
Pliki z projektem można znaleźć na dysku Google pod tym linkiem.
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:
- static void MX_FMC_Init(void)
- {
- FMC_SDRAM_TimingTypeDef SdramTiming;
- /** 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_8;
- hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
- hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
- hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_2;
- hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
- 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_0;
- /* SdramTiming */
- SdramTiming.LoadToActiveDelay = 2;
- SdramTiming.ExitSelfRefreshDelay = 7;
- 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(__FILE__, __LINE__);
- }
- }
Plik .h z inicjalizacją SDRAM oraz funkcją zapisującą dane wygląda następująco:
- #ifndef __MT48LC4M32B2_H
- #define __MT48LC4M32B2_H
- //---------------------------------------------
- #include "stm32f7xx_hal.h"
- //---------------------------------------------
- #define SDRAM_BANK_ADDR ((uint32_t)0xC0000000)
- //---------------------------------------------
- #define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
- #define SDRAM_MR_BURST_LENGTH_1 ((uint16_t)0x0000)
- #define SDRAM_MR_BURST_LENGTH_2 ((uint16_t)0x0001)
- #define SDRAM_MR_BURST_LENGTH_4 ((uint16_t)0x0002)
- #define SDRAM_MR_BURST_LENGTH_8 ((uint16_t)0x0004)
- #define SDRAM_MR_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
- #define SDRAM_MR_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
- #define SDRAM_MR_CAS_LATENCY_2 ((uint16_t)0x0020)
- #define SDRAM_MR_CAS_LATENCY_3 ((uint16_t)0x0030)
- #define SDRAM_MR_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
- #define SDRAM_MR_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
- #define SDRAM_MR_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
- //---------------------------------------------
- void F7_SDRAM_Init(SDRAM_HandleTypeDef *hsdram);
- void Fill_Buffer(uint32_t *bufferData, uint32_t bufferLenght, uint32_t offSet);
- #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:
- void F7_SDRAM_Init(SDRAM_HandleTypeDef *hsdram)
- {
- __IO uint32_t tmpmrd = 0;
- /* Configurate a clock config command */
- command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
- command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
- command.AutoRefreshNumber = 1;
- command.ModeRegisterDefinition = 0;
- /* Send configurate command */
- halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
- if(halStatus != HAL_OK){
- _Error_Handler(__FILE__, __LINE__);
- }
- /* Delay between commands need to be at least 100us */
- FMC_SDRAM_DELAY(1);
- /* Configurate precharge command */
- command.CommandMode = FMC_SDRAM_CMD_PALL;
- command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
- command.AutoRefreshNumber = 1;
- command.ModeRegisterDefinition = 0;
- /* Send configurate command */
- halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
- if(halStatus != HAL_OK){
- _Error_Handler(__FILE__, __LINE__);
- }
- FMC_SDRAM_DELAY(1);
- /* Configurate Autorefresh command */
- command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
- command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
- command.AutoRefreshNumber = 8;
- command.ModeRegisterDefinition = 0;
- /* Send configurate command */
- halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
- if(halStatus != HAL_OK){
- _Error_Handler(__FILE__, __LINE__);
- }
- FMC_SDRAM_DELAY(1);
- /* Program external memory mode register */
- tmpmrd = (uint32_t) SDRAM_MR_BURST_LENGTH_1 |
- SDRAM_MR_BURST_TYPE_SEQUENTIAL |
- SDRAM_MR_CAS_LATENCY_2 |
- SDRAM_MR_OPERATING_MODE_STANDARD |
- SDRAM_MR_WRITEBURST_MODE_SINGLE;
- command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
- command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
- command.AutoRefreshNumber = 1;
- command.ModeRegisterDefinition = tmpmrd;
- /* Send configurate command */
- halStatus = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
- if(halStatus != HAL_OK){
- _Error_Handler(__FILE__, __LINE__);
- }
- /* Set the refresh rate counter (15.62 us x Freq) - 20 */
- /* Set the device refresh counter */
- hsdram->Instance->SDRTR |= ((uint32_t)((1292)<< 1));
- }
Funkcja wpisująca dane jak i odczytująca dane z niego znajduje się w głównej pętli programu:
- #include "main.h"
- #include "stm32f7xx_hal.h"
- /* USER CODE BEGIN Includes */
- #include "f7_sdram_mt48lc4m32b2.h"
- #include "string.h"
- /* USER CODE END Includes */
- /* Private variables ---------------------------------------------------------*/
- UART_HandleTypeDef huart1;
- SDRAM_HandleTypeDef hsdram1;
- /* USER CODE BEGIN PV */
- /* Private variables ---------------------------------------------------------*/
- #define BUFFER_SIZE ((uint32_t)0x0400)
- #define WRITE_READ_ADDR ((uint32_t)0x0800)
- uint32_t BufferSend[BUFFER_SIZE];
- uint32_t BufferReceive[BUFFER_SIZE];
- uint32_t iLoop = 0;
- __IO uint32_t statusData = 0;
- /* USER CODE END PV */
- /* Private function prototypes -----------------------------------------------*/
- void SystemClock_Config(void);
- static void MPU_Config(void);
- static void MX_GPIO_Init(void);
- static void MX_USART1_UART_Init(void);
- static void MX_FMC_Init(void);
- /* USER CODE BEGIN PFP */
- /* Private function prototypes -----------------------------------------------*/
- /* USER CODE END PFP */
- /* USER CODE BEGIN 0 */
- /* USER CODE END 0 */
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- char bufferUSART[20]={0};
- /* USER CODE END 1 */
- /* MPU Configuration----------------------------------------------------------*/
- MPU_Config();
- /* Enable I-Cache-------------------------------------------------------------*/
- SCB_EnableICache();
- /* Enable D-Cache-------------------------------------------------------------*/
- SCB_EnableDCache();
- /* MCU Configuration----------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* USER CODE BEGIN Init */
- /* USER CODE END Init */
- /* Configure the system clock */
- SystemClock_Config();
- /* USER CODE BEGIN SysInit */
- /* USER CODE END SysInit */
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_USART1_UART_Init();
- MX_FMC_Init();
- /* USER CODE BEGIN 2 */
- F7_SDRAM_Init(&hsdram1);
- Fill_Buffer(BufferSend, BUFFER_SIZE, 0x00000001);
- /* Write data to SDRAM */
- for (iLoop = 0; iLoop < BUFFER_SIZE; iLoop++)
- {
- *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*iLoop) = BufferSend[iLoop];
- }
- /* Read data from SDRAM */
- for (iLoop = 0; iLoop < BUFFER_SIZE; iLoop++)
- {
- BufferReceive[iLoop] = *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*iLoop);
- }
- for (iLoop = 0; iLoop < BUFFER_SIZE; iLoop++)
- {
- sprintf(bufferUSART,"%04ld: 0x%08lX \r\n",(unsigned long)iLoop,(unsigned long)BufferReceive[iLoop]);
- HAL_UART_Transmit(&huart1, (uint8_t*)bufferUSART,strlen(bufferUSART),0x1000);
- HAL_Delay(100);
- }
- for (iLoop = 0; (iLoop < BUFFER_SIZE) && (statusData == 0); iLoop++)
- {
- if (BufferReceive[iLoop] != BufferSend[iLoop])
- {
- statusData++;
- }
- }
- /* Printf 32 bit status data */
- sprintf(bufferUSART,"Data veryfication: %zu \r\n",statusData);
- HAL_UART_Transmit(&huart1, (uint8_t*)bufferUSART,strlen(bufferUSART),0x1000);
- /* Check data */
- if (statusData > 0x0000)
- {
- HAL_UART_Transmit(&huart1, (uint8_t*)"Error\r\n",7,0x1000);
- HAL_Delay(200);
- }
- else if(statusData == 0x0000)
- {
- /* OK, turn on LED1 */
- HAL_UART_Transmit(&huart1, (uint8_t*)"Passed\r\n",8,0x1000);
- }
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_RESET);
- HAL_Delay(1000);
- HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_SET);
- HAL_Delay(1000);
- }
- /* USER CODE END 3 */
- }
Pliki z projektem można znaleźć na dysku Google pod tym linkiem.