Инициализация TFT дисплея на примере ILI9341 для AVR.

Инициализация TFT дисплея на примере ILI9341 для AVR.

Около года тому назад на сайте появилась статья о том как инициализировать TFT дисплей, под управлением SSD1289, а где-то около месяца назад мне написал один из посетителей сайта. Суть письма была в том, что он заказал дисплей по указанной в статье ссылке, но запустить его не получалось и он предложил этот дисплей и ещё несколько других выслать мне, а я, в свою очередь, должен буду выложить код если получится их запустить.
 
Как оказалось, дисплей, который ему прислали с али управляется драйвером ILI9341, об этом помогла догадаться надпись на нём.
Верхний дисплей с драйвером SSD1289, нижний с ILI9341.
Инициализация TFT дисплея на примере ILI9341 для AVR.

Поэтому как только ко мне пришёл этот дисплей, сразу начал изучать даташит на ILI9341.
 
В отличие от SSD1289 у ILI9341 нет регистров, для общения с ним используются команды. Сначала посылаешь команду, а затем набор параметров, то есть после того, как мы послали команду дисплей уже ждёт, что ему передадут параметры. Также надо сказать, что общение с дисплеем осуществляется по одному из двух протоколов: интеловский i8080 и мотороловский M6800, чем они отличаются описывал тут, не стал изменять традициям и выбрал i8080. У этого протокола существует две реализации, для получения более подробной информации можно почитать даташит.
 
Первым делом необходимо реализовать низкоуровневые функции и тут важно понять, что мы правильно понимаем даташит. Самый простой способ проверить, правильность понимания даташита, это считать какую-то информацию с дисплея, например, его ID.
Инициализация TFT дисплея на примере ILI9341 для AVR.

На картинке видно, что если контроллеру отправить команду 0xD3h и 4 раза считать данные, то в последние два считывания он должен вернуть ID(9341) драйвера. Но для начала надо разобраться как отправлять команды и читать данные, для этого нам понадобятся три картинки из даташита.
Первая, определяет управляющие сигналы для выбранного интерфейса.
Инициализация TFT дисплея на примере ILI9341 для AVR.

Вторая и третья определяют их последовательность, для записи
Инициализация TFT дисплея на примере ILI9341 для AVR.

для чтения
Инициализация TFT дисплея на примере ILI9341 для AVR.


Тогда функция, для отправки команды, будет выглядеть так.
void TFT_Send_Cmd(uint8_t cmd)
{
	COMMAND_PORT &= ~(1<<lcd_dc);   //будем слать команду
	COMMAND_PORT |= (1<<lcd_rd);	//выставляем на ножке, отвечающей за чтение 1
	
	COMMAND_PORT &= ~(1<<lcd_cs);   //активируем чип
	
	COMMAND_PORT1 &= ~(1<<lcd_wr);	//стробируем битом записи
	DATA_PORT_0 = cmd;
	_delay_us(5);
	COMMAND_PORT1 |= (1<<lcd_wr);	

	COMMAND_PORT |= (1<<lcd_cs);    //деактивируем чип
	
}

А функция которая читает данные так.
uint8_t TFT_Read_Data(void)
{
	uint8_t data = 0;
	DATA_DDR_0 = 0X00;	//порт на вход с подтяжкой к земле
	DATA_PORT_0 = 0x00;
	COMMAND_PORT |= (1<<lcd_dc);	 //будем читать ДАННЫЕ
	COMMAND_PORT1 |= (1<<lcd_wr);	 //выставляем на ножке, отвечающей за запись 1
	
	COMMAND_PORT &= ~(1<<lcd_cs);    //активируем чип
	
	COMMAND_PORT &= ~(1<<lcd_rd);    //стробируем битом чтения
	_delay_us(5);
	data = PINA;
	COMMAND_PORT |= (1<<lcd_rd);
	
	COMMAND_PORT |= (1<<lcd_cs);    //деактивируем чип
	DATA_DDR_0 = 0XFF;				//порт на выход
	return data;
}

Инициализация TFT дисплея на примере ILI9341 для AVR.

Инициализация TFT дисплея на примере ILI9341 для AVR.

Из скриншотов понятно, что описанные выше функции заработали, осталось реализовать отправку данных по 8 и 16 бит. Дело в том, что в основном при отправке данных используются только младшие 8 бит шины, но, например, при записи данных в память дисплея используются все 16 бит.

void TFT_Write_Data(uint8_t data)
{
	COMMAND_PORT |= (1<<lcd_dc);	//будем слать ДАННЫЕ
	COMMAND_PORT |= (1<<lcd_rd);	//выставляем на ножке, отвечающей за чтение 1
	
	COMMAND_PORT &= ~(1<<lcd_cs);   //активируем чип

	COMMAND_PORT1 &= ~(1<<lcd_wr);	//стробируем битом записи
	DATA_PORT_0 = data;
	_delay_us(5);
	COMMAND_PORT1 |= (1<<lcd_wr);

	COMMAND_PORT |= (1<<lcd_cs);    //деактивируем чип
}

void TFT_Write_Data16(uint16_t data)
{
	COMMAND_PORT |= (1<<lcd_dc);	//будем слать ДАННЫЕ
	COMMAND_PORT |= (1<<lcd_rd);	//выставляем на ножке, отвечающей за чтение 1

	COMMAND_PORT &= ~(1<<lcd_cs);   //активируем чип
	
	COMMAND_PORT1 &= ~(1<<lcd_wr);	//стробируем битом записи
	DATA_PORT_0 = (data & 0x00ff);
	DATA_PORT_8 = (data >> 8);
	_delay_us(5);
	COMMAND_PORT1 |= (1<<lcd_wr);
	
	COMMAND_PORT |= (1<<lcd_cs);    //деактивируем чип
}

Всё, мы реализовали все необходимые низкоуровневые функции, теперь можно переходить непосредственно к инициализации. Перед тем как перейти к инициализации, надо сказать, что напряжение питания ЖК-ячейки постоянно изменяет свою полярность, сделано это для того, чтобы избежать явлений гидролиза и диссоциации сложных органических соединений, из которых состоит жидкокристаллический материал.

void Init_ILI9341(void)
{
	//конфигурируем все используемые выводы как выходы
	DATA_DDR_0 = 0XFF;
	DATA_DDR_8 = 0XFF;
	COMMAND_DDR = 0XFF;
	COMMAND_DDR1 = 0X01;
	
	COMMAND_PORT |= (1<<lcd_res);	

	TFT_Send_Cmd(0x01);        //Software Reset
	_delay_ms(1000);
	

	//Power Control 1
	TFT_Send_Cmd(0xC0);		//задаём градацию серого цвета
	TFT_Write_Data(0x25);	


	//Power Control 2
	TFT_Send_Cmd(0xC1);		//настройка step up преобразователя
	TFT_Write_Data(0x11);


	//VCOM Control 1
	TFT_Send_Cmd(0xC5);		//контрастность определяется разностью VCOMH - VCOML = 5.2V
	TFT_Write_Data(0x2B);	        //VCOMH = 3.825
	TFT_Write_Data(0x2B);  	//VCOML = -1.375
	
	//VCOM Control 2
	TFT_Send_Cmd(0xC7);		//на Vcom по сути ШИМ, а тут мы задаем offset для него
	TFT_Write_Data(0x86);        //VML=58 VMH=58

	//Memory Access Control
	TFT_SetOrientation(orient); //выбираем ориентацию дисплея
	
	//COLMOD: Pixel Format Set
	TFT_Send_Cmd(0x3A);		//один пиксель будет кодироваться 16 битами
	TFT_Write_Data(0x05);
	
	//Frame Rate Control 
	TFT_Send_Cmd(0xB1);
	TFT_Write_Data(0x00);
	TFT_Write_Data(0x18);	 //Frame Rate 79Hz


	//Display Function Control
	TFT_Send_Cmd(0xB6);
	TFT_Write_Data(0x0A);
	TFT_Write_Data(0x82);//восьмой бит определяет нормальный цвет кристала белый - 1, черный - 0,
	TFT_Write_Data(0x27);

	// Sleep Out
	TFT_Send_Cmd(0x11);

	_delay_ms(120);

	//Display On
	TFT_Send_Cmd(0x29);
}

В функции инициализации есть функция TFT_SetOrientation(2), которая определяет порядок отрисовки, MAX_X и MAX_Y объявлены глобально и инициализированы нулями.

void TFT_SetOrientation(uint8_t orient)
{
	TFT_Send_Cmd(0x36);
	switch (orient)
	{
		case 0: TFT_Write_Data(0x48);
				break;
		case 1: TFT_Write_Data(0x28);
				break;
		case 2: TFT_Write_Data(0x88);
				break;
		case 3: TFT_Write_Data(0xE8);
				break;
	}
	if (orient == 0 || orient == 2)
	{
		MAX_X = 239;
		MAX_Y = 319;
	}
	else
	{
		MAX_X = 319;
		MAX_Y = 239;
	}
}

А вот как выглядит дисплей после инициализации.
Инициализация TFT дисплея на примере ILI9341 для AVR.

Чтобы понять, что он точно инициализировался, закрасил его красным цветом.
Инициализация TFT дисплея на примере ILI9341 для AVR.

Как это сделать буду описывать в следующей статье.
Теперь что касается схемы подключения так, как распиновка 40 выводных дисплеев SSD1289 и ILI9341 одинаковая, схему подключения оставил без изменения и выглядит она так.
Инициализация TFT дисплея на примере ILI9341 для AVR.

Таким образом, можно сделать одну отладочную плату для обоих дисплеев и ещё важно чтобы напряжение питания МК и дисплея было 3.3 вольта, при питании от 5 вольт дисплей не запустился. Подсветку дисплея тоже запитал от 3.3 вольта через резистор 47Ом.
 
А на этом всё, в следующей статье мы рассмотрим как выводить символы на дисплей и там же будет проект для Atmega16 в AtmelStudio6.2.

Для желающих быстро проверить работает ли их дисплей, оставляю тут прошивку для Atmega16, которая заливает дисплей разными цветами ili9341_i8080.hex [3,54 Kb] (cкачиваний: 628).

Недавно приобрёл пару дисплеев тут.
комментарии
27