Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Недавно пришла плата с STM32VET6, заказанная на али, и в этой статье мы попробуем подключить к ней TFT дисплей, кстати, на плате предусмотрена отдельная колодка для него.
Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

В отличие от Atmega у STM32 для подключения дисплея предусмотрен специальный интерфейс и называется он FSMC, его также называют контроллером внешней памяти.Смысл работы FSMC заключается в следующем, при подключении по FSMC дисплея или внешней памяти, мы можем обращаться к регистрам подключённого устройства, как будто они находятся в памяти микроконтроллера. Нам не надо дёргать за ножки и отсчитывать временные интервалы, как мы это делали в Atmega, надо просто писать данные в регистр, а обо всём остальном позаботиться интерфейс.
Начнём с подключения, общая схема подключения выглядит так.
Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Теперь надо разобраться на каких ножках находятся Nex, NOE, NWE, Ax и шина данных. Для этого скачиваем STM32CUBEMX, создаём новый проект для нашего контроллера и во вкладке Peripherals выбираем FSMC, картинки можно увеличить кликнув по ним.
Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Далее, выставляем настройки как на картинке выше и видим на каких ножках находятся интересующие нас выводы. Если сопоставить их, то получим следующую схему подключения, левый столбец - выводы дисплея, правый - выводы микроконтроллера.
Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Подсветку подключим к 5V через резистор 200Ohm, чтобы уменьшить нагрузку на стабилизатор 3.3V. При конфигурации FSMC надо выбрать вывод из диапазона FSMC_A16 - FSMC_A23 для подключения вывода дисплея RS. Мы выбрали FSMC_A16, как подключить к другому выводу будет рассмотрено ниже.

С подключением разобрались, давайте перейдём к написанию кода. За основу возьмём проект под Atmega и перепишем в нём низкоуровневые функции .

// Определяем адреса
// для записи данных
#define LCD_DATA 0x60020000
// для записи команд
#define LCD_REG 0x60000000

__inline void Lcd_Write_Index(uint16_t index)
{
   *(uint16_t *) (LCD_REG) = index;
}
////////////////////////
__inline void Lcd_Write_Data(uint16_t data)
{
   *(uint16_t *) (LCD_DATA)= data;
}
///////////////////
uint16_t Lcd_Read_Data()
{
   uint16_t data = * (uint16_t *)(LCD_DATA);
   return data;
}
////////////////////////
uint16_t Lcd_Read_Reg(uint16_t reg_addr)
{
   volatile uint16_t data = 0;
   Lcd_Write_Index(reg_addr);
   data = Lcd_Read_Data();
   return data;
}
///////////////////////
void Lcd_Write_Reg(uint16_t reg,uint16_t value)
{
   *(uint16_t *) (LCD_REG) = reg;
   *(uint16_t *) (LCD_DATA) = value;
}

Давайте рассмотрим как они работают. Подключив дисплей по FSMC, а мы подключим его к первому банку, по сути мы добавили в адресное пространство микроконтроллера адреса с 0x60000000 по 0x6003FFFF.
Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Из таблицы выше видно, что для выбора регистра надо записать его номер в любой из адресов от 0x020000 до 0x03FFFF, но так, как мы будем работать с первым банком памяти, то полный диапазон адресов будет выглядеть так 0x60020000-0x6003FFFF. Для записи данных рассуждения аналогичные, надо отметить, что у меня все работало только, если указанный адрес был чётным.

Отправка данных выглядит следующим образом:

*(uint16_t *) ( LCD_DATA)= data;
Мы по адресу, выделенному для записи данных, записываем данные)))
LCD_DATA - это адрес(0x60020000), мы не можем просто взять и записать значение по этому адресу сделав вот так

*LCD_DATA = data;

потому что так мы указываем начальный адрес переменной, но не указываем её размер и где она заканчивается.

А если мы укажем (uint16_t *) то становится понятно, что размер переменной 16 бит и от указанного адреса надо считать 16 бит.

Написанное выше справедливо при подключении FSMC_A16, состояние которого определяет, что будем слать регистр или данные. Если в качестве переключающего вывода мы хотим выбрать FSMC_A18(PD13), он также выведен колодку подключения TFT, то надо инициализировать PD13 и указать адреса следующим образом.

#define LCD_DATA 0x60080000

#define LCD_REG 0x60000000

Таким образом, при подключении RS к выводу FSMC_A(Х) надо в LCD_DATA установить в единицу Х+1 бит, не забывая указать адрес банка.
Теперь когда реализованы низкоуровневые функции, осталось только произвести инициализацию портов и FSMC.

FSMC_NORSRAMInitTypeDef fsmc;
FSMC_NORSRAMTimingInitTypeDef fsmcTiming;
GPIO_InitTypeDef gpio;

// Включаем тактирование портов
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE , ENABLE);

// Включаем  тактирование FSMC
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);

// Инициализация пинов, задействованных в общении по FSMC
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &gpio);

gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &gpio);
// Reset
gpio.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOE, &gpio);
/////////////////////////////////
// CS -> 1
// Reset -> 0
// RD -> 1
// RW -> 1

GPIO_SetBits(GPIOD, GPIO_Pin_7);
GPIO_ResetBits(GPIOE, GPIO_Pin_1);
GPIO_SetBits(GPIOD, GPIO_Pin_4);
GPIO_SetBits(GPIOD, GPIO_Pin_5);

// Настройка FSMC
fsmcTiming.FSMC_AddressSetupTime = 0x02;
fsmcTiming.FSMC_AddressHoldTime = 0x00;
fsmcTiming.FSMC_DataSetupTime = 0x05;
fsmcTiming.FSMC_BusTurnAroundDuration = 0x00;
fsmcTiming.FSMC_CLKDivision = 0x00;
fsmcTiming.FSMC_DataLatency = 0x00;
fsmcTiming.FSMC_AccessMode = FSMC_AccessMode_B;


fsmc.FSMC_Bank = FSMC_Bank1_NORSRAM1;
fsmc.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
fsmc.FSMC_MemoryType = FSMC_MemoryType_NOR;
fsmc.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
fsmc.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
fsmc.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
fsmc.FSMC_WrapMode = FSMC_WrapMode_Disable;
fsmc.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
fsmc.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
fsmc.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
fsmc.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
fsmc.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
fsmc.FSMC_ReadWriteTimingStruct = &fsmcTiming;
fsmc.FSMC_WriteTimingStruct = &fsmcTiming;

FSMC_NORSRAMInit(&fsmc);
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);

Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.

Теперь все функции дисплея, которые были написаны для Atmega16, работают на STM32F103VET6. На этом всё, в архиве проект для вывода картинки как на превью, в настройках проекта во вкладке target установить Xtal = 8MHz. ssd1289_stm32f103VET6_example.rar [25,75 Kb] (cкачиваний: 616).
комментарии
0