Урок 66
Часть 1
HAL. LTDC. SDRAM
Сегодня мы попробуем в качестве памяти для дисплея воспользоваться внешней памятью типа SDRAM. Работаем мы с платой STM32 F746-DISCOVERY, в которой данная память уже входит в комплект и с которой мы уже научились работать, изучая урок 62. С интерфейсом LTDC мы тажке умеем работать благодаря уроку 64. Поэтому нам будет легче. В вышеуказанном уроке мы работали с интерфейсом, используя в качестве памяти область оперативной памяти. Но это хорошо только для занятий. В жизни это не годится. Во-первых, потому что мы «съели» почти всю память, а во-вторых нам даже этого не хватило для работы с полноправным 32-битным форматом пикселя. В принципе, мы уже работали с дисплеем в связке с памятью SDRAM, но это было с применением библиотеки BSP, поэтому сами мы особого полезного кода там не написали. А сегодня мы попробуем обрести самостоятельность в данном вопросе и отвязать код от данной библиотеки. Мы, конечно, будем пользоваться некоторым передовым опытом, который накоплен благодаря глубокому внутреннему изучению кода библиотеки, но, самое главное, подключать нам её к каждому проекту не придётся и мы уже не будем привязаны к конкретной плате и микросхеме и можем смело свой код переделывать под любой дисплей и под любую память SDRAM.
Итак начнём с проекта.
Проект мы переделаем из проекта LTDC001 урока 64 и назовём его уже LTDC_SDRAM. Запустим наш проект в программе-генераторе проектов Cube MX и начнём с ним колдовать дальше.
Включим FMC следующим образом
Переопределим ножку PH2 (K4) на PC3 (L4)
Зайдём в Clock Configuration и установим следующие делители
В результате данных изменений у нас частота тактирования LCD установится в 16 МГц
Перейдём в Configuration и следующим образом настроим FMC
Теперь перенастроим LTDC — Layer Settings
В Cortex M7 MPU изменим следующим образом
Сгенерируем код, откроем его в Keil, настроим программатор на аторезет.
Перепишем из проекта FMC_SDRAM файлы MT48LC4M32B2.c и MT48LC4M32B2.h в соответствующие папки, затем в дерево проектов в Keil добавим файлы MT48LC4M32B2.c и ltdc.c.
Попробуем собрать код.
Если всё нормально собралось, то подключим библиотеку FMC в файле main.c
#include «ltdc.h»
#include «stdint.h»
#include «MT48LC4M32B2.h»
Заменим объявление ниже
/* USER CODE BEGIN PV */
/* Private variables ———————————————————*/
volatile uint32_t RGB565_480x272[65280] = {0x00000000};
#define LCD_FRAME_BUFFER SDRAM_DEVICE_ADDR
/* USER CODE END PV */
Добавим инициализацию памяти SDRAM в main()
/* USER CODE BEGIN 2 */
MT48LC4M32B2_init(&hsdram1);
В следующей функции изменим входной параметр
MT48LC4M32B2_init(&hsdram1);
HAL_LTDC_SetAddress(&hltdc,LCD_FRAME_BUFFER,0);
Зайдём в заголовочный файл MT48LC4M32B2.h и несколько изменим там имя макроса стартового адреса памяти SDRAM и добавим ещё один макрос
#define SDRAM_DEVICE_ADDR ((uint32_t)0xC0000000)
#define REFRESH_COUNT ((uint32_t)0x0603) /* SDRAM refresh counter (100Mhz SD clock) */
Перейдём в файл реализации MT48LC4M32B2.c и удалим весь вот этот код в конце функции инициализации памяти
/* 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));
Вместо данного кода добавим другой
hal_stat = HAL_SDRAM_SendCommand(hsdram,&command,SDRAM_TIMEOUT);
HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
Теперь перейдём в другой файл — ltdc.с и удалим там объявление
extern volatile uint32_t RGB565_480x272[65280];
Ко всем именам функций кроме функции отрисовки прямой линии пристроим суффикс _565, так как они работают именно с таким форматом пикселя, а у нас теперь другой. Имена изменятся на TFT_FillScreen565, TFT_FillRectangle565 и TFT_DrawPixel565. В прототипах также исправим.
Теперь начнем писать функции без префиксов, только в некоторых местах входные переменные изменят разрядность и, само собой, тела также притерпят некоторые изменения, что не помешает нам, конечно, пользоваться кодом из уже написанных функций.
Напишем функцию заливки всего экрана определённым цветом
//————————————————-
void TFT_FillScreen(uint32_t color)
{
uint32_t i;
uint32_t n = hltdc.LayerCfg[0].ImageHeight*hltdc.LayerCfg[0].ImageWidth;
for(i=0;i<n;i++)
{
*(__IO uint32_t*) (hltdc.LayerCfg[0].FBStartAdress + (i*4)) = color;
}
}
//————————————————-
Напишем на неё прототип.
Напишем также функцию отрисовки пикселя
//————————————————-
void TFT_DrawPixel(uint16_t Xpos, uint16_t Ypos, uint32_t color)
{
*(__IO uint32_t*) (hltdc.LayerCfg[0].FBStartAdress + (4*(Ypos*hltdc.LayerCfg[0].ImageWidth + Xpos))) = color;
}
//————————————————-
Напишем также и на эту функцию прототип и перейдём в функцию main(), закомментируем пока весь код в бесконечном цикле и исправим вызов функции для заливки экрана до бескоечного цикла, чтобы попробовать залить экран, например, зелёным цветом
HAL_LTDC_SetAddress(&hltdc,LCD_FRAME_BUFFER,0);
TFT_FillScreen(0xFF00FF00);
Соберём код, прошьём контроллер и посмотрим результат
В следующей части нашего урока мы проверим работу дисплея с памятью SDRAM при помощи нескольких тестов по выводу определённых примитивов на его экран.
Предыдущий урок Программирование МК STM32 Следующая часть
Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY
Смотреть ВИДЕОУРОК (нажмите на картинку)
По аналогии с чипселектом для SRAM аналогичные сигналы как японял так же привязаны к банку памяти? Вопрос возник в связи с тем, что у меня на дивелоперской плате по PDF этот сигнал выведен на PF6, а это соответсвует CDCKE1+CDNE1. И следовательно выбираем второй банк SDRAM, во вкладке region адрес устанавливаем 0xD0000000.
Все, разобрался. Сигнал CDCKE1+CDNE1 можно использовать и в SDRAM1 и в SDRAM2, при этом эти два сигнала определяют область памяти CDCKE0+CDNE0 — это облась 0xC0000000 — 0xcFFFFFFF, а CDCKE1+CDNE1 — это 0xD0000000 — 0xDFFFFFFF. Полностью работает проект написанный для color565 и более того.
Отлично, что разобрались. Поздравляю!
Подскажите собрал на строил все по инструкции но у меня на экране выводит например зеленый цвет и изображение плохое как будто матрица не исправна. 746disco