Работа с графическим дисплеем SSD1298

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

Данная статья преследует цель рассмотреть нюансы управления большим цветным LCD при помощи микроконтроллера серии STM8. Рассматривать будем на примере дисплея китайского производства типа SX-TG280SDCPET00 с контроллером SSD1298 и STM8L162S8T6. Дисплей данный был выдран из игровой приставки QUMO GameBox LE. Вот такой:


 
Дисплей может называться и по-другому. Главное – чтоб контроллер в нем был SSD1298. Да и код можно легко портировать куда угодно, благо на Си написан. Вообще их можно купить в китае на Али, например тут.

Ниже я приведу коды и для других типов контроллеров, но за их неимением не могу проверить работоспособность.
 

Описание дисплея и его контроллера
Дисплей с контроллером SSD1298 представляет собой удобный для крепления и пайки законченный модуль с гибким шлейфом. К основной плате устройства он соединяется методом пайки 37-ми контактов.
 

  • Разрешение: 320*240 точек
  • Размер видимой области: 58*49 мм
  • Общий размер модуля: 50*70 мм.

 

Контроллер предоставляет широкие возможности для конфигурации вывода изображения: Есть возможность вывода информации при различном повороте дисплея*, определение области вывода информации*, определение порядка поступления данных о цвете точки и количестве бит на точку* и прокрутка видимой области. Все это делается путем записи определенных значений в регистры конфигурации контроллера.
 

Запуск дисплея
Порядок следующий
 

1. Произвести аппаратный сброс контроллера подачей низкого логического уровня на вход /RESET дисплея;

1
2
delay_ms(100);  // Выжидаем некоторое время (может потребоваться на больших частотах)
LCD_RES = 1;    // Деактивируем аппаратный сброс, который был активирован при настройке портов МК

 

2. Настроить внутренние регистры конфигурации:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  // Теперь записываем данные для настройки контроллера дисплея
  // Эти параметры я брал в интернете. Сначала происходит настрока внутреннего преобразователя
  // для питания стекляшки. Эти параметры я менять не советую.
  LCD_WriteReg(0x0028, 0x0006);  	// VCOM OTP
  LCD_WriteReg(0x0000, 0x0001);  	// Oscillation start
  LCD_WriteReg(0x0003, 0xaea4);  	// power control 1---line frequency and VHG,VGL voltage
  LCD_WriteReg(0x000c, 0x0004);  	// power control 2---VCIX2 output voltage
  LCD_WriteReg(0x000d, 0x000c);  	// power control 3---Vlcd63 voltage
  LCD_WriteReg(0x000e, 0x2800);  	// power control 4---VCOMA voltage VCOML=VCOMH*0.9475-VCOMA
  LCD_WriteReg(0x001e, 0x00b5);  	// POWER CONTROL 5---VCOMH voltage
 
  // Следующие параметры описаны в ДШ на стр. 30-31. Они определяют порядок 
  // сканирования строк, порядок вывода цветов и др.
  LCD_WriteReg(0x0001, 0x3B3F);  	// Driver Output Control
 
  // Следующую строку также менять не советую
  LCD_WriteReg(0x0002, 0x0600);  	// LCD Drive AC Control
 
  // Регистр управления режимом Sleep. Может понадобиться для устройств с батарейным 
  // питанием Для справки: Дисплей потребляет от 2,5 до 8 мА в активном режиме
  // и от 70 до 300 мкА в спящем (стр. 63 ДШ)
  LCD_WriteReg(0x0010, 0x0000);  	// Sleep Mode
 
  // Режим позиционирования и направления сканирования дисплея.
  // Для данных значений при расположении дисплея в Landscape контроллер справа
  // нижний левый угол будет иметь координаты 0,0
  // Я такие значения применил для удобства вывода изображений из BMP файлов
 
  // Более подробно по данной установке можно прочитать на стр. 43-46 ДШ
  LCD_WriteReg(0x0011, 0x6838);  // Entry Mode

 

Вот регистр с адресом 0x0011 представляет большой интерес. Он позволяет управлять дисплеем в зависимости от его положения в устройстве.
Ниже вырезка из ДШ с указанием названий битов:
 

За ориентацию и направление «развертки» отвечают биты AM, ID0 и ID1.
В зависимости от их значения сканирование и вывод данных из экранного ОЗУ будет выводиться по следующим алгоритмам:
 

Значение в моих исходниках установлено для Landscape режима (шлейф справа). Для Portrait положения дисплея строка будет иметь вид LCD_WriteReg(0x0011,0x6830); Шлейф в таком случае должен располагаться снизу.
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  // Не нашел этих параметров в ДШ, но переписывать не стал...
  LCD_WriteReg(0x0005, 0x0000);  // ???
  LCD_WriteReg(0x0006, 0x0000);  // ???
  LCD_WriteReg(0x0016, 0xef1c);  // ???
 
// Следующие настройки можно посмотреть на стр. 39 ДШ. Я их не использую и не пробовал, 
//т.к. нет надобности.
  LCD_WriteReg(0x0007, 0x0033);  // Display control 1
    /* when GON=1 and DTE=0,all gate outputs become VGL */
    /* when GON=1 and DTE=0,all gate outputs become VGH */
    /* non-selected gate wires become VGL */
 
  // Далее расположены настройки размера видимой области дисплея. Их можно менять
  // по ходу работы при необходимости вывода в определенную область экрана
  LCD_WriteReg(0x000b, 0x0000);  // Frame Cycle Control
  LCD_WriteReg(0x000f, 0x0000);  // Gate Scan Start Position
 
  // По следующим двум строкам ничего не подскажу – прокрутку не использовал…
  LCD_WriteReg(0x0041, 0x0000);  // Vertical Scroll Control 1
  LCD_WriteReg(0x0042, 0x0000);  // Vertical Scroll Control 2

 

По следующему коду тоже сказать ничего не могу. Не использовал эту возможность. Из чтения ДШ сделал вывод, что эти регистры используются при делении дисплея на 2 части.
 

1
2
3
4
  LCD_WriteReg(0x0048, 0x0000);  // First Window Start
  LCD_WriteReg(0x0049, 0x013f);  // First Window End
  LCD_WriteReg(0x004a, 0x0000);  // Second Window Start
  LCD_WriteReg(0x004b, 0x0000);  // Second Window End

 

Далее настраивается диапазон (окно) для вывода изображения или текста. Сейчас здесь записаны максимальные значения и вывод будет осуществляться на весь экран, но если Вы используете быстрое обновление определенного небольшого участка экрана, не затрагивая остальные области, разумно ограничить область вывода путем записи адресов крайних точек в регистры окна:

Для пояснения процесса вывода в ДШ есть вот такое изображение:
http://easyelectronics.ru/img/STM8/displ/lcd_conf_2.jpg

1
2
3
  LCD_WriteReg(0x0044, 0xef00);  // Horizontal RAM start and end address
  LCD_WriteReg(0x0045, 0x0000);  // Vertical RAM start address
  LCD_WriteReg(0x0046, 0x013f);  // Vertical RAM end address

 

Счетчики строк и столбцов. Я их меняю функцией LCD_SetCursor. При запуске, ясное дело, устанавливаются в 0, чтоб рисовать с начального адреса видео ОЗУ.
 

1
2
  LCD_WriteReg(0x004e, 0x0000);  // Set GDDRAM X address counter
  LCD_WriteReg(0x004f, 0x0000);  // Set GDDRAM Y address counter

 

3. Настройки регистров гамма-коррекции. Для предотвращения искажения цвета не рекомендую менять их. Данные значения я выдрал из какого-то универсального драйвера. И в ДШ их не нашел, в инете на данный контроллер тоже.
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  LCD_WriteReg(0x0030,0x0707);
  LCD_WriteReg(0x0031,0x0202);
  LCD_WriteReg(0x0032,0x0204);
  LCD_WriteReg(0x0033,0x0502);
  LCD_WriteReg(0x0034,0x0507);
  LCD_WriteReg(0x0035,0x0204);
  LCD_WriteReg(0x0036,0x0204);
  LCD_WriteReg(0x0037,0x0502);
  LCD_WriteReg(0x003a,0x0302);
  LCD_WriteReg(0x003b,0x0302);
  LCD_WriteReg(0x0023,0x0000);
  LCD_WriteReg(0x0024,0x0000);
  LCD_WriteReg(0x0025,0x8000);
  LCD_WriteReg(0x0026,0x7000);
  LCD_WriteReg(0x0020,0xb0eb);
  LCD_WriteReg(0x0027,0x007c);

 

Перечисленные настройки (те, которые я использовал) уже позволяют формировать на экране дисплея изображения, отображать текст. Ввиду наличия у меня медленного контроллера я не пытался вывести видео и использовать дисплей полностью, со всеми его возможностями.
После выполнения вышеописанного кода на экране должен появиться «мусор» из случайным образом засвеченных точек. Для того чтоб очистить экран необходимо очистить содержимое экранного ОЗУ контроллера. За это у нас отвечает функция LCD_Clear(unsigned int Color);
 

Перед тем, как ее описать, приведу код еще нескольких вспомогательных функций.
Сразу скажу, что шина данных у меня указана «в лоб», без дефайнов, а шина управления переобозвана:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Обзываем линии шины данных для удобства
#define LCD_CS  PH_ODR_bit.ODR4
#define LCD_RS  PH_ODR_bit.ODR5
#define LCD_WR  PH_ODR_bit.ODR6
#define LCD_RD  PH_ODR_bit.ODR7
#define LCD_RES PD_ODR_bit.ODR4
 
// Процедура записи адреса регистра
void LCD_WriteIndex(unsigned int index)
{
  LCD_RS = 0; 		// Тип данных - команда (в данном случае адрес)
  LCD_RD = 1; 		// При записи RD должен быть стопудово равен 1, а то не запишет
  PB_ODR = (index & 0x00FF);		// Выставляем младшую часть адреса на шину
  PF_ODR = ((index & 0xFF00) >> 8);	// и старшую
  LCD_WR = 0; 		// Следующие три строки - строб /WR
  delay_us(5);
  LCD_WR = 1;
}
 
// Процедура записи данных в регистр или ОЗУ
void LCD_WriteData(unsigned int data)
{
  LCD_RS = 1; 			// Тип данных - данные
  PB_ODR = (data & 0x00FF); 	// Вывод на шину старшего
  PF_ODR = ((data & 0xFF00) >> 8);  	// и младшего байтов данных
  LCD_WR = 0; 			// Строб
  delay_us(5);
  LCD_WR = 1;
}

 

Ну и еще больше упрощаем себе жизнь, описав функцию записи данных в регистр одной строкой:

1
2
3
4
5
6
7
8
9
10
<code>
// Процедура записи данных в регистр
// Очень полезна при инициализации
void LCD_WriteReg(unsigned int LCD_Reg, unsigned int LCD_RegValue)
{
  LCD_CS = 0;			// Активируем Chip Select
  LCD_WriteIndex(LCD_Reg);	// Пишем адрес регистра
  LCD_WriteData(LCD_RegValue);	// Пишем в него данные
  LCD_CS = 1;			// Деактивируем Chip Select
}

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Функция очистки дисплея заливает весь экран цветом, указанным в аргументе Color
void LCD_Clear(unsigned int Color)
{
  unsigned long index=0;
  LCD_SetCursor(0,0);		// Устанавливаем курсор в начало
  LCD_CS = 0;			// Здесь запись организована "атомарно" для ускорения процедуры
  LCD_WriteIndex(0x0022); 	// Запись этого адреса в регистр индекса означает, что
                          	// сейчас будут выводиться данные в экранное ОЗУ
                          	// Как себя будут вести счетчики строк и столбцов определяется
                          	// регистром с адресом 0x0011
  for(index=0;index < 0x12C00;index++)
	{ 			// цикл с количеством итераций 320*240
    	LCD_WriteData(Color); 	// ставим точки заданного цвета
 	}
  LCD_CS = 1;         		// Отцепляемся от дисплея
}

 

Засветка точки осуществляется записью в регистр 0x0022 кода цвета точки.
 

Координаты засвечиваемой точки определяются содержимым регистров 0x004e и 0x004f (см. выше). После записи точки счетчики адреса автоматически обновляются согласно установленным правилам (регистр 0x0011, см. выше).
 

Обратите внимание, что адрес регистра в вышеописанной функции записывается один раз, затем чередой поступают данные. При достижении счетчиком адреса строк (столбцов) края окна, размеры которого установлены в регистрах 0x0044 — 0x0046 (см. выше), происходит обнуление счетчика строк (столбцов) и изменение счетчика столбцов (строк). То есть развертка похожа на телевизионную.
 

Если дисплей поддерживает чтение данных из области видео ОЗУ, возможно чтение содержимого экрана в контроллер. Я не использовал такой возможности опять же из-за низкой рабочей частоты МК, но хотелось бы для нормального отображения букв на цветном фоне.
 

Подключение дисплея
Мой экземпляр дисплея имеет 16-битную шину данных и шину управления с 5 линиями, но сам контроллер дисплея имеет несколько вариантов шин (16 и 8 бит параллельная;16, 9 и 8 бит последовательная), тип которых выбирается замыканием соответствующих линий на землю. Эти линии замкнуты на гибком шлейфе дисплея и, скорее всего, изменить тип шины не удастся.
 

С целевой платформой дисплей соединяется посредством пайки 37-ми контактов:

Поясню их назначение:

  • DB00-DB15 – шина данных;
  • /CS – Chip Select;
  • RS (DC по даташиту) – команда / данные;
  • /WR – строб записи;
  • /RD – строб чтения;
  • /RESET – аппаратный сброс;
  • IM0, IM3 – выводы конфигурации шины;
  • Y-,Y+,X-,X+ — выводы тачскрина;
  • LEDA, LEDK1-LEDK4 – выводы диодов подсветки;
  • VDD – питание;
  • GND – земля.

 

Взаимодействие с дисплеем может осуществляться при низком логическом уровне на его выводе /CS . По шине данных мы передаем на контроллер команды и данные и можем считывать его байт состояния, тип контроллера и данные, записанные в экранную область ОЗУ контроллера.
 

Выбор типа передаваемых данных осуществляется установкой вывода RS в соответствующее состояние (команда – 0, данные – 1). Передача данных/команд в дисплей сопровождается стробом линии /WR, чтение – стробом /RD. Вывод аппаратного сброса /RESET предназначен для установки в начальное состояние всех регистров управления контроллера дисплея (но не данных в экранном ОЗУ!) после подачи на него питания (по линиям VDD, GND).
 

В ДШ есть изображение, объясняющее принцип общения с дисплеем:

Как видно из рисунка, для обращения к дисплею используется немного другой алгоритм – обращение по стробу /CS. У меня же – Стробом /WR или /RD. Я пробовал и так и так – работает, так что тут Вы вольны выбирать сами. Вывод E (OE) у меня не выведен на шлейф, но это не принципиально.
 

Итог

  • Для вывода точки необходимо инициализировать дисплей:
  • произвести аппаратный сброс
  • заполнить регистры конфигурации начальными значениями.
  • Затем установить счетчики адреса экранного ОЗУ в необходимое состояние
  • записать в индексный регистр адрес 0x0022 (функцией LCD_WriteIndex(unsigned int index))
  • вывести данные, соответствующие цвету точки.

 

Хочу предупредить об одном подводном камне. Не планируя читать данные из дисплея я поначалу не развел плату под вывод /RD, и не смог запустить дисплей. После, установив стабильный логический уровень на этом выводе, получил и стабильную работу. Не оставляйте висеть рабочие выводы!
 

С выводами подсветки дисплея все просто – тут имеется 4 светодиода с общими анодами и разделенными катодами. Для их включения необходимо применить гасящие резисторы. Я установил 4 резистора по 68 Ом (питание – 3,3 в) и яркости мне хватает.
 

Выводы тачскрина полезны при его наличии. Мне не повезло – дисплей попался без тачскрина и в магазине такой диагонали не оказалось. Дисплеи с данным контроллером могут быть разной диагонали, в зависимости от размера пикселя. Кроме того, на шлейфе дисплея место для подпайки тачскрина может не совпадать со шлейфом самого тачскрина, поэтому не лоханитесь, как я, покупайте тачскрин, если необходимо, взяв с собой дисплей.
 

Отдельного внимания заслуживают выводы IM0 и IM3 – именно с их помощью выбирается тип шины дисплея. Тут есть нюанс. Выводы могут быть подключены к контроллеру дисплея, а, в моем случае, нет. То есть я не могу поменять 16 разрядную шину на 8-ми. В Вашем случае они могут быть подключены, что нетрудно выяснить, проследив дорожки от выводов к контроллеру. Выбор типа шины и тайминги хорошо описаны в даташите на контроллер.
 

Если вдруг Вам достанется дисплей с подключенными на шлейфе линиями выбора шины, то вот картинка из ДШ, объясняющая как получить тот или иной протокол обмена:

Ввиду того, что шина моего дисплея имеет ширину 16 бит мне пришлось задействовать под это дело 2 порта микроконтроллера. В моем случае это порты B и F. Под линии шины управления у меня отведены старшая тетрада порта H и линия порта D. Линии выбраны из-за удобства разводки печатной платы.
 

Как видите, схема до безобразия проста.
 

Описание протокола взаимодействия
Итак, я упоминал, что подключать данный дисплей мы будем к МК семейства STM8L. При написании своих программ я не использую библиотек от STM. Не знаю, плохо это или хорошо, но мне так удобнее. Тем более, что при таком подходе обращения к портам становятся прозрачными и любители AVR и тому подобных PIC’ов смогут легко переписать код под свои нужды и камни. В принципе, код прокомментирован, по большей части разобран выше и прост, так что, настроить порты особого труда не составит.
 

1298.с

Сложной мне представляется здесь функция вывода текста, разберем ее подробнее.
 

Для формирования буквы я использую внешний хидер с описанием изображения символов — NewFont.h (вложен в архив). Исходный проект этого шрифта также вложен в архив. Программа Light Font Generator позволяет рисовать шрифты и потом получать на выходе файлы – хидеры для использования в среде CodeVisionAVR. Ну а IAR чем хуже? :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
void LCD_Putchar(unsigned char chr)
{					// Аргумент - код буквы 
  unsigned char i, a, tmp;
  unsigned int addr;
  extern unsigned int paper, ink; 	// Глобальные переменные - цвет фона и буквы
  extern unsigned int X;          	// Глобальные переменные позиции буквы
  extern unsigned char Y;
 
  switch(chr)				// Так я организовал переход на новую строку
	{   
   	case 10: Y -= 9; break;
    	case 13: X = 3;  break;  	// И "возврат каретки"
      	default:
	{
      	addr = chr << 3;
	tmp = Y;  // сохраняем позицию для возврата при отображении новой строки точек
      	for(i=0;i<8;i++)
		{     //Счетчик строки знакоместа
        	LCD_WriteReg(0x004e,Y); 			// Установка координаты Y
        	LCD_CS = 0;         				// Выводим строку точек
        	LCD_WriteIndex(0x0022);
 
        	for(a=0;a<8;a++)
			{   				//Счетчик столбцов знакоместа
          		if(NewFont8x8[addr+a]&(0x80>>i))
				{ 			// Если в знакоместе точка = 1
			       	LCD_WriteData(ink);     // Выводим цвет ink
          			}
			else
				{
            			LCD_WriteData(paper);   // Иначе цвет фона
          			}
        		}
 
        	LCD_CS = 1;
        	Y++;  		// Переходим на новую строку знакоместа
      		}
      	Y = tmp;  	// Восстанавливаем значение буквы по вертикали
 
      	// Ниже расположено вычисление ширины буквы для более плотного расположения 
	// текста на экране
      	i = 3;   
 
      	for(a=7;a!=0;a--)
		{
        	if(NewFont8x8[addr+a] != 0x00)
			{
          		i = a + 2;
         		 break;
        		}
      		}
 
      	// Переходим на новую строку если достигли правого края экрана
      	X += i;
      	if(X > 310)
		{
        	Y -= 9;
        	X = 3;
      		}
    	break;
  	}
 
  LCD_SetCursor(X, Y); // Координаты следует сохранить для вывода последующего символа
}

 

Немного поясню. Код, полученный в результате работы с вышеупомянутой программой (LFG) представляет собой таблицу знакомест, развернутую в большой массив. Я использую шрифт 8х8 пикселей. Соответственно для отображения каждого символа используется 8 байт.
 

Ну тут, как бы, ничего нового. Перед тем, как вывести символ, устанавливаем глобальные переменные X и Y в нужное место экрана. Это будут координаты левого нижнего угла символа. Чудовищного вида конструкция:

1
NewFont8x8[addr+a]&(0x80>>i)

определяет рисунок символа. Здесь

  • NewFont8x8 – глобальная константа, содержащая изображения символов;
  • addr – код символа, сдвинутый влево на 3 разряда (указатель на первый байт символа);
  • a – смещение (столбец знакоместа);
  • i – счетчик строки знакоместа.

 
Старший значащий бит в знакоместе расположен внизу. Сканирование происходит как показано на рисунке красными стрелками. Если бит в данном месте знакоместа равен единице, на экране ставим точку одного цвета, иначе – другого. Я хотел еще реализовать текст на фоне, но скорости контроллера недостаточно для чтения цвета точки из экранного ОЗУ и последующей записи. Очень уж много там телодвижений происходит.
 

По достижению переменной a (смещение координаты X) значения 7 происходит ее обнуление и инкрементирование переменной i (смещение координаты Y).
После отображения всего символа возвращаем координату Y в то положение, которое было до отрисовки символа, чтобы следующий символ был на том же уровне. С координатой X немного сложнее. Можно просто добавлять к предыдущему значению число 8 (максимальная ширина символа), но текст в таком случае выглядит не очень… Я пробовал высчитывать актуальную ширину символа и заносить ее в отдельный массив, но при изменении символа приходилось искать его ширину вручную и править, что мне не понравилось. Решил я предоставить эту рутину контроллеру.
 

Теперь контроллер читает байты знакоместа справа налево пока не встретит байт, отличный от 0x00. При таком подходе получается промежуток между символами в 1 пиксель. Текст выглядит цельно и аккуратно.
 

Это массив буквы А, пример которой изображен на рисунке выше:

1
0x7E,0x11,0x11,0x11,0x7E,0x00,0x00,0x00

Как видите, три нуля справа не являются частью символа. Позже они отбрасываются и вставляется только один пустой байт. Ну тут можно поэкспериментировать. Кажется мне, что шрифт 16х16 лучше делить двумя пустыми байтами. Не пробовал.

Кусок кода:

1
2
3
4
5
6
7
      // Переходим на новую строку если достигли правого края экрана
      X += i;
      if(X > 310)
	{
        Y -= 9;
        X = 3;
	}

Выполняет перенос буквы на новую строку, оставляя ее целой. При другом разрешении дисплея Вам, наверняка, придется подправить значения. Например, значение 310 – край дисплея у меня (+8 точек максимум на последнее знакоместо и перестраховка в 1 точку).
 

Если буква не влазит – она будет перенесена на новую строку. Значение Y новой строки отличается от текущей, в моем случае, на 9 пикселей (8 – высота шрифта + 1 пиксель межстрочного интервала). При использовании шрифта с размером, например, 16х16, очевидно, придется установить разность между координатами, как минимум, 17 пикселей. Кстати, иногда текст с большим межстрочным интервалом читать приятнее. Все зависит от условий использования дисплея.
 

Далее. Для вывода текста есть у меня пара функций:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Вывод строки на экран. В качестве аргумента - указатель на строку
void LCD_PutString(char* str)
{//
  unsigned int i=0;
  while(str[i]) 	// Пока не встретим терминатора... Мне нужны твоя одежда, твои ботинки и твой мотоцикл...
	{  
    	LCD_Putchar(str[i]);
    	i++;
  	}
}
 
// Это я написал ввиду наличия строковых констант
void putString(const char* str)
{
  unsigned int i=0;
 
  while(str[i])
	{
    	LCD_Putchar(str[i]);
    	i++;
  	}
}

Из комментариев понятно какая зачем может пригодиться – одна для вывода строк из ОЗУ, другая – для вывода текстовых констант. Нутром чую, что возможно решение подобных задач более простым способом, но в Си, как оказалось, я не особо силен.

Результат:

На сбои знака процент в самом низу не обращайте, пожалуйста. Не думал, что батарею можно зарядить до 4,28V :)

Хочу добавить, что при небольшом изменении кода этого, с позволения сказать, видеодрайвера, я его успешно использовал для запуска дисплеев с контроллерами другого типа и на других МК.
 

Компилировал, конечно, тоже не только при помощи IAR. Ввиду того, что «драйвер» не один раз переписывался и допиливался под конкретные задачи, стопроцентной абстракции от типа железа я не пытался добиться.
 

В дополнение вкладываю в архив с исходный код некоего универсального драйвера (UNIDriverLCD.txt). Со слов автора этот драйвер поддерживает не один LCD контроллер, но, как оказалось, не все так просто. Мне по большей части он пригодился лишь с данным дисплеем, другой же экземпляр попался на шину 8 бит, тогда как драйвер написан для 16 бит шины. На самом деле – это мелочи. Имея цоколевку дисплея и зная алгоритм инициализации его контроллера, запустить стекляшку не составит труда. Знающим и ищущим этот файл должен неплохо помочь.
 

Также мне среди китайцев попадался дисплей с контроллером OTM2201 и разрешением 176*220. Раскурил я его и кое-какие наработки тоже выложу. (2201_driver.c + ДШ с распиновкой на последнем листе).
 

По дополнительным вопросам с радостью отвечу. А при их большом числе можно и еще одну статью запилить. За сим откланяюсь…

Файлы к статье

45 thoughts on “Работа с графическим дисплеем SSD1298”

  1. // Вывод строки на экран. В качестве аргумента - указатель на строку
    void LCD_PutString(char* str)
    ...
    // Это я написал ввиду наличия строковых констант
    void putString(const char* str)
    ...

    Уберите это AVR’овское непотребство. Вы же под STM8 пишите.

    А по самому шрифту — загляните в сообщество, там есть утилиты, формирующие шрифт построчно и с необходимыми дескрипторами (размер сивмола, высота…).

    1. Да, согласен, мое знание Си оставляет желать лучшего, особенно в части строк… Но вывод строковой константы у меня использовался только для вывода сообщений об ошибках. Сейчас я от этого пытаюсь отказаться, но это уже в аналогичном проекте на STM32.
      А как Вы усмотрели «непотребство» именно от AVR архитектуры?
      Ну и по утилитам для шрифтов… Я пол-инета обыскал в поисках удобной и гибкой утилиты для создания шрифтов — проблема… Либо подходящая, но платная, либо удобная, либо гибкая. Уже был готов написать свою, но смирился с извратом в плане вывода знакоместа…

    2. А в чём конкретно состоит «AVR’овское непотребство» именно этих двух дефиниций?
      В том лишь, что вторая — полная копия первой, за исключением названия и типа аргумента?

      Предположу, что автор полагает, что «const char *» и «char *» — это какие-то разные типы. Вот и всё. :)

      1. Компилятор не хотел брать… «ожидается const char*, дано char*» и наоборот. Убился пробовать менять туда-обратно. Решил плюнуть. Все равно позже этого не будет…

        1. Ругался-то, поди, на вызов LCD_PutString(str), где str была определена как

          char *str = "text";

          так?

          Тогда надо было или:
          const char *str = "text";
          или
          LCD_PutString((const char *)str);

          :)
          Или просто опцию у компилятора выключить, чтобы не ругался на такие вещи. Где это у IAR, не скажу — не знаю. Могу только про gcc рассказать.


          1. char *str = "text";

            Именно! Для меня было радостью, что ИАР намного лучше работает со строками, чем CVAVR. Но, видимо, не с той стороны (или не той стороной) смотрю на это. Указатели на строку или, тем более, на функцию для меня пока труднопонимаемы…

        1. Кстати, в архиве с исходниками есть файл (не мой) с порядком инициализации в т.ч. и на 1289. Файл не мой, где сдул — уже не вспомню, но нервов он мне сохранил немало. Код там избыточен, но посмотреть рекомендую.

        2. Дисплеи очень похожи. По интерфейсам, регистрам практически идентичны. 89й возможно чуть более навернут, а так, ядро — одно и то же.

  2. Di, а есть где-то инфа по цветным от сотовых?
    К примеру, у меня лежит шикарный экран от Нокии 6300, Сименса М55 и ещё кучка разных (Моторолла, Лыжи, не помню модели точно, и пара ч/б от совсем древних).
    Никак не могу найти по ним ничего (кроме упоминания, что у экрана от М55 13-Мгц SPI), может, тебе известны какие ходы-выходы?

  3. Нашел на 6300… Свои наработки выкладывать не стану, так как по большей части они дублируют то, что я уже выложил.
    Подключение дисплея неплохо описано тут
    По опыту могу сказать, что шрифт, который я использую, на таком дисплее выглядит крайне мелко, так как пиксели практически не видно. А вот картинки получилось выводит с изумительным качеством. Единственной фишкой в то время у меня был вывод картинки, полученной по COM порту из ПК (в терминале я посылал файл BMP), так как FAT в то время еще не смог подцепить. Ну а нормальной должности в устройствах дисплей так и не получил — для текста, да и вообще, мелковат. Есть мысль сделать из него брелок-фоторамку, если таки достану stm8S103 с 8КБ памяти на борту…

  4. что-то цитата выпала…
    там было:
    контроллер читает байты знакоместа справа налево пока не встретит байт, отличный от 0×00. При таком подходе получается промежуток между символами в 1 пиксель.

    1. Там счетчик по условию a!=0, то есть нулевой столбик стопудово остается. Ну и после этого еще пустой столбец вставляется, как разделитель символов. Вполне нормальный отступ получился и я не стал переделывать, чтоб пробел был еще шире. Знак ! тоже выглядит как и нужно. В общем, картинка сверху демонстрирует реальный вид текста. Никакого фотошопа)

  5. У меня есть дисплей он китайского телефона TV702, на нем тоже 37 выводов под пайку. Как думаешь у него контреллер такой же который ты описываешь?

  6. H-KLM529A01-1B-081109-R-D2 написано сбоку дисплея
    A487A-38-P071211-2524 НАПИСАНО ВНИЗУ ближе к шлейфу
    на шлейфе GIANPLUS KFM529B21-1a

  7. Тут такой путь мне видится…
    Сначала идем сюда и сопоставляем расположение выводов подсветки и тачскрина… Так определяем примерную цоколевку.
    Затем надо либо попробовать завести дисплей, юзая коды инициализации поочередно. Это метод почти_тыка. Либо можно попробовать прочитать код драйвера из адреса 0x00 или 0x04 драйвера… Так можно узнать драйвер более конкретно. А буквы на шлейфе и самой стекляшки, как показывает практика, далеко не всегда совпадают…

      1. Ну в таком случае рекомендую попробовать этот код инициализации дисплея. (остальное оставьте как есть пока). Если что-то не очень понятно, спрашивайте, постараюсь подробнее разъяснить…

        1. Есть библиотека UTFT для Arduino (автор Henning Karlsen), в ней в наглядной форме можно посмотреть коды инициализации популярных контроллеров TFT дисплеев. Если есть сомнения в модели — теоретически можно просто перебрать по очереди всё что есть — глядишь от какого-то оживёт.

          Вот что поддерживается:

          #define ITDB32 0 // HX8347-A (16bit)
          #define ITDB32WC 1 // ILI9327 (16bit)
          #define TFT01_32W 1 // ILI9327 (16bit)
          #define ITDB32S 2 // SSD1289 (16bit)
          #define TFT01_32 2 // SSD1289 (16bit)
          #define ITDB24 3 // ILI9325C (8bit)
          #define ITDB24D 4 // ILI9325D (8bit)
          #define ITDB24DWOT 4 // ILI9325D (8bit)
          #define ITDB28 4 // ILI9325D (8bit)
          #define TFT01_24_8 4 // ILI9325D (8bit)
          #define TFT01_24_16 5 // ILI9325D (16bit)
          #define ITDB22 6 // HX8340-B (8bit)
          #define ITDB22SP 7 // HX8340-B (Serial)
          #define ITDB32WD 8 // HX8352-A (16bit)
          #define TFT01_32WD 8 // HX8352A (16bit)
          #define ITDB18SP 9 // ST7735 (Serial)
          #define LPH9135 10 // PCF8833 (Serial)
          #define ITDB25H 11 // S1D19122 (16bit)
          #define ITDB43 12 // SSD1963 (16bit) 480x272
          #define ITDB50 13 // SSD1963 (16bit) 800x480
          #define ITDB24E_8 14 // S6D1121 (8bit)
          #define ITDB24E_16 15 // S6D1121 (16bit)

          1. Чаще проблема стоИт в определении распиновки. Один и тот же китаец может быть собран на дисплеях с разными контроллерами. Это отслеживается даже по т.н. фуллам, кто знает о чем я. Кроме того, встречались дисплеи абсолютно схожие по всем параметрам, но с разной маркировкой. Радует одно — в магазинах запчастей для сотовых они стоят копейки. Потому, подобрав даже по фотке, я попал на нужный мне дисплей. Хотя, может это частный случай…

  8. В микрочиповской библиотеке (MAL) есть поддержка SSD1289 и соседних по ряду соломоновских чипов. С поддержкой фонтов, примитивов и базового UI-набора. Работает с резистивными тачскринами или кастомными кнопками. Умеренно-геморройно допиливается напильником под наличное железо\дисплеи на таких чипах. Библиотека открытая, многие используют её как базис для разных других контроллеров и дисплеев. Хоть она официально и точится под микрочиповские наборы и процессоры начиная от 16-битных, всякое бывает. Techtoys например запиливает под свои киты, где и дисплеи разные и pic18 опятьже.

  9. Я начал экспериментировать с подобными дисплеями примерно год назад. Первая радость от заработавшего дисплея и пёстрых демок быстро сменяется вопросом: а что делать дальше? Функциональность существующих библиотек скупая, процесс отладки GUI — изнурителен постоянными перезаливками программы в контроллер, шрифты 8×8 — это семидесятые годы прошлого века. В общем, начал решать эти вопросы для себя созданием эмулятора, который плавно вырос до самостоятельного продукта. Первую версию и планы на будущее можно глянуть тут:
    http://pd4ml.com/pixelmeister/
    Главная фича этого релиза (помимо самого эмулятора): импорт TTF шрифтов в (очень) компактный растровый формат и библиотека для вывода текста такими шрифтами.

    Чтоб загладить неприятный осадок от рекламы в не особо подходящем для неё месте, готов пораздавать пару недель лицензии читателям easyelectronics.ru бесплатно (только сначала поиграйтесь с trial версией и убедитесь, что это то, что вам действительно нужно).

  10. А как быть тем у кого другой микроконтроллер?
    Например у меня экран dlw-0043(из Explay t35 tv выдран, самого Мк чет не вижу) и хочу сделать чтобы можно было на этот дисплей посылать видеосигнал RCA входом.
    Возможно?
    Если да, подскажите ссылку на ресурс пожалуйста, где описано подобное.

    1. Видел я платы в продаже с подобным функционалом. Стоимость у нас в районе 2Круб. Дисплей к такой платке, как правило, идет с интерфейсом RGB. Вместе с дисплеем платка уже в районе 5Круб))). А для Вашего случая, видимо, нужен будет шустрый АЦП и нормальный проц. Это уже далеко за возможности AVR и STM8 выходит. Тут прям не знаю, что бы посоветовать…

  11. мда уж…
    Как я понял тогда дешевле сразу взять
    http://www.ebay.com/itm/NEW-3-5-TFT-LCD-Color-Screen-Car-Rearview-Monitor-DVD-VCR-/160696147452 — причем тут дисплей абсолютно такой же как и мой, только единственно не сенсорный.
    За 600 руб + доставка(400-600 руб) ну и плюс комиссия(300 руб), но если брать не один(точнее даже если нужен не один), то выгода будет очень неплохой,
    А мой же дисплей такой http://gsmset.ru/product/Tachskrin-universalniy-7662-mm-shleyf-22-mm-poseredine-DLW-0043-.html
    Почему там такая цена 600 руб? ведь по идее у них тоже есть АЦП и шустрый проц.
    Какая минимальная частота должна быть чтобы обрабатывать аналоговый сигнал?

  12. Так. То, что Вы показали по ссылке — просто тачскрин. Это не дисплей.
    Вы сами посчитали цену доставки девайса. А все магазины, которые это доставляют сюда, с чего должны получать выгоду???

    1. Я думал сразу с экраном идет, ну в любом случае экран-то у меня есть,
      RCA мне нужен был потому как я хотел его подключить к raspberry pi, так как подобные экраны на видео подключают именно через этот интерфейс, но есть там еще HDMI, через него может прощу будет подключить? Меня впринципе не цена интересует, а Опыт в разработке устройств.
      raspberry pi конешно пока еще едет и в ближайшее время наверно не стоит его ожидать(на сайте написано 4 мес.), а вот экран уже есть и хотелось бы уже заранее попробовать его модернизировать для работы на raspberry pi .

      1. Без должного опыта в таких конструкциях я б, наверное, не советовал туда лезть. Начните с чего-то попроще. Посоветовать ничего не могу, так как с HDMI не работал — частоты там дикие, а я еще даже STM32 не раскурил как следует.

        1. Понятно, значит буду ждать эти экраны ссылки на который дал выше, как приедут попробую разобраться что там внутри=)) Мк надеюсь можно будет перепрограммировать, тот что будет там?

          1. Вы напоминаете мне меня лет дцать назад. Тоже все хотел перепрошить под свои нужды. А компилятор есть? А даташит на МК? Китайцы на свои поделки, как правило, схем не выкладывают. Начните с малого. Подцепите от нокии 3310 к АВР дисплей…. Если получится — возьмите дисплей побольше — 6300 или от китайца. Почуете «порох в пороховницах» — обратите внимание на дисплей от PSP, например. А сразу от телевизора ковырять, пусть и от карманного — не вариант. У самого уже давно валяется 5,6» дисплей от автомобильного медиацентра и что с ним делать я понятия не имею. Информации на него нет.

          2. Компилятор вы имеете ввиду саму программу с помощью которой можно будет заливать на МК ? Пока нет, так как даже не знаю какие МК там в этих экранах,
            А такж есть программатор на AVR и на PIC , ну не совсем программатор — плата Arduino(там есть и компилятор — SDK) , сама программирует AVR но с помощью некоторой манипуляции может и PIC программировать, кстати как я понял он может программировать только те МК которые работают на частоте не больше чем сам программатор?

        2. Если вас не усложнит, скиньте пожалуйста ссылку на ту самую плату с помощью которой можно выодит композитный выход(RCA) ну и datasheet/описание/принцип работы если есть что-то подобное или что-нибудь, а то в интерете ищу уже целый день почти и никаких даже схем RCA интерфейса не нашел, либо поисковые системы эти схемы и информацию оставляет на второй план а на первый выводит только покупка/продажу кабелей

          1. Спасибо больше, по названиям нашел подешевле http://www.zip-2002.ru/?z=grey&i=138&p=612&id=79803/ — 300 рублей всего, даже пару штук наверно возьму, а то у меня еще 5 шт. дисплеев с Siemens-ов осталось(S75,C72,M65,C62,A52) + есть вероятность что возьму дисплей Ordroid-X(точнее у них на сайте тупо 14дюймовый LCD дисплей продают для своих ORDROID-ов) , туда попробую прицепить если останется, а так вообще сначала когда открыл схему эту Ужаснулся, ппц там не только АЦП + МК, походу это вообще какой-то универсальный драйвер… Надеюсь документацию они тоже высылают? А то в интернете реально нет совсем никакой информации, даже схемы Композитного интерфейса не нашел=(…

  13. А с чего Вы взяли, что к данному контроллеру подойдут все вышеперечисленные стекляшки? Там протоколы одинаковые? Товарищ, я крайне терпелив и стараюсь отвечать по теме, но сейчас налицо полное непонимание работы интерфейсов дисплеев… Вам следует начать с простого, как я уже отметил выше. Сейчас, купив контроллер без дисплея к нему Вы, скорее всего, просто зря потратите деньги.

  14. Не могу разобраться с изменением ориентации текста. Меняю в регистре 0х0011 значения ID и AM, но не вижу реакции на экране. Как в программе, например по нажатию кнопки, менять ориентацию отображаемой информации?

      1. До вывода. Уже не один день. Вариантов было множество. Менялось только при изменении начальных координат в LCD_SetCursor, точнее в LCD_WriteReg(0x004e, Ypos );
        LCD_WriteReg(0x004f, Xpos );
        Ypos и Xpos задавал крайними значениями.

  15. Обратите свое внимание на регистр 0x01. там есть биты управления сканированием оперативы. В частности, Вас должны заинтересовать биты TB и RL. Попробуйте «поиграть» с ними.

  16. Остался в живых дисплей от Samsung Galaxy S2 с тачскрином , зубами щелкаю вокруг него, а как подступиться не знаю :-( И контроллер там Atmel. Может кому какая инфа о таком попадалась что и как?

Добавить комментарий

Ваш e-mail не будет опубликован.

Перед отправкой формы:
Human test by Not Captcha