Главная / 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шт, не думал что они такие геморройные ))

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

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