среда, 13 ноября 2013 г.

Библиотека для TFT дисплеев на контроллере ILI9341

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




Кратенько про экранчик:
Экран цветной, TFT, диагональ 2.8 –дюйма (MI0283QT-9А) с разрешением 320х240 пикселей, отображает 262 тысячи цветов. Покрыт резистивной сенсорной панелью. Умеет работать по SPI, по параллельному интерфейсу 8 или 16 бит и VGA. Переключение производится коммутацией выводов IM0-IM3.
Документацию на контроллер и плату расширения можно взять в конце статьи.

Поскольку в качестве управляющего контроллера используется 8-и битные AVR с максимальной частотой 16 МГц, рассматривать в качестве используемого интерфейса SPI смысла не вижу, поскольку это будет не просто медленно, а ОЧЕНЬ медленно, использовать же 16-и битный параллельный не позволяет жаба, поскольку выходит что кроме экрана к меге16/32 уже ничего и прицепить не выйдет, так что в итоге выбрал параллельный 8-и битный.

Схема подключения с выбранным интерфейсом будет выглядеть так:

Не требующиеся в данном варианте подключения либо подключены на землю, либо оставлены висеть в воздухе согласно даташиту на контроллер. Выводы DB0 - DB7 вешаем на порт контроллера через который будем гонять данные(в примере будет порт B). Выводы LCD-RD#, LCD-WR#, LCD-RS, LCD-CS# вешаем на порт через который будем гнать сигналы управления(порт А у меня). Х+/-, Y+/- на входы АЦП (выводы тачскрина). RC-цепочка на выводе LCD-RST# обеспечивает аппаратный сброс контроллера экрана при включении питания.
Смотрим на временную диаграмму управления экраном:
Цикл записи

Цикл чтения

 Таблица управляющих сигналов в выбранном интерфейсе

Из приведенных картинок видно, что ничего особо сложно не предвидится, , так же ясно что все управляющие сигналы инверсные, т.е. либо вешать на управляющие линии внешние резисторы, либо нужно включить внутренние резисторы подтяжки на порту управления. Переводя же картинки в слова можно сказать так:
Запись команды в контроллер - придавить к земле вывод LCD-CS# -> придавить LCD-RS -> выставить на шине данных адрес команды( D0 - D7 ) -> дрыгнуть ногой LCD-WR# -> отпустить LCD-RS - > выставить на шине данные команды( D0 - D7 ) -> дрыгнуть ногой LCD-WR# -> отпустить LCD-CS#. Аналогично же можно расписать последовательность действий при чтении из контроллера. Все это безобразие выливается в такой код:
#define TFT_DATA_PORT PORTB
#define TFT_DATA_DIR DDRB
#define TFT_CTRL_PORT PORTA
#define TFT_CTRL_DIR DDRA

#define TFT_RDX 0
#define TFT_WRX 1
#define TFT_DCX 2
#define TFT_CSX 3

#define TFT_RDX_LO {TFT_CTRL_PORT &=~(1 << TFT_RDX);}
#define TFT_RDX_HIGH {TFT_CTRL_PORT |=(1 << TFT_RDX);}

#define TFT_WRX_LO {TFT_CTRL_PORT &=~(1 << TFT_WRX);}
#define TFT_WRX_HIGH {TFT_CTRL_PORT |=(1 << TFT_WRX);}

#define TFT_DCX_LO {TFT_CTRL_PORT &=~(1 << TFT_DCX);}
#define TFT_DCX_HIGH {TFT_CTRL_PORT |=(1 << TFT_DCX);}

#define TFT_CSX_LO {TFT_CTRL_PORT &=~(1 << TFT_CSX);}
#define TFT_CSX_HIGH {TFT_CTRL_PORT |=(1 << TFT_CSX);}

void InitInterface(void)
{
TFT_CTRL_DIR |= (1 << TFT_RDX) | (1 << TFT_WRX) | (1 << TFT_DCX) | (1 << TFT_CSX);
TFT_CTRL_PORT |= (1 << TFT_RDX) | (1 << TFT_WRX) | (1 << TFT_DCX) | (1 << TFT_CSX);
TFT_DATA_DIR |= 0xFF;
TFT_DATA_PORT = 0x00;
}

void TFT_SendCMD(uint8_t cmd)
{
TFT_WRX_LO;
TFT_RDX_HIGH;
TFT_DCX_LO;
TFT_CSX_LO;
TFT_DATA_PORT = cmd;
asm("NOP");
TFT_WRX_HIGH;
asm("NOP");
TFT_CSX_HIGH;
TFT_DCX_HIGH;
}

void TFT_WriteData(uint8_t data)
{
TFT_WRX_LO;
TFT_RDX_HIGH;
TFT_DCX_HIGH;
TFT_CSX_LO;
TFT_DATA_PORT = data;
asm("NOP");
TFT_WRX_HIGH;
asm("NOP");
TFT_CSX_HIGH;
TFT_DCX_HIGH;
}

void TFT_SendData(uint16_t data)
{
uint8_t data1 = data>>8;
uint8_t data2 = data&0xff;
TFT_WriteData(data1);
TFT_WriteData(data2);
}
Собственно как и говорил - кроме мордобоя никаких чудес. В архиве сама библиотека, шрифт размером 8х16(да именно 8х16 несмотря на то что называется Font16x16, так получилось...) и документация. В библиотеке реализована инициализация дисплея, вывод текста и графические примитивы. Берем  эту радость ТУТ.
Пример применения:
#include <avr/io.h>
#include "TFT.h"

extern uint16_t MAX_X, MAX_Y;

int main(void)
{
InitInterface();
TFT_Init(1);
TFT_ClearScreen();
TFT_FillScreen(0, MAX_X, 0, MAX_Y, BLUE);

while(1)
    {
                 // тут сами чегонить...
    }
}
Для посмотреть этого должно хватить.
Про работу с тачскрином очень хорошо расписано тут, так что я этим заниматься не буду, ибо нечего плодить сущности. Ну и в финале картинка того что у меня вышло
Тут у нас приемничек с RDS на базе SI4703. Кстати, как в очередной раз одержу победу над ленью, напишу статейку и выложу библиотечку для этой штуки.

17 комментариев:

  1. А это точно экран на контроллере ILI9341? У меня в даташите контроллер LIL9341. Опечатка?

    По вашей ссылке на библиотеки нет скачивания. какая-то реклама и предложение купить gold

    ОтветитьУдалить
    Ответы
    1. Точно точно, ILITEC ILI9341. По ссылке все скачивается, проверил.

      Удалить
  2. Я никак не могу понять принцип передачи данных , в даташите сказано что, чтоб записать данные в дисплей необходимо установить ножку WRX а там , на панели дисплея этой ножки вообще нет .
    В SPI для чтения данных необходимо установить 7-й бит в единицу или наоборот , для записи в ноль но в дисплее не сходится этот метод , может кто объяснить ? СПОСИБО !

    ОтветитьУдалить
  3. Вот тут Display ILI9341 работает на максимальной скорости 10Mhz https://www.youtube.com/watch?v=Q8JTn1H9eHk блоки индикации написаны оптимально, скорость получилась вполне хорошая

    ОтветитьУдалить
    Ответы
    1. Когда сможете разогнать на Atmega32 до 10 МГц (с учетом того что максимальная тактовая у неё 16МГц ) - сообщите! 😉 А так да, чего бы на арме то и не по SPI. Сейчас сам на них перебираюсь потихоньку.

      Удалить
  4. Где бы ты ни был и чем не занимался СПАСИБО ТЕБЕ ОГРОМНОЕ. Столько времени пытался завести этот проклятый LCD.
    Переписал с минимальными изменениями под STM32 - завелся на ура.

    ОтветитьУдалить
  5. а на контролере HX8357В можите написать библиотеку?

    ОтветитьУдалить
    Ответы
    1. Теоретически - да. На практике - сильно сомневаюсь, поскольку экрана с таким контроллером у меня нет и вряд ли появится, да и с Atmel я ушел на STM...

      Удалить
  6. Что-то не могу запустить на Atmega128 в Proteus8. Черный экран и все...

    ОтветитьУдалить
  7. Этот комментарий был удален автором.

    ОтветитьУдалить
  8. Здравствуйте! Очень хочется разобраться с дисплеем и хотя бы понять, работает ли он. Но никак не компилируется код из архива в atmel studio 7. Постоянно куча ошибок вываливается.Не могли бы Вы помочь разобраться? Спасибо.

    ОтветитьУдалить
    Ответы
    1. Скорее всего, не находит используемые библиотеки, поскольку ничего хитрого в коде нет, сплошной ногодрыг. Возможно поменялись функции в используемых библиотеках... Читайте что за ошибки выдает, оно ж там понятно пишет, чего не хватает, как правило. Проверить не могу, поскольку с AVR перешел на STM32 и atmel studio у меня нет.

      Удалить
    2. Спасибо за ответ. Два ворнинга выскакивает ссылаясь на оператор if (int i=0; i <lenght; i++). Точный перевод не помню, но что-то типа о невозможности сравнения двух разных типов данных. unsigned char lenght и int i.
      И второе, это ошибка - id returned 1 exit status

      Удалить
    3. Ну так тут либо i объявить как unsigned char, либо length как int всего и делов.

      Удалить
    4. Именно так и сделал. Предупреждений нет. Остается только ошибка - id returned 1 exit status.С ней к сожалению, мне не хватает знаний справиться.

      Удалить
    5. Перед этой строкой должно быть что то еще, т.е. это не весь текст сообщения об ошибке.

      Удалить