Урок 62
Часть 4
FMC SDRAM
В прошлой части урока мы научились передавать команды микросхеме памяти SDRAM через контроллер FMC, используя только функции библиотеки HAL.
Также мы уже передали две команды, и, мало того, проверили, что они успешно передались.
Следующая команда
HAL_Delay(1);
command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
command.AutoRefreshNumber = 8;
command.ModeRegisterDefinition = 0;
hal_stat = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
HAL_Delay(1);
Здесь мы задаём режим автоорегенерации, необходимый для нормального функционирования SDRAM.
Следующая команда
tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_2 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
command.AutoRefreshNumber = 1;
command.ModeRegisterDefinition = tmpmrd;
hal_stat = HAL_SDRAM_SendCommand(hsdram, &command, SDRAM_TIMEOUT);
Здесь включаем размер пакета, последовательный режим передачи, латентность 2 такта, стандартный режим, одиночный режим записи пакета.
И в конце инициализации мы вручную в в регистр FMC SDRTR, отвечающий за время авторегенерации отправим определенное число (разъяснено в комментарии к коду)
/* Step 8: Set the refresh rate counter */
/* (15.62 us x Freq) — 20 */
/* Set the device refresh counter */
hsdram->Instance->SDRTR |= ((uint32_t)((1292)<< 1));
}
Вот. собственно и вся инициализация.
Также мы знаем, что FMC отображает адреса SDRAM по определённым адресам. Откроем страницу 307 в Reference Manual на контроллер
Так ка мы подключали именно Bank 1, то напишем ещё один макрос в файл MT48LC4M32B2.h, чтобы нам было удобнее и нагляднее обращаться к SDRAM из программы
//————————————————
#define SDRAM_BANK_ADDR ((uint32_t)0xC0000000)
А также напишем несколько макросов и глобальных переменных в файле main.c
/* USER CODE BEGIN PV */
/* Private variables ———————————————————*/
#define BUFFER_SIZE ((uint32_t)0x0100)
#define WRITE_READ_ADDR ((uint32_t)0x0800)
uint32_t aTxBuffer[BUFFER_SIZE];
uint32_t aRxBuffer[BUFFER_SIZE];
uint32_t uwIndex = 0;
/* USER CODE END PV */
Здесь макросы для размера буфера, то есть это количество байт, которые мы будем писать в память SDRAM и затем оттуда читать, смещение от начала SDRAM в FMC, буфер для записи, буфер для чтения и порядковый номер в массиве.
Также добавим функцию в файл main.c для заполнения буфера определёнными данными для последующего его использования для записи в SDRAM. Функция была взята из примера с репозитория Cube MX и в изменениях не нуждается
/* USER CODE BEGIN 0 */
static void Fill_Buffer(uint32_t *pBuffer, uint32_t uwBufferLenght, uint32_t uwOffset)
{
uint32_t tmpIndex = 0;
/* Put in global buffer different values */
for (tmpIndex = 0; tmpIndex < uwBufferLenght; tmpIndex++ )
{
pBuffer[tmpIndex] = tmpIndex + uwOffset;
}
}
/* USER CODE END 0 */
Во входящих параметрах у нас указатель на буфер, который мы будем заполнять данными , количество байтов заполнения, а также значение начального байта. Дальше значения байтов будут увеличиваться на 1.
Воспользовавшись данной функцией и вызвав её в main(), заполним наш буфер, начав с любого значения
/* USER CODE BEGIN 2 */
MT48LC4M32B2_Init(&hsdram1);
Fill_Buffer(aTxBuffer, BUFFER_SIZE, 0x37BA0F68);
Теперь запишем в SDRAM содержимое буфера
Fill_Buffer(aTxBuffer, BUFFER_SIZE, 0x37BA0F68);
//запишем данные из буфера в память SDRAM с адреса 0xC0000800
for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
{
*(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*uwIndex) = aTxBuffer[uwIndex];
}
После этого попытаемся их из SDRAM прочитать в другой буфер
}
//прочитаем в другой буфер данные из памяти SDRAM с адреса 0xC0000800
for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
{
aRxBuffer[uwIndex] = *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*uwIndex);
}
Затем отправим байты в текстовом виде в USART
}
//отправим байты в USART
for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
{
sprintf(str1,»%03ld: 0x%08lXrn»,(unsigned long) uwIndex,(unsigned long)aRxBuffer[uwIndex]);
HAL_UART_Transmit(&huart1, (uint8_t*)str1,strlen(str1),0x1000);
HAL_Delay(100);
}
Задержка здесь оптимальная, так как если меньше, виснет терминальная программа, другие не виснут, но они не такие наглядные.
По окончании передачи зажжем зелёный светодиод
}
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_SET);
Из бесконечного цикла всё удалим
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
Соберём код, откроем терминальную программу, нажмём там Connect, выбрав порт и прошьём контроллер.
Мы должны в терминале увидеть следующую картину
Ну вот! У нас всё считалось из памяти SDRAM, так как все байты пришли в USART.
Я думаю, к теме FMC и SDRAM мы ещё вернёмся, когда будем программировать дисплей, а также в других случаях.
Предыдущая часть Программирование МК STM32 Следующий урок
Техническая документация на микросхему SDRAM MT48LC4M32B2
Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
у меня STM32F429 cсобрал проект, но почему-то микроконтроллер зависает на выполнении процедуры *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*uwIndex) = aTxBuffer[uwIndex]; Подскажите пожалуйста, что не так.
А не так то, что контроллер совсем другой, ножки другие, да и возможно память тоже другая, а дисплей тем более другой — в нём своя память вроде есть.
Подтверждаю: код из репозитория повисает в этом месте.
Доброго времени суток.
Сдублирую вопрос в двух темах, может кто откликнется.
Разбираюсь с отладкой STM32F4-disco на 429 камне. Вроде разобрался с записью и чтением с sdram, но при подключении дисплея возникла странная проблемка. После записи в SDRAM буфера для экрана (просто заливаю зеленым), после нескольких чтений экран из зеленого перетекает в белый. Где я мог накосячить в работе с памятью?
Вопрос решен, проблема была в инициализации памяти. Не включился autorefresh нормально
Из какого урока по Си можно научиться понимать смысл подобных конструкций *(__IO uint32_t*)?
Да из обычных учебников, если глянуть через definition в IDE, то увидишь запись #define volatile __IO. Просто переименовали ключевое слово, дабы компилятор ее не оптимизировал.