RSS блога
Подписка
LCD12864 5V Serial Port LCD Display Monitor Blue Screen Liquid Crystal Display with Backlight and Chinese Word Stock ECT-230296
- Цена: $10.50
- Перейти в магазин
Доброе время суток! Наконец-то я получил долгожданный графический монохромный дисплей синего цвета разрешением 128x64. Подробности – далее…
Порадовало то что дисплей пришел в точности, такой как на картинке при заказе.
Обратил внимание на этот факт только потому, что недавно был заказан у другого продавца подобный дисплей синего цвета, а прислали дисплей зелёного цвета.
Упаковка также порадовала: дисплей был аккуратно завёрнут в пупырчатый пакетик красного цвета и заклеен кусочком скотча, также имеется и наклейка со штрихкодом и артикулом продавца.
Внутри пакета обнаружился сам дисплей, опять же порадовало то, что дисплей доехал без каких либо видимых повреждений и царапин.
На лицевой стороне имеется защитная плёнка.
На обратной стороне имеется маркировка самого дисплея, выводов интерфейса управления, каждый вывод подписан, также есть построечный резистор VR1, судя по всему это встроенный регулятор контрастности который позволяет не подключать вывод 3 (V0).
Ну что же, приступим к освоению данного дисплея.
Очень быстро был найден соответствующий datasheet. В запросе в google 12864B PDF быстро находим родной datasheet.
К примеру здесь
Из него выяснилось, что дисплей управляется контроллером ST7920 и заточен под интерфейс SPI, так как контакт 15 (PSB) звонится прямо на землю.
Думаю это легко можно изменить, перекинув резистор R10 на позицию R9. Это конечно догадка, но нам и так сойдет. Разберёмся позже. А ещё был удивлён широким температурным диапазоном работы дисплея, по датащиту он варьируется в диапазоне от -20 и до 70 градусов цельсия!
Ну с распиновкой всё понятно, а как быть с контроллером? Как оказалось в сети полно информации и по контроллеру, например здесь
Поискав побольше информации нашлось и множество примеров подключения, различные примеры работы с данным дисплеем.
Например:
— пример 1
— пример 2
— пример 3
— пример 4
Я же буду подключать свой дисплей к микроконтроллеру ATMEGA32 (просто потому что есть и памяти программ хватает для экспериментов)
Писать код буду в Atmel studio 6 которую можно взять на официальном сайте www.atmel.com совершенно бесплатно.
И так, подключаем дисплей по простенькой схемке:
По началу я решил сэкономить время и воспользоваться примерами и описанием вот отсюда, скачав библиотеки и примеры вот отсюда
Немного поправив код у меня кое что получилось.
Хотя я прописал частоту тактового генератора в компиляторе, связь с дисплеем была неустойчивой и сильно сыпались пиксели из за помех на шине SPI дисплея. Тем не менее удалось получить вот такую картинку.
Разбираться и копаться в чужых библиотеках я не стал и потому решил написать свой код с нуля, а заодно и разобраться что к чему.
И так дисплей имеет два набора инструкций для работы. Это basic instruction и extended instruction. Режимы работы можно задать как при инициализации так и в любое время когда потребуется. В конце кода это демонстрируется. Также в данном дисплее имеется знакогенератор и всевозможных символов на разных языках. В основном это китайские символа но есть также английские символа и к великой радости русские.
Для примера выведу на дисплей рамку в режиме basic instruction.
Вот как это выглядит:
Потом переключаю дисплей в режим графики extended instruction и сразу же вижу много мусора в памяти дисплея которую надо очистить или можно сразу вывести графическое изображение. Я решил очистить дисплей при помощи функции QC12864_clear_graph.
При написании этой функции удалось выяснить интересные моменты о которых я ничего не нашел в datasheet. Первое это интересную адресацию координат в режиме графики при передаче данных через SPI. В дисплей нужно передавать по 2 байта как указано в datasheet. Координаты расположены следующим образом: Сначала загружаются первые 16 байт которые оказываются в первой строке. Это ровно 128 bit (16*8=128). Но вот вторая строка из 128 bit оказывается во второй части дисплея. Причём если адресация по горизонтали (в программе это ox) инкриминируется автоматически, то по вертикали инкремент не происходит и данные пишутся с начала координат, т.е. после 256 bit данные начинают загружаться с начала координат. И так попеременно данные будут загружаться то в первую часть дисплея то во второй. Это на мой взгляд весьма неудобно. Потому пришлось применить хитрость с адресацией чтоб вывести изображение последовательно.
По завершению очистки дисплея увидим рамку которая создавалась в начале. Как видите графика не затрагивает данные которые мы вводили ранее.
Теперь выведем массив из 1024 байт, это простенькая картинка которая находится в массиве qweqwe. Как и с очисткой дисплея пришлось применить хитрый алгоритм.
В итоге подучим вот такую вот картинку:
Ну и в заключении переключимся в режим basic instruction и очистим дисплей командой QC12864_clear(); и вернём режим графики т.е. extended instruction послав на дисплей команду SendSPI(0,0b00110110);
К сожалению последовательный интерфейс оказался очень медленным способом вывода графических изображений а потому думаю врятли в дальнейшем буду его использовать с данным дисплеем. А вот по паралельному интерфейсу вполне можно добиться приемлемой скорости даже для простенькой анимации.
Итог:
В общем, я остался доволен, дисплей вполне подходит для многих задач, к примеру реализации часов, метеостанции, гаджетов для авто и многих других приборов где требуется вывод разнообразной информации. Но для новичков мало разбирающихся в программировании дисплей немного сложен в освоении.
Порадовало то что дисплей пришел в точности, такой как на картинке при заказе.
Обратил внимание на этот факт только потому, что недавно был заказан у другого продавца подобный дисплей синего цвета, а прислали дисплей зелёного цвета.
Упаковка также порадовала: дисплей был аккуратно завёрнут в пупырчатый пакетик красного цвета и заклеен кусочком скотча, также имеется и наклейка со штрихкодом и артикулом продавца.
Внутри пакета обнаружился сам дисплей, опять же порадовало то, что дисплей доехал без каких либо видимых повреждений и царапин.
На лицевой стороне имеется защитная плёнка.
На обратной стороне имеется маркировка самого дисплея, выводов интерфейса управления, каждый вывод подписан, также есть построечный резистор VR1, судя по всему это встроенный регулятор контрастности который позволяет не подключать вывод 3 (V0).
Ну что же, приступим к освоению данного дисплея.
Очень быстро был найден соответствующий datasheet. В запросе в google 12864B PDF быстро находим родной datasheet.
К примеру здесь
Из него выяснилось, что дисплей управляется контроллером ST7920 и заточен под интерфейс SPI, так как контакт 15 (PSB) звонится прямо на землю.
Думаю это легко можно изменить, перекинув резистор R10 на позицию R9. Это конечно догадка, но нам и так сойдет. Разберёмся позже. А ещё был удивлён широким температурным диапазоном работы дисплея, по датащиту он варьируется в диапазоне от -20 и до 70 градусов цельсия!
Ну с распиновкой всё понятно, а как быть с контроллером? Как оказалось в сети полно информации и по контроллеру, например здесь
Поискав побольше информации нашлось и множество примеров подключения, различные примеры работы с данным дисплеем.
Например:
— пример 1
— пример 2
— пример 3
— пример 4
Я же буду подключать свой дисплей к микроконтроллеру ATMEGA32 (просто потому что есть и памяти программ хватает для экспериментов)
Писать код буду в Atmel studio 6 которую можно взять на официальном сайте www.atmel.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);
}
}
#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 дисплея. Тем не менее удалось получить вот такую картинку.
Разбираться и копаться в чужых библиотеках я не стал и потому решил написать свой код с нуля, а заодно и разобраться что к чему.
В итоге был написан вот такой демо-код
#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
}
}
#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.
Вот как это выглядит:
Потом переключаю дисплей в режим графики extended instruction и сразу же вижу много мусора в памяти дисплея которую надо очистить или можно сразу вывести графическое изображение. Я решил очистить дисплей при помощи функции QC12864_clear_graph.
При написании этой функции удалось выяснить интересные моменты о которых я ничего не нашел в datasheet. Первое это интересную адресацию координат в режиме графики при передаче данных через SPI. В дисплей нужно передавать по 2 байта как указано в datasheet. Координаты расположены следующим образом: Сначала загружаются первые 16 байт которые оказываются в первой строке. Это ровно 128 bit (16*8=128). Но вот вторая строка из 128 bit оказывается во второй части дисплея. Причём если адресация по горизонтали (в программе это ox) инкриминируется автоматически, то по вертикали инкремент не происходит и данные пишутся с начала координат, т.е. после 256 bit данные начинают загружаться с начала координат. И так попеременно данные будут загружаться то в первую часть дисплея то во второй. Это на мой взгляд весьма неудобно. Потому пришлось применить хитрость с адресацией чтоб вывести изображение последовательно.
По завершению очистки дисплея увидим рамку которая создавалась в начале. Как видите графика не затрагивает данные которые мы вводили ранее.
Теперь выведем массив из 1024 байт, это простенькая картинка которая находится в массиве qweqwe. Как и с очисткой дисплея пришлось применить хитрый алгоритм.
В итоге подучим вот такую вот картинку:
Ну и в заключении переключимся в режим basic instruction и очистим дисплей командой QC12864_clear(); и вернём режим графики т.е. extended instruction послав на дисплей команду SendSPI(0,0b00110110);
К сожалению последовательный интерфейс оказался очень медленным способом вывода графических изображений а потому думаю врятли в дальнейшем буду его использовать с данным дисплеем. А вот по паралельному интерфейсу вполне можно добиться приемлемой скорости даже для простенькой анимации.
Итог:
В общем, я остался доволен, дисплей вполне подходит для многих задач, к примеру реализации часов, метеостанции, гаджетов для авто и многих других приборов где требуется вывод разнообразной информации. Но для новичков мало разбирающихся в программировании дисплей немного сложен в освоении.
Самые обсуждаемые обзоры
+74 |
3807
147
|
+56 |
3977
69
|
+35 |
3103
60
|
Хотел вывести туда инфу от MPD, пришлось отказаться от этой идеии.
(большинство сред разработки обладают готовыми решениями, вам просто остаётся придумать что сделать, а вопрос как сделать обычно уже решён встроенными библиотеками.)
Обзор плюсую.
можно же взять дешевле, цветной, 320x240
себе такой брал
https://aliexpress.com/item/item/Brand-New-2-2-Serial-SPI-TFT-Color-LCD-Module-Display-240X320-w-PCB-Adpater/1149927865.html
А обзор действительно. понравился! Если бы увлекался моделированием, обязательно воспользовался бы статьей.
Только вот хотел плюсануть, а пишет что недостаточно кармы. Объясните что это? И почему ее вдруг стало недостаточно? Вроде правил не нарушал и жил мирно…
действительно интересно
Ну и размер экрана тут побольше (не разрешение, а именно размер).
А простенький дисплейчик текстовый куда угодно прицепить можно. Знакогенератор ему не нужен (ну тут проблема с русским часто возникает). Но если пару строк текста вывести — самое оно. А если сдвиговый регистр прицепить, то и совсем шоколадно — ноги экономятся.
А дисплей ТС чем хорош? Через SPI медленно, но реально. Если раз в несколько секунд обновлять пару цифр — то вполне. Параллельный интерфейс намного быстрее. Но знакогенератор уже памяти отъест. А если еще и графику прикручивать, то уже нужно оперативки прилично под хранение состояния дисплея. МК уже не MidRange даже получается. Для 8 битных, конечно. Мда… Делал недавно одну железку. Дисплейчик простенький от Нокии. Все красиво… пока плату не протравил и не запаял. Полкило мозга не хватило… Отлаживал на макетке — там проблем не было. Так и не удалось ужаться… Пришлось новый МК искать, чтоб по ногам совпадал, и памяти больше было.
Я вообще покупал 10 за $50… модуль прекрасный… использую в разработке системы умный дом…
Еще есть дешевле и менее габаритные от NOKIA мне они прислись больше по душе, так как достаточное разрешение и очень низкое энерго потребление…
Вот на ебей например ebay.com/itm/200940401722 8.85$.Может и дешевле можно найти.
На Тини мало что покупаю, но некоторые единичные вещи там удобнее покупать.
У меня целая эпопея с этими дисплеями получилась.
Машина у меня синего цвета (Nexia). Сделал всю подсветку салона (освещение панели приборов, подсветку кнопок стеклоподъемникови т.д.) на синих светодиодах.
Вместо часов стоит бортовой компрьютер МК-Н
Зеленое свечение данного компьютера выбивается из общей картины. Естественно, захотелось заменить на синий.
Здесь: https://aliexpress.com/item/item/12864-128x64-Dots-Graphic-Blue-Color-Backlight-LCD-Display-Module/561548748.html заказал дисплей. прислали почти такой же, как на картинке у продавца, но с 16 выводами.
Написал продавцу типа " не то прислали", отвечает: «не вопрос, пришлю другой!». Удивило то что не попросил вернуть неправильный. Думаю «ну здорово!», жду еще 2 месяца.
Прислали с 20 выводами но плата и сам дисплей большего размера, хотя, судя по маркировке (12864А), разрешение такое же (128х64).
В результате в машине до сих пор зеленый экран, а в столе валяется 2 дисплея, которые не подходят.
Может, кто подскажет, можно ли подключить дисплей с 16 выводами вместо 20?
Меняю 2 дисплея на 1! Вы поняли, какие предлагаю, и какой нужен мне.
P.S. Пршу прощения за качество фото — снимал телефоном.