Подключение TFT дисплея по FSMC к STM32F103VET6 на примере SSD1289.
Недавно пришла плата с STM32VET6, заказанная на али, и в этой статье мы попробуем подключить к ней TFT дисплей, кстати, на плате предусмотрена отдельная колодка для него.
В отличие от Atmega у STM32 для подключения дисплея предусмотрен специальный интерфейс и называется он FSMC, его также называют контроллером внешней памяти.Смысл работы FSMC заключается в следующем, при подключении по FSMC дисплея или внешней памяти, мы можем обращаться к регистрам подключённого устройства, как будто они находятся в памяти микроконтроллера. Нам не надо дёргать за ножки и отсчитывать временные интервалы, как мы это делали в Atmega, надо просто писать данные в регистр, а обо всём остальном позаботиться интерфейс.
Начнём с подключения, общая схема подключения выглядит так.
Теперь надо разобраться на каких ножках находятся Nex, NOE, NWE, Ax и шина данных. Для этого скачиваем STM32CUBEMX, создаём новый проект для нашего контроллера и во вкладке Peripherals выбираем FSMC, картинки можно увеличить кликнув по ним.
Далее, выставляем настройки как на картинке выше и видим на каких ножках находятся интересующие нас выводы. Если сопоставить их, то получим следующую схему подключения, левый столбец - выводы дисплея, правый - выводы микроконтроллера.
Подсветку подключим к 5V через резистор 200Ohm, чтобы уменьшить нагрузку на стабилизатор 3.3V.
А теперь внимание, начинается интересное!!!
На картинке видно, что адресное пространство разделено на 4 банка.
А первый банк разделен на 4 части. По этому когда мы пишем в диапазон от 0x60000000 до 0x63FFFFFF, автоматически становится активным вывод NE1, если пишем в диапазон от 0x640000000 до 0x67FFFFFF становится активным NE2.
Что касается вывода FSMC_Ax, его состояние которого определяет, что будем слать регистр или данные.
При подключении RS к выводу FSMC_A(Х) надо в LCD_DATA установить в единицу Х+1 бит, не забывая указать адрес банка .
Если в качестве переключающего вывода мы хотим выбрать FSMC_A18 необходимо указать адреса следующим образом.
Теперь когда реализованы низкоуровневые функции, осталось только произвести инициализацию портов и FSMC. За основу возьмём проект под Atmega и перепишем в нём низкоуровневые функции .
Отправка данных выглядит следующим образом:
*(uint16_t *) ( LCD_DATA)= data;
Мы по адресу, выделенному для записи данных, записываем данные)))
LCD_DATA - это адрес(0x60020000), мы не можем просто взять и записать значение по этому адресу сделав вот так
*LCD_DATA = data;
потому что так мы указываем начальный адрес переменной, но не указываем её размер и где она заканчивается.
А если мы укажем (uint16_t *) то становится понятно, что размер переменной 16 бит и от указанного адреса надо считать 16 бит.
Теперь все функции дисплея, которые были написаны для Atmega16, работают на STM32F103VET6. На этом всё, в архиве проект для вывода картинки как на превью, в настройках проекта во вкладке target установить Xtal = 8MHz. .
В отличие от Atmega у STM32 для подключения дисплея предусмотрен специальный интерфейс и называется он FSMC, его также называют контроллером внешней памяти.Смысл работы FSMC заключается в следующем, при подключении по FSMC дисплея или внешней памяти, мы можем обращаться к регистрам подключённого устройства, как будто они находятся в памяти микроконтроллера. Нам не надо дёргать за ножки и отсчитывать временные интервалы, как мы это делали в Atmega, надо просто писать данные в регистр, а обо всём остальном позаботиться интерфейс.
Начнём с подключения, общая схема подключения выглядит так.
Теперь надо разобраться на каких ножках находятся Nex, NOE, NWE, Ax и шина данных. Для этого скачиваем STM32CUBEMX, создаём новый проект для нашего контроллера и во вкладке Peripherals выбираем FSMC, картинки можно увеличить кликнув по ним.
Далее, выставляем настройки как на картинке выше и видим на каких ножках находятся интересующие нас выводы. Если сопоставить их, то получим следующую схему подключения, левый столбец - выводы дисплея, правый - выводы микроконтроллера.
Подсветку подключим к 5V через резистор 200Ohm, чтобы уменьшить нагрузку на стабилизатор 3.3V.
А теперь внимание, начинается интересное!!!
На картинке видно, что адресное пространство разделено на 4 банка.
А первый банк разделен на 4 части. По этому когда мы пишем в диапазон от 0x60000000 до 0x63FFFFFF, автоматически становится активным вывод NE1, если пишем в диапазон от 0x640000000 до 0x67FFFFFF становится активным NE2.
Что касается вывода FSMC_Ax, его состояние которого определяет, что будем слать регистр или данные.
При подключении RS к выводу FSMC_A(Х) надо в LCD_DATA установить в единицу Х+1 бит, не забывая указать адрес банка .
Если в качестве переключающего вывода мы хотим выбрать FSMC_A18 необходимо указать адреса следующим образом.
#define LCD_DATA 0x60080000 //1000 0000 0000 0000 000
#define LCD_REG 0x60000000
Теперь когда реализованы низкоуровневые функции, осталось только произвести инициализацию портов и FSMC. За основу возьмём проект под 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;
}
Отправка данных выглядит следующим образом:
*(uint16_t *) ( LCD_DATA)= data;
Мы по адресу, выделенному для записи данных, записываем данные)))
LCD_DATA - это адрес(0x60020000), мы не можем просто взять и записать значение по этому адресу сделав вот так
*LCD_DATA = data;
потому что так мы указываем начальный адрес переменной, но не указываем её размер и где она заканчивается.
А если мы укажем (uint16_t *) то становится понятно, что размер переменной 16 бит и от указанного адреса надо считать 16 бит.
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);
Теперь все функции дисплея, которые были написаны для Atmega16, работают на STM32F103VET6. На этом всё, в архиве проект для вывода картинки как на превью, в настройках проекта во вкладке target установить Xtal = 8MHz. .
Похожие статьи