AVR Урок 34. Дисплей TFT 240×320 8bit. Часть 4



 

Урок 34

Часть 4

 

Дисплей TFT 240×320 8bit

 

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

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

 

//—————————————————————

void TFT9341_Flood(unsigned short color, unsigned long len)

{

}

//—————————————————————

 

Первый входной параметр — это цвет пикселей, а второй длина области памяти для заливки. Адресация будет в отдельной функции.

Добавим в функцию несколько переменных, причём две из них сразу проинициализируем определённым образом

 

void TFT9341_Flood(unsigned short color, unsigned long len)

{

  unsigned short blocks;

  unsigned char i, hi = color>>8, lo=color;

 

То есть 16 бит цвета мы здесь распределим по двум переменным.

Опустим ножку выбора и ножку команд/данных

 

unsigned char i, hi = color>>8, lo=color;

CS_ACTIVE;

CD_COMMAND;

 

Отправим цвет в соответствующий для этого регистр

 

CD_COMMAND;

TFT9341_Write8(0x2C);

CD_DATA;

TFT9341_Write8(hi);

TFT9341_Write8(lo);

 

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

Продекрементируем длину области, так как отсчёт у нас происходит не с 1, а с 0

 

TFT9341_Write8(lo);

len—;

 

Разобьём нашу длину на блоки по 64 пикселя

 

len—;

blocks=(unsigned short)(len/64);//64 pixels/block

 

Дальше условие, если младшая часть 16-битного цвета равна старшей

 

blocks=(unsigned short)(len/64);//64 pixels/block

if (hi==lo)

{

}

else

{

}

 

В теле условия цикл, равный количеству блоков

 

if (hi==lo)

{

  while(blocks—)

  {

  }

}

 

В теле цикла ещё один цикл, равный 16

 

while(blocks—)

{

  i=16;

  do

  {

    WR_STROBE;WR_STROBE;WR_STROBE;WR_STROBE;//2bytes/pixel

    WR_STROBE;WR_STROBE;WR_STROBE;WR_STROBE;//x4 pixel

  } while (—i);

}

 

В данном цикле мы просто стробируем, отправляя один и тот же байт в контроллер дисплея, уровни байта ведь у на ножках порта установлен. Получается, что мы один и тот же байт отправим 128 раз, то есть если разбить на пары, то 64 пары и, следовательно, 64 пикселя. То есть при условии равенства старшего и младшего байтов цвета мы отправили столько блоков, сколько у нас есть, а засчёт того, что мы каждый раз не устанавливаем уровень на ножках порта данных, мы значительно выигрываем во времени.

 

 

Выйдя из цикла отправки всех блоков, мы отправим оставшуюся часть байтов, если у нас длина не будет делиться на 64 без остатка

 

    } while (—i);

  }

  //Fill any remaining pixels(1 to 64)

  for (i=(unsigned char)len&63;i—;)

  {

    WR_STROBE;

    WR_STROBE;

  }

}

 

Вот так. Теперь заходим в «противную часть» нашего условия, когда у нас старший и младший байт цвета будут разными. Я думаю, это будет чаще. Там также будет цикл, равный количеству полных блоков

 

else

{

  while(blocks—)

  {

  }

}

 

В цикле также цикл, равный 16, в котором мы передаём весь блок

 

while(blocks—)

{

  i=16;

  do

  {

    TFT9341_Write8(hi);TFT9341_Write8(lo);TFT9341_Write8(hi);TFT9341_Write8(lo);

    TFT9341_Write8(hi);TFT9341_Write8(lo);TFT9341_Write8(hi);TFT9341_Write8(lo);

  } while (—i);

}

 

Ну, тут понятно, раз байты не равны, передаём их по очереди.

Затем также передаём оставшуюся часть пикселей

 

      } while (—i);

    }

  //Fill any remaining pixels(1 to 64)

  for (i=(unsigned char)len&63;i—;)

  {

    TFT9341_Write8(hi);

    TFT9341_Write8(lo);

  }

}

 

 

Ну и в конце поднимем ножку выбора

 

      TFT9341_Write8(lo);

    }

  }

  CS_IDLE;

}

 

Теперь напишем функцию записи в регистр 32-битного числа

 

//—————————————————————

void TFT9341_WriteRegister32(unsigned char r, unsigned long d)

{

  CS_ACTIVE;

  CD_COMMAND;

  TFT9341_Write8(r);

  CD_DATA;

  _delay_us(1);

  TFT9341_Write8(d>>24);

  _delay_us(1);

  TFT9341_Write8(d>>16);

  _delay_us(1);

  TFT9341_Write8(d>>8);

  _delay_us(1);

  TFT9341_Write8(d);

  CS_IDLE;

}

//—————————————————————

 

Я думаю, тут даже объяснять ничего не надо. Мы сначала передаём адрес регистра, а затем с некотроыми задержками передаём наши данные.

Но, прежде чем отправлять в память байты, нам нужно объявить область памяти, в которую будет вся наша цепочка одинаковых пикселей отправляться. Для этого мы напишем специальную функцию

 

//—————————————————————

void TFT9341_SetAddrWindow(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)

{

  unsigned long t;

  CS_ACTIVE;

  t = x1;

  t<<=16;

  t |= x2;

  TFT9341_WriteRegister32(0x2A,t);//Column Addres Set

  t = y1;

  t<<=16;

  t |= y2;

  TFT9341_WriteRegister32(0x2B,t);//Page Addres Set

  CS_IDLE;

}

//—————————————————————

 

Ну здесь мы в специализированные регистры 2Ah и 2Bh передаём наши коортдинаты сначала начала и окончания вертикальной области, а затем горизонтальной, распределив соответственно эти координаты в четырехбайтовые величины. Отправка в регистры стандартная.

Также нам потребуется какой-то шрифт, если мы захотим выводить какой-то текст. Я с помощью определённой программы создал этот шрифт, причём написал не весь шрифт, а несколько букв, ибо памяти у нас не так много. Сделал я их высотой в 16 пикселей

 

font 16

//—————————————————————

//font 16

const unsigned char chars16[][32] PROGMEM =

{

//SPACE

{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},

//0

{0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x0f, 0xe0, 0x0c, 0x60, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30,

0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x0c, 0x60, 0x0f, 0xe0, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00},

//1

{0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,

0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xc0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00},

//A

{0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x07, 0xf0, 0x01, 0x40, 0x03, 0x60, 0x03, 0x60, 0x06, 0x30,

0x07, 0xf0, 0x0f, 0xf8, 0x0c, 0x18, 0x0c, 0x18, 0x3e, 0x3e, 0x3e, 0x3e, 0x00, 0x00, 0x00, 0x00},

//B

{0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x0f, 0xf0, 0x06, 0x30, 0x06, 0x30, 0x07, 0xe0, 0x07, 0xf0,

0x06, 0x38, 0x06, 0x18, 0x06, 0x18, 0x06, 0x38, 0x0f, 0xf0, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00},

//C

{0x00, 0x00, 0x00, 0x00, 0x07, 0xd8, 0x0f, 0xf8, 0x1c, 0x38, 0x38, 0x18, 0x30, 0x00, 0x30, 0x00,

0x30, 0x00, 0x30, 0x08, 0x38, 0x0c, 0x1c, 0x38, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00}

};

//—————————————————————

 

 

 

 

Ну и далее собственно заливка всей области экрана определённым цветом

 

//—————————————————————

void TFT9341_FillScreen(unsigned int color)

{

  TFT9341_SetAddrWindow(0,0,X_SIZE-1,Y_SIZE-1);

  TFT9341_Flood(color,(long)X_SIZE*(long)Y_SIZE);

}

//—————————————————————

 

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

Напишем на данную функцию прототип и вызовем её в main() после инициализации дисплея

 

TFT9341_ini();

TFT9341_FillScreen(BLACK);

 

Соберём код, пошьём контроллер и посмотрим результат

 

image08

 

Как мы видим, дисплей наш окрасился в чёрный цвет.

Поменяем цвет на красный и ещё раз проверим

 

TFT9341_ini();

TFT9341_FillScreen(RED);

 

image09

 

В следующей части нашего урока мы напишем ещё несколько функций для работы с дисплеем и попробуем вывести некоторые примитивы на экран дисплея.

 

 

Предыдущая часть Программирование МК AVR Следующая часть

 

 

Техническая документация на контроллер дисплея ILI9341

 

Программатор, символьный дисплей LCD 20×4 и переходник для него можно приобрести здесь:

Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером

Дисплей LCD 20×4

Переходник PCF8574 IIC

 

 

Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)

AVR Дисплей TFT 240×320 8bit

 

Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)

AVR Дисплей TFT 240×320 8bit

16 комментариев на “AVR Урок 34. Дисплей TFT 240×320 8bit. Часть 4
  1. Alex:

    Спасибо за видео, мне нравится. Только вот что-то у меня не пошло вроде все проверил, подсоединено все верно и программу проверил все также, но что — то не идет, не могу понять куда копать?

  2. Максим:

    Добрый день,

    спасибо за столь подробное описание.

    Я пишу на CodeVisionAVR. Решил переделать код под нее.

    Но вот незадача, качаю исходник, беру готовых hex, прошиваю.

    Дисплей инициализируется, но вместо теста выдает вот такую картинку

    Открываю проект в AtmelStudio 7

    Компилирую по новой и экран перестает инициализироваться.

    Подскажите в чем может быть причина?

    И не совсем понял, как можно изменить номера портов Data.

    С командными все понятно, они явно заданы.

    А у дата портов указывается только буква порта

  3. SMDT:

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

  4. SMDT:

    Код в железе не работает ! Автор вопросы игнорирует.

  5. Автор все вопросы читает. Если у него есть ответ, то он его даёт. Если ответа нет, то такой комментарий скорее всего также пройдёт модерацию и имеет право быть опубликованным, если нет в нём ссылок и нарушений. Вашу ситуацию воспроизвести не удалось. У меня код работает прекрасно. Вы уверены, что в Вашем дисплее установлен именно такой же контроллер?

    • SMDT:

      Здравствуйте. Да, уверен. Но дело даже не в этом. ATMEGA8 без подключения к дисплею совсем молчит — на всех ножках по нулям. МК точно живой, т.к. другие ваши проекты работают на нем. Но такого же не может быть !? Также в схеме на первом уроке не указан внешний кварц, а в проекте указана частота 16МГц. Мега8 может тактироваться от внутреннего источника на 16 МГц ? Даже если и от 8-ми, все равно на ножках МК должны же быть какие-то шевеления?

      • Надо читать техническую документацию. Источник тактирования задаётся фьюзами. Поэтому только внимательно изучать, иначе можно контроллер вывести из строя.

  6. SMDT:

    И еще странность. В Протеусе , на ножке RESET присутствует 5 вольт. И дисплей там работает. Заливаю прошивку в Atmega8 на RESET 0 вольт постоянно . МК точно рабочий.

  7. Artem:

    Здраствуйте! Понадобился Ili9341 по 8bit.
    Собирал инфу и попал к вам. Проект старый, но может вы вспомните свои алгоритмы.
    В вашем коде для отправки данных весь массив делится на блоки по 64 пикселя и каждый блок на 16 по 4 пикселя.
    Зачем нужны блоки по 4 пикселя мне, думаю, понятны, а зачем нужны блоки по 64 — нет.
    Отправляя по 4, а не по одному, уменьшается количество проверок While.
    В одном цикле проверка условия While будет после отправки каждых 4 байта.
    Во втором цикле после каждых 16х4 = 64 байт будет дополнительная проверка, забирающая такт или больше.
    Не могу понять зачем нужен второй цикл. Что я упускаю?

  8. Artem:

    Ошибочка: не байт, а пиксель. (пиксель = 2 байта).
    Исправьте, пожалуйста.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*