Дек 012012
 

Кроме вывода символов иногда может потребоваться отображение графической информации. В этом случае можно использовать дисплей, в котором изображение формируется с помощью матрицы точек. Одними из самых простых и популярных являются дисплеи на основе контроллера KS0108. Здесь будут рассмотрены характеристики и порядок работы с одним из дисплеев данного типа, имеющим разрешение 128х64 пикселей.

Краткие характеристики:

Дисплей матричный монохромный со светодиодной подсветкой.

Напряжение питания +5В. Для управления непосредственно дисплеем требуется большее напряжение – от 8В до 17В. Поэтому схема содержит собственный встроенный драйвер, который и формирует необходимое напряжение для дисплея, а кроме того еще формируется отрицательное напряжение на внешнем выводе Vee, которое используется для регулировки контрастности.

Контроллер работает с 8-разрядной параллельной шиной данных.

Экран имеет разрешение 128х64 точек и разделен на две половины размерностью 64х64 точки. Левая и правая части дисплея (матрица 64х64 точки) управляются отдельными контроллерами типа KS0108. Каждый контроллер имеет внутреннее ОЗУ емкостью 512 байт (4096 бит).

Назначение выводов индикатора:

Назначение выводов

Это важно!!! Выводы выбора контроллера (соответственно вывод информации в левую или правую половину дисплея) CS1, CS2 могут быть с прямым или инверсным управлением, в зависимости от модели индикатора и его производителя. У моего дисплея выбор производится установкой низкого уровня на соответствующем выводе, поэтому в таблице так и указано.

Для регулировки контраста на вывод Vo подается напряжение с движка переменного резистора, который подключается между выводами отрицательного напряжения Vee и напряжением питания +5В. Обычно его сопротивление равно 10 кОм.

Цепь питания светодиодов подсветки может быть разорвана, у моего индикатора на обратной стороне запаяны резисторы по 51 Ом, включенные параллельно, итого 25.5 Ом последовательно со светодиодами.  И пустые контактные площадки для установки перемычек или дополнительных резисторов в цепи питания светодиодов. С резисторами на 25.5 Ом ток в цепи подсветки выбран с большим запасом, и его можно значительно уменьшить для снижения потребления мощности. Установил последовательно еще 300 Ом – яркость подсветки для комнатных условий оказалась вполне достаточной.

С назначением выводов RS (в других моделях индикатора вывод может быть обозначен как DI) и RW вроде все однозначно понятно из таблицы. Вывод E является управляющим (стробирующим сигналом) для чтения и записи, по перепаду логического уровня с высокого в низкий на этом выводе, происходит чтение или запись данных и команд.

То есть, алгоритм работы с дисплеем будет следующим:

  • Выставляем на выводы DB0-DB7 данные или команду, а на выводы RS и RW соответствующие уровни.
  • Устанавливаем высокий уровень на выводе E
  • Сбрасываем в низкое состояние уровень на выводе E. Вот и всё!

В документации на контроллер KS0108 можно найти временные диаграммы сигналов, с минимально допустимыми задержками между изменением уровней сигналов на выводах, в частности, и изменениями уровней на выводе E. Для нормальной работы достаточно задержек не менее 1 мкс.

Как же выводятся данные в определенные координаты? На дисплей передается по 8 бит данных одновременно. Эти 8 бит будут отображены в 8 “пикселях” матрицы экрана вертикально, верхним будет бит 0, нижним – бит 7. Перед вводом байта данных, необходимо задать начальные координаты экрана, в которых отобразится бит 0. Эти координаты необходимо вычислить самостоятельно, давайте посмотрим на рисунок:

Вывод на дисплей

Поскольку данные выводятся сразу целым байтом, вертикальная область (64 точки) разделена на 8 страниц. По горизонтали же область экрана разделена на 2 части по 64 точки в каждой, за вывод в каждую половину экрана отвечает один из двух контроллеров KS0108, выбираемый с помощью выводов CS1, CS2. Для вывода 8 бит данных на экран необходимо:

  • Выбрать правую или левую половину экрана с помощью уровней на выводах CS1, CS2. Можно выбрать сразу обе части, тогда в обоих половинах отобразится одинаковая информация, так можно делать при полной заливке или очистке экрана для сокращения времени.
  • Задать номер страницы (X = 0…7)
  • И позицию в ней (Y = 0…63)
  • Передать данные DB0…DB7

Система команд контроллеров дисплея приведена в следующей таблице:

Команды управления

Обратите внимание на команду задания координат начальной строки. С помощью этой команды можно изменить начальное положение координат страниц 0…7, на которые разделена область дисплея по вертикали. Изменив координаты страницы 0, получим автоматический сдвиг всех остальных страниц. Эта команда может использоваться для получения эффекта “скроллинга” — перемещения изображения сверху-вниз или наоборот. Изменяя координаты начальной строки через заданные промежутки времени, можно “прокручивать” изображение на экране.

Для формирования выводимого массива изображения или отдельных символов есть полезная программа, разработанная Петром Высочанским,  – графический редактор для индикаторов на основе контроллеров KS0107 и KS0108. Вот ссылка на сайт www.ikarab.narod.ru, где можно скачать эту программу и почитать о ней подробней.

Графический редактор

В ней я сделал массив с кириллическим шрифтом для вывода на дисплей. Все делается легко и быстро – рисуем нужный символ по точкам и получаем готовый код для его отображения.

Кириллица

Для управления индикатором был использован микроконтроллер ATMega16. Вот схема подключения:

Схема подключения индикатора

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

Далее опишу код программы. Написав программу, заодно провел эксперимент, сделал два проекта: один в AVR Studio 4, другой в Atmel Studio 6. Оба проекта с абсолютно одинаковым кодом и уровнями оптимизации. При этом, 6-я студия в соревновании вышла вперед, сгенерировав меньший по размеру файл прошивки. Выигрыш относительно AVR Studio 4 — около 300 байт, при размере выходного файла чуть более 5 КБ. Это не призыв переходить на новую Atmel Studio 6, я сам с ней практически незнаком, чтобы делать выводы и сравнения. Всего лишь результат одного эксперимента.

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

#include <avr/io.h> 
#include <util/delay.h> 

#define data_port PORTB 
#define data_ddr DDRB 
#define cmd_port PORTA 
#define cmd_ddr DDRA 
#define CS1 0 
#define CS2 1 
#define DI 2 
#define RW 3 
#define E 4 
#define RST 5


Затем массив шрифта:

unsigned char letter[44][8] = { 
{0x00,0xFC,0x22,0x22,0x22,0xFC,0x00,0x00},//А
{0x00,0xFE,0x92,0x92,0x92,0x60,0x00,0x00},//Б
{0x00,0xFE,0x92,0x92,0x92,0x6C,0x00,0x00},//В
{0x00,0xFE,0x02,0x02,0x02,0x02,0x00,0x00},//Г 
{0x00,0xC0,0x7C,0x42,0x7C,0xC0,0x00,0x00},//Д
{0x00,0xFE,0x92,0x92,0x82,0x82,0x00,0x00},//Е
{0x00,0xFE,0x93,0x92,0x83,0x82,0x00,0x00},//Ё 
{0x00,0xEE,0x10,0xFE,0x10,0xEE,0x00,0x00},//Ж
{0x00,0x44,0x82,0x92,0x92,0x6C,0x00,0x00},//З
{0x00,0xFE,0x20,0x10,0x08,0xFE,0x00,0x00},//И 
{0x00,0xFE,0x20,0x11,0x08,0xFE,0x00,0x00},//Й
{0x00,0xFE,0x10,0x28,0x44,0x82,0x00,0x00},//К
{0x00,0xE0,0x10,0x08,0x04,0xFE,0x00,0x00},//Л
{0x00,0xFE,0x04,0x08,0x04,0xFE,0x00,0x00},//М
{0x00,0xFE,0x10,0x10,0x10,0xFE,0x00,0x00},//Н
{0x00,0x7C,0x82,0x82,0x82,0x7C,0x00,0x00},//О 
{0x00,0xFE,0x02,0x02,0x02,0xFE,0x00,0x00},//П 
{0x00,0xFE,0x22,0x22,0x22,0x1C,0x00,0x00},//Р 
{0x00,0x7C,0x82,0x82,0x82,0x82,0x00,0x00},//С 
{0x00,0x02,0x02,0xFE,0x02,0x02,0x00,0x00},//Т 
{0x00,0x8E,0x90,0x90,0x90,0x7E,0x00,0x00},//У 
{0x00,0x1C,0x22,0xFE,0x22,0x1C,0x00,0x00},//Ф
{0x00,0xC6,0x28,0x10,0x28,0xC6,0x00,0x00},//Х 
{0x00,0xFE,0x80,0x80,0xFE,0x80,0x00,0x00},//Ц 
{0x00,0x0E,0x10,0x10,0x10,0xFE,0x00,0x00},//Ч
{0x00,0xFE,0x80,0xF0,0x80,0xFE,0x00,0x00},//Ш 
{0x00,0xFE,0x80,0xF0,0x80,0xFE,0x80,0x00},//Щ 
{0x00,0x02,0xFE,0x88,0x88,0x70,0x00,0x00},//Ъ
{0x00,0xFE,0x88,0x88,0x70,0xFE,0x00,0x00},//Ы 
{0x00,0xFE,0x88,0x88,0x70,0x00,0x00,0x00},//Ь 
{0x00,0x82,0x82,0x92,0x92,0x7C,0x00,0x00},//Э 
{0x00,0xFE,0x10,0x7C,0x82,0x7C,0x00,0x00},//Ю 
{0x00,0x8C,0x52,0x32,0x12,0xFE,0x00,0x00},//Я 
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//Пробел
{0x00,0x7C,0xA2,0x92,0x8A,0x7C,0x00,0x00},//0 
{0x00,0x00,0x00,0x04,0xFE,0x00,0x00,0x00},//1 
{0x00,0x84,0xC2,0xA2,0x92,0x8C,0x00,0x00},//2
{0x00,0x42,0x92,0x9A,0x96,0x62,0x00,0x00},//3 
{0x00,0x30,0x28,0x24,0x22,0xFE,0x00,0x00},//4 
{0x00,0x4E,0x8A,0x8A,0x8A,0x72,0x00,0x00},//5
{0x00,0x7C,0x8A,0x8A,0x8A,0x70,0x00,0x00},//6 
{0x00,0x82,0x42,0x22,0x12,0x0E,0x00,0x00},//7 
{0x00,0x6C,0x92,0x92,0x92,0x6C,0x00,0x00},//8
{0x00,0x0C,0x92,0x52,0x32,0x1C,0x00,0x00}};//9


Далее функции, формирующие команды дисплея:

//Посылка команды дисплею 
void lcd_cmd(unsigned char cmd)
 {
  _delay_us(1);
  data_port = cmd;//выводим команду на DB0-DB7
  _delay_us(1);
  cmd_port |= (1<<E);//устанавливаем "1" на E 
  _delay_us(1);
  cmd_port &= ~(1<<E);// сбрасываем Е
  _delay_us(1);
  } 

//Посылка данных дисплею 
void lcd_data(unsigned char data)
 {
  _delay_us(1);
  data_port = data;//данные на DB0-DB7
  _delay_us(1);
  cmd_port |= (1<<DI);//устанавливаем "1" на DI - передаются данные
  _delay_us(1);
  cmd_port |= (1<<E);//устанавливаем "1" на E
  _delay_us(1);
  cmd_port &= ~(1<<E);// сбрасываем Е
  _delay_us(1);
  cmd_port &= ~(1<<DI);// сбрасываем DI
  _delay_us(1);
 } 

//Инициализация дисплея 
void lcd_init()
 {
  data_ddr = 0xFF;
  cmd_ddr |= (1<<DI)|(1<<RW)|(1<<E)|(1<<CS1)|(1<<CS2)|(1<<RST);
  cmd_port |= (1<<RST);
  cmd_port &=~((1<<CS2)|(1<<CS1));
  cmd_port |= (1<<CS2);
  lcd_cmd(0x3F);
  cmd_port &=~((1<<CS2)|(1<<CS1));
  cmd_port |= (1<<CS1);
  lcd_cmd(0x3F);
 } 

//Заливка дисплея 
void lcd_fill()
 {
  cmd_port &=~((1<<CS2)|(1<<CS1));
  cmd_port |= (1<<CS2);
  for(unsigned char i=0; i<8; i++)//X
  {
   lcd_cmd(0xB8+i);
   for(unsigned char j=0; j<64; j++)//Y
    {
     lcd_cmd(0x40+j);
     lcd_data(0xFF);//записываем везде 1
    }
  } 
  cmd_port &=~((1<<CS2)|(1<<CS1));
  cmd_port |= (1<<CS1);
  for(unsigned char i=0; i<8; i++)//X
  {
   lcd_cmd(0xB8+i);
   for(unsigned char j=0; j<64; j++)//Y
   {
    lcd_cmd(0x40+j);
    lcd_data(0xFF);//записываем везде 1
   }
  }
  cmd_port &=~((1<<CS2)|(1<<CS1));
 }
 
//Очистка дисплея 
void lcd_clr()
 { 
  cmd_port &=~((1<<CS2)|(1<<CS1));
  cmd_port |= (1<<CS2);
  for(unsigned char i=0; i<8; i++)//X
  {
   lcd_cmd(0xB8+i);
   for(unsigned char j=0; j<64; j++)//Y
    {
     lcd_cmd(0x40+j);
     lcd_data(0x00);//записываем везде 0
    }
  }
  cmd_port &=~((1<<CS2)|(1<<CS1));
  cmd_port |= (1<<CS1);
  for(unsigned char i=0; i<8; i++)//X
   { 
    lcd_cmd(0xB8+i);
    for(unsigned char j=0; j<64; j++)//Y
     {
      lcd_cmd(0x40+j);
      lcd_data(0x00);//записываем везде 0
     }
   } 
  cmd_port &=~((1<<CS2)|(1<<CS1));
 }
 
//Установка страницы 0..7 
void set_x(unsigned char pos_x)
 {
  data_port = pos_x|0xB8;//Координата X
  _delay_us(1);
  cmd_port |= (1<<E);//устанавливаем "1" на E
  _delay_us(1);
  cmd_port &= ~(1<<E);// сбрасываем Е
  _delay_us(1);
 } 

//Установка координат внутри страницы 0..63
void set_y(unsigned char pos_y)
 {
  data_port = pos_y|0x40;//Координата Y
  _delay_us(1);
  cmd_port |= (1<<E);//устанавливаем "1" на E
  _delay_us(1);
  cmd_port &= ~(1<<E);// сбрасываем Е
  _delay_us(1);
 } 

//Функция вывода символа в заданные координаты 
void lcd_char(unsigned char sector, unsigned char pos_x, unsigned char pos_y, unsigned char abc)
 {
  cmd_port &=~((1<<CS2)|(1<<CS1));
  switch(sector)
  {
   case 1: cmd_port |= (1<<CS2);
   break;
   case 2: cmd_port |= (1<<CS1);
   break;
  }
  set_x(pos_x);
  //Вывод символа
  for(unsigned char i=0; i<8; i++)
  {
   set_y(pos_y+i);
   lcd_data(letter[abc][i]);
  }
 }

И, в основном цикле программы передаем команды и символы на дисплей:

int main(void)
 {
  lcd_init();
  lcd_fill();
  _delay_ms(1000);
  lcd_clr();
  lcd_cmd(0xC0);
  lcd_char(1, 0, 0, 0);
  lcd_char(1, 0, 8, 1);
  lcd_char(1, 0, 16, 2);
  lcd_char(1, 0, 24, 3);
  lcd_char(1, 0, 32, 4);
  lcd_char(1, 0, 40, 5);
  lcd_char(1, 0, 48, 6);
  lcd_char(1, 0, 56, 7);
  lcd_char(2, 0, 0, 8);
  lcd_char(2, 0, 8, 9);
  lcd_char(2, 0, 16, 10);
  lcd_char(2, 0, 24, 11);
  lcd_char(2, 0, 32, 12);
  lcd_char(2, 0, 40, 13);
  lcd_char(2, 0, 48, 14);
  lcd_char(2, 0, 56, 15);
  lcd_char(1, 1, 0, 16);
  lcd_char(1, 1, 8, 17);
  lcd_char(1, 1, 16, 18);
  lcd_char(1, 1, 24, 19);
  lcd_char(1, 1, 32, 20);
  lcd_char(1, 1, 40, 21);
  lcd_char(1, 1, 48, 22);
  lcd_char(1, 1, 56, 23);
  lcd_char(2, 1, 0, 24);
  lcd_char(2, 1, 8, 25);
  lcd_char(2, 1, 16, 26);
  lcd_char(2, 1, 24, 27);
  lcd_char(2, 1, 32, 28);
  lcd_char(2, 1, 40, 29);
  lcd_char(2, 1, 48, 30);
  lcd_char(2, 1, 56, 31);
  lcd_char(1, 2, 0, 32);
  lcd_char(1, 2, 8, 34);
  lcd_char(1, 2, 16, 35);
  lcd_char(1, 2, 24, 36);
  lcd_char(1, 2, 32, 37);
  lcd_char(1, 2, 40, 38);
  lcd_char(1, 2, 48, 39);
  lcd_char(1, 2, 56, 40);
  lcd_char(2, 2, 0, 41);
  lcd_char(2, 2, 8, 42);
  lcd_char(2, 2, 16, 43);
  while(1);
 }

Результат:

Работа программы_1Работа программы_2

P.S. Код программы в тексте отображался как-то криво, пришлось вручную поправлять его в html – мог допустить опечатки. Поэтому архив с файлами программы прикрепляю дополнительно, если будете пробовать в “железе” – используйте эти файлы.

Файлы программы KS0108

Другие статьи:

  2 Responses to “Графический дисплей с управляющим контроллером KS0108”

  1. Хорошая статья. Огромное спасибо! Вы не планируете написать статью про подключение к STM32 TFT LCD и SD карту памяти c файловой системой?