Главная / TinyDeal / Графический монохромный дисплей LCD12864B

Графический монохромный дисплей LCD12864B

Перейти в магазин ( $10.50 )

Графический монохромный дисплей LCD12864BДоброе время суток! Наконец-то я получил долгожданный графический монохромный дисплей синего цвета разрешением 128x64. Подробности – далее…

Порадовало то что дисплей пришел в точности, такой как на картинке при заказе.
Обратил внимание на этот факт только потому, что недавно был заказан у другого продавца подобный дисплей синего цвета, а прислали дисплей зелёного цвета.
Упаковка также порадовала: дисплей был аккуратно завёрнут в пупырчатый пакетик красного цвета и заклеен кусочком скотча, также имеется и наклейка со штрихкодом и артикулом продавца.
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
Внутри пакета обнаружился сам дисплей, опять же порадовало то, что дисплей доехал без каких либо видимых повреждений и царапин.

На лицевой стороне имеется защитная плёнка.
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
На обратной стороне имеется маркировка самого дисплея , выводов интерфейса управления, каждый вывод подписан, также есть построечный резистор VR1, судя по всему это встроенный регулятор контрастности который позволяет не подключать вывод 3 (V0).
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com

Ну что же, приступим к освоению данного дисплея.
Очень быстро был найден соответствующий datasheet. В запросе в google 12864B PDF быстро находим родной datasheet.
К примеру здесь
Из него выяснилось, что дисплей управляется контроллером ST7920 и заточен под интерфейс SPI, так как контакт 15 (PSB) звонится прямо на землю.
Думаю это легко можно изменить, перекинув резистор R10 на позицию R9. Это конечно догадка, но нам и так сойдет. Разберёмся позже. А ещё был удивлён широким температурным диапазоном работы дисплея, по датащиту он варьируется в диапазоне от -20 и до 70 градусов цельсия!
Ну с распиновкой всё понятно, а как быть с контроллером? Как оказалось в сети полно информации и по контроллеру, например здесь
Поискав побольше информации нашлось и множество примеров подключения, различные примеры работы с данным дисплеем.
Например:
- пример 1
- пример 2
- пример 3
- пример 4

Я же буду подключать свой дисплей к микроконтроллеру ATMEGA32 (просто потому что есть и памяти программ хватает для экспериментов)
Писать код буду в Atmel studio 6 которую можно взять на официальном сайте http://www.atmel.com совершенно бесплатно.
И так, подключаем дисплей по простенькой схемке:
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
По началу я решил сэкономить время и воспользоваться примерами и описанием вот отсюда, скачав библиотеки и примеры вот отсюда

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include "u8g.h"
u8g_t u8g;
void draw(void)
{
u8g_SetFont(&u8g, u8g_font_6x10);
u8g_DrawStr(&u8g, 0, 15, "Hello World!");
}
int main(void)
{
u8g_InitSPI(&u8g, &u8g_dev_st7920_128x64_sw_spi, PN(2, 2), PN(2, 1), PN(3, 0), U8G_PIN_NONE, PN(3,4) );

for(;;)
{
u8g_FirstPage(&u8g);
do
{
draw();
} while ( u8g_NextPage(&u8g) );
u8g_Delay(100);
}
}

Хотя я прописал частоту тактового генератора в компиляторе, связь с дисплеем была неустойчивой и сильно сыпались пиксели из за помех на шине SPI дисплея. Тем не менее удалось получить вот такую картинку.
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
Разбираться и копаться в чужых библиотеках я не стал и потому решил написать свой код с нуля, а заодно и разобраться что к чему.

#include <avr/io.h>
#include <avr/iom32.h>
#include <avr/delay.h>
// тут указываем на каких выводах сидить наш дисплей
#define CS PC0
#define SID PC1
#define SCLK PC2
#define RES PC4

const unsigned char * pic;
extern const unsigned char qweqwe[];

const unsigned char qweqwe[1024] = {
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,
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,
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,
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,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0xFF,0xE0,0x00,0x7F,0xE0,0x00,0x00,0x00,0x03,0xE0,0x00,
0x00,0x00,0x00,0x00,0x03,0xFF,0xF8,0x00,0xFF,0xF8,0x00,0x00,0x00,0x1F,0xE0,0x00,
0x00,0x00,0x00,0x00,0x0F,0xFF,0xFE,0x03,0xFF,0xFC,0x00,0x00,0x00,0xFF,0xC0,0x00,
0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0x07,0xFF,0xFE,0x00,0x00,0x03,0xFF,0xF8,0x00,
0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0x8F,0xFF,0xFF,0x00,0x00,0x1F,0xFE,0x04,0x00,
0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xEF,0xFF,0xFF,0x80,0x00,0x7F,0xF0,0x0C,0x00,
0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x01,0xFF,0x80,0x30,0x00,
0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x0F,0xFC,0x01,0xC0,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x1F,0xE0,0x0E,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x7F,0x00,0x70,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0xF8,0x07,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,0xC1,0xF8,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x90,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x60,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xE7,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x79,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0xC1,0xFF,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x0E,0x00,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x70,0x00,0x7F,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x03,0xC0,0x00,0x3F,0xFF,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x9E,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x07,0xF0,0x00,0x00,0x07,0xFF,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x0F,0xC0,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x3F,0xE0,0x00,0x00,0x01,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
// Функция инициализации портов
void init_io(void)
{
//Порт A на выход
DDRA=0xFF;
PORTA=0x00;
//Порт B на выход
DDRB=0xFF;
PORTB=0x00;
//Порт С на выход
DDRC=0XFF;
PORTC=0x00;
//Порт В на выход
DDRD=0xFF;
PORTD=0x00;
}
// Функция отправки 1 на дисплей
void SendSPI_1 (void)
{
PORTC|=(1<<SID);
_delay_us(250);
PORTC|=(1<<SCLK);
_delay_us(250);
PORTC&=~(1<<SID);
_delay_us(250);
PORTC&=~(1<<SCLK);
_delay_us(250);
}
// Функция отправки 0 на дисплей
void SendSPI_0 (void)
{
_delay_us(250);
PORTC|=(1<<SCLK);
_delay_us(500);
PORTC&=~(1<<SCLK);
_delay_us(250);
}
// Функция отправки на дисплей бита RS и байта на дсплей
void SendSPI (uint8_t sell, uint8_t Data)
{
unsigned char i;

_delay_us(100);
PORTC&=~(1<<SID)|(1<<SCLK);
_delay_us(100);
// Отправляем на дисплей 5 единиц
for (i=0; i<5; i++) { SendSPI_1(); }
// Отправляем 0
SendSPI_0();
// Отправляем бит RS
if (sell==0) { SendSPI_0(); } else { SendSPI_1(); }
// Отправляем 0
SendSPI_0();
// Отправляем старшие 4 бита
for (i=0; i<4; i++) { if (((Data>>(7-i))&0x01)==1) SendSPI_1(); else SendSPI_0();
}
// Отправляем 4 бита равные 0
for (i=0; i<4; i++)
{
SendSPI_0();
}
// Отправляем 4 младших бита
for (i=0; i<4; i++) { if (((Data>>(3-i))&0x01)==1) SendSPI_1(); else SendSPI_0();
}
// Отправляем 4 бита равные 0
for (i=0; i<4; i++)
{
SendSPI_0();
}
_delay_us(100);

}
// Функция инициализации дисплея
void QC12864_init (void)
{
_delay_ms(40);
PORTC|=(1<<RES);
_delay_ms(50);
PORTC|=(1<<CS);
SendSPI(0,0x30);
SendSPI(0,0x30);
SendSPI(0,0x0C);
SendSPI(0,0x01);
SendSPI(0,0x06);
}
// Фенкция очистки дисплея в режиме базовых инструкций
void QC12864_clear(void)
{
SendSPI(0,0x01);
_delay_ms(2);
}
// Функция очистки дисплея в режиме графики
void QC12864_clear_graph(void)
{
unsigned int i,tm;
uint8_t ox,oy;
tm=8;
ox=0b10000000;
oy=0b10000000;
SendSPI(0,oy);
SendSPI(0,ox);
for (i=0; i<512; i++)
{
if (i==tm)
{
tm=tm+8;
if (i<256)
{
ox=0b10000000;
} else
{
ox=0b10000000+8;
}
oy++;
if (i==256)
{
oy=0b10000000;
}
SendSPI(0,oy);
SendSPI(0,ox);
}
SendSPI(1,0x00);
SendSPI(1,0x00);
}
}

int main(void)
{
init_io();
QC12864_init();
QC12864_clear();

unsigned int i,tm; // Объявили переменные
uint8_t ox,oy; // Объявили переменные в 8bit

SendSPI(1,0xA9);
SendSPI(1,0xB3);
for (i=0; i<6; i++)
{
SendSPI(1,0xA9);
SendSPI(1,0xA5);
}

SendSPI(1,0xA9);
SendSPI(1,0xB7);

SendSPI(0,0b10000000+16);
SendSPI(1,0xA9);
SendSPI(1,0xA7);
SendSPI(0,0b10000000+23);
SendSPI(1,0xA9);
SendSPI(1,0xA7);
SendSPI(0,0b10000000+8);
SendSPI(1,0xA9);
SendSPI(1,0xA7);
SendSPI(0,0b10000000+15);
SendSPI(1,0xA9);
SendSPI(1,0xA7);
SendSPI(0,0b10000000+24);
SendSPI(1,0xA9);
SendSPI(1,0xBB);

for (i=0; i<6; i++)
{
SendSPI(1,0xA9);
SendSPI(1,0xA5);
}
SendSPI(1,0xA9);
SendSPI(1,0xBF);
_delay_ms(2000);

SendSPI(0,0b00110110); // Перевели дисплей в режим графики
QC12864_clear_graph(); //Функция очистки дисплея

//Передаём на дисплей масив картинки qweqwe

pic = qweqwe; //
ox=0b10000000;
oy=0b10000000;
SendSPI(0,oy);
SendSPI(0,ox);
tm=16;
for (i=0; i<1024; i++)
{
if (i==tm)
{
tm=tm+16;
if (i<512)
{
ox=0b10000000;
} else
{
ox=0b10000000+8;
}
oy++;
if (i==512)
{
oy=0b10000000;
}
SendSPI(0,oy);
SendSPI(0,ox);
}
SendSPI(1,*(pic + i));
}

SendSPI(0,0b00110000); // Перевели дисплей в обычный режим
QC12864_clear(); //очистели
SendSPI(0,0b00110110); // Вернули графику

while(1)
{
//TODO:: Please write your application code
}
}

И так дисплей имеет два набора инструкций для работы. Это basic instruction и extended instruction. Режимы работы можно задать как при инициализации так и в любое время когда потребуется. В конце кода это демонстрируется. Также в данном дисплее имеется знакогенератор и всевозможных символов на разных языках. В основном это китайские символа но есть также английские символа и к великой радости русские.

Для примера выведу на дисплей рамку в режиме basic instruction.
Вот как это выглядит:
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
Потом переключаю дисплей в режим графики extended instruction и сразу же вижу много мусора в памяти дисплея которую надо очистить или можно сразу вывести графическое изображение. Я решил очистить дисплей при помощи функции QC12864_clear_graph.

При написании этой функции удалось выяснить интересные моменты о которых я ничего не нашел в datasheet. Первое это интересную адресацию координат в режиме графики при передаче данных через SPI. В дисплей нужно передавать по 2 байта как указано в datasheet. Координаты расположены следующим образом: Сначала загружаются первые 16 байт которые оказываются в первой строке. Это ровно 128 bit (16*8=128). Но вот вторая строка из 128 bit оказывается во второй части дисплея. Причём если адресация по горизонтали (в программе это ox) инкриминируется автоматически, то по вертикали инкремент не происходит и данные пишутся с начала координат, т.е. после 256 bit данные начинают загружаться с начала координат. И так попеременно данные будут загружаться то в первую часть дисплея то во второй. Это на мой взгляд весьма неудобно. Потому пришлось применить хитрость с адресацией чтоб вывести изображение последовательно.

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

В итоге подучим вот такую вот картинку:
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
Ну и в заключении переключимся в режим basic instruction и очистим дисплей командой QC12864_clear(); и вернём режим графики т.е. extended instruction послав на дисплей команду SendSPI(0,0b00110110);
Графический монохромный дисплей LCD12864B. Обзор на InSKU.com
К сожалению последовательный интерфейс оказался очень медленным способом вывода графических изображений а потому думаю врятли в дальнейшем буду его использовать с данным дисплеем. А вот по паралельному интерфейсу вполне можно добиться приемлемой скорости даже для простенькой анимации.
Итог:
В общем, я остался доволен, дисплей вполне подходит для многих задач, к примеру реализации часов, метеостанции, гаджетов для авто и многих других приборов где требуется вывод разнообразной информации. Но для новичков мало разбирающихся в программировании дисплей немного сложен в освоении.

About Saiberex

Обратите внимание

STM32F103RBT6 Development Breadboard. Плата для разработки устройств на микроконтроллере STM32.

Перейти в магазин ( $13.52 )
Перейти в магазин ( $13.52 ) Доброе время суток! Наигравшись с микроконтроллерами 8bit, я решил …

2 комментария

  1. Ammateur

    обалдеть что люди могут :)

  2. Oleg

    Привет Saiberex, можешь по подробней рассказать об этом LCD, интересуют управляющие коды для переключения режимов, и принцип адресации в графическом и текстовом режиме. А так же, как можно выводить текст по мельче. В инете очень мало информации по ним, тоже приобрел 2шт, не думал что они такие геморройные ))

Добавить комментарий для Ammateur Отменить ответ

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