ЖК-модуль с контроллером HT1621 состоит из 6-и разрядов (7 сегментов) и дополнительно содержит индикатор батареи. Управление индикатора осуществляется по шине 3-Wire.
Подключение индикатора к плате Arduino Nano достаточно простое, индикатор содержит всего 5 контактов (или 6, дополнительный контакт может быть для отдельного питания подсветки). Три контакта эта шина управления CS WR Data, а два остальных это питание GND и VCC (5 В).
Для поддержки ЖК-модуля на базе платформы Arduino имеются несколько библиотек, одна из них это библиотека HT1621. Библиотека достаточно проста в использовании, поддерживает вывод на индикатор цифр и различных символов, так же имеет отдельную функцию для управления индикатором батареи.
Ниже показан скетч с простым примером использования библиотеки:
#include <HT1621.h> // http://rcl-radio.ru/wp-content/uploads/2024/06/HT1621-2.1.2.zip HT1621 lcd; void setup(){ lcd.begin(6, 7, 8); // (cs, wr, Data) lcd.backlight(); lcd.clear(); lcd.print("HELLO"); delay(5000); lcd.setBatteryLevel(3); lcd.print("HT1621"); delay(5000); lcd.clear(); } void loop(){ lcd.print(millis()/1000.0, 1); delay(100); }
Так же библиотека содержит несколько примеров которые Вы можете использовать для Ваших проектов.
Контроллер HT1621 может применяться с различными ЖК индикаторами, следует учитывать, что все ЖК индикаторы разные, у них может быть разное кол-во выводов, кол-во сегментов, порядковый номер сегментов, такие индикаторы могут содержать сегментные группы в виде матрицы для вывода символов или содержать определенное кол-во сегментов в группе для вывода цифр или символов (7 сегментов или 16 сегментов). Поэтому предложенная библиотека подходит только конкретно для этого ЖК-модуля. Если Вам необходимо использовать контроллер HT1621 но с другим ЖК-индикатором, то необходимо понять как работает данный контроллер хотя бы на примере выше описанного ЖК-модуля, но без применения библиотек.
Как работает ЖК-индикатор можно узнать из http://rcl-radio.ru/?p=131856.
Описание контроллера HT1621:
- Напряжение питания: 2.4…5.2 В
- Встроенный 256 kHz RC генератор
- Работа с внешним 32.768 kHz кварцем или от источника частоты 256 kHz
- Выбор напряжения смещения (bias) 1/2 или 1/3 и выбор продолжительности работы 1/2, 1/3 или 1/4 (duty)
- 1/2 или 1/3 bias — уровень напряжения
- 1/2, 1/3 или 1/4 duty — кол-во COM линий
- Внутренний формирователь основной частоты
- Две частоты работы драйвера звонка (2kHz/4kHz)
- Режим пониженного энергопотребления
- Внутренний генератор и WDT
- Вывод основной частоты или вывод переполнения WDT
- 8 видов источников для получения основной частоты и для WDT
- 32×4 LCD драйвер
- Встроенная оперативная память дисплея 32х4
- Трехпроводный, последовательный интерфейс
- Встроенное формирование частоты возбуждение LCD
- Программное конфигурирование
- Два режима работы: c данными и с командами
- Операции обращения к памяти с автоинкрементом
- Три режима доступа к данным
- Вывод VLCD для настройки рабочего напряжения LCD
Память контроллера содержит 32 байта по 4-е бита, что дает возможность управления 128-ю сегментами ЖК-индикатора.
Как видно на скриншоте, каждый бит отвечает за одну COM линию, а один адрес отвечает за один вывод линии сегментов. Поэтому контроллер содержит 4 вывода под COM линию и 32 вывода под линию сегментов. Дополнительно есть выводы под подключение пьезоизлучателя и подключение кварцевого резонатора, в данном ЖК-модуле кварцевый резонатор не используется, используется внутренняя тактовая частота 256 кГц.
В данной ЖК-модуле который содержит 6 разрядов по 7 сегментов плюс запятые и индикатор батареи, подключение индикатора к ЖК-контроллеру достаточно упорядочено, а именно на каждый разряд отводится 2 адреса памяти и используются все 4-е COM линии. Адреса 0:1 (из адресов 0:31) отвечают только за самый младший разряд индикатора, а адреса 10:11 отвечают за самый старший разряд индикатора, при этом в разрядах где-то есть запятая, а где-то вместо запятой происходит управление индикатором батареи.
Для запуска ЖК-модуля используется две основные команды, это команда конфигурации (код 100) и команда записи памяти (код 101).
Отправка кода 100
Коды конфигурации необходимы для корректного запуска контроллера, в даташите имеется подробный список необходимых команд, но для запуска данного модуля необходимо всего 4-е команды:
- data_100(HT1621_SYS_EN); // включение внутреннего тактового генератора
- data_100(HT1621_BIAS); // использует 1/3 bias (3 уровня напряжений) и 4-е COM линии
- data_100(HT1621_RC256); // Внутренняя тактовая частота RC-генератора 256 кГц
- data_100(HT1621_LCD_ON); // включаем индикатор
Функция отправки кода 100
void data_100(int data){ digitalWrite(LCD_CS,LOW); for(int i = 2; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (0b100 >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 8; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (data >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } delayMicroseconds(10);digitalWrite(LCD_CS,HIGH); delayMicroseconds(10);digitalWrite(LCD_WR,LOW); delayMicroseconds(10); }
Байт отправки кода 100 содержит двоичное число 100 и 9-ь бит кода команды.
Отправка кода 101
Код команды 101 (отправка данных в память) содержит двоичное число 101, адрес памяти А5:А0 и данные COM линии D0:D3.
Функция отправки кода 101
void data_101(int addr, int data){ digitalWrite(LCD_CS,LOW); for(int i = 2; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (0b101 >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 5; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (addr >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 0; i <=3; i++){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (data >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } delayMicroseconds(10);digitalWrite(LCD_CS,HIGH); delayMicroseconds(10);digitalWrite(LCD_WR,LOW); }
Если для примера отправить две команды записи памяти:
data_101(0,0b0110);
data_101(1,0b1100);
то на индикаторе в самом младшем разряде загорится цифра 4, так как каждый бит отправляемый в память отвечает за свой сегмент индикатора.
Ниже показано распределение бит по сегментам:
data_101(0,0bABC DP);
data_101(1,0bFGED);
Как видно адрес 0 отвечает за сегменты ABC и dp, а адрес памяти 1 за сегменты FGED.
Ниже показан простой пример управления ЖК-индикатором без использования библиотек, это код поддерживает вывод цифр, добавить различные символы по примеру кода вывода цифр достаточно просто.
Основная функция для вывода цифр и символов:
print_lcd(n, m, dp);
где:
n — это номер разряда, m — выводимая цифра или код символа(например код 11 выведет знак «-«), dp — выводит запятую или управляет индикатором батареи.
Используя этот пример запуска ЖК-модуля на HT1621 Вы можете запустить любой другой ЖК-индикатор который использует контроллер HT1621.
#define LCD_CS 6 #define LCD_WR 7 #define LCD_DATA 8 #define HT1621_BIAS 0x52 // 1/3duty 4com #define HT1621_SYS_DIS 0x00 // Turn off the oscillator system oscillator and LCD bias generator #define HT1621_SYS_EN 0x02 // Turn on the system oscillator #define HT1621_LCD_OFF 0x04 // Turn off LCD bias #define HT1621_LCD_ON 0x06 // Turn on the LCE bias #define HT1621_XTAL 0x28 // external clock #define HT1621_RC256 0x30 // internal clock void setup() { pinMode(LCD_CS, OUTPUT); pinMode(LCD_WR, OUTPUT); pinMode(LCD_DATA, OUTPUT); digitalWrite(LCD_CS,HIGH); digitalWrite(LCD_WR,HIGH); data_100(HT1621_SYS_EN); // Turn on the system oscillator data_100(HT1621_BIAS); // BIAS 13 4 public ports data_100(HT1621_RC256); // Use RC_256K system clock source, on-chip RC oscillator data_100(HT1621_LCD_ON); clear_lcd(); } void loop(){ print_lcd(0, 6,false); print_lcd(1, 5,true); print_lcd(2, 4,false); print_lcd(3, 3,true); print_lcd(4, 2,true); print_lcd(5, 11,false); delay(500); } void print_lcd(byte raz, byte num, bool dp){ switch(raz){ case 0: raz=0;break; case 1: raz=2;break; case 2: raz=4;break; case 3: raz=6;break; case 4: raz=8;break; case 5: raz=10;break; } switch(num){ case 0: data_101(raz,0b1110+dp);data_101(raz+1,0b1011);break; // 0 case 1: data_101(raz,0b0110+dp);data_101(raz+1,0b0000);break; // 1 case 2: data_101(raz,0b1100+dp);data_101(raz+1,0b0111);break; // 2 case 3: data_101(raz,0b1110+dp);data_101(raz+1,0b0101);break; // 3 case 4: data_101(raz,0b0110+dp);data_101(raz+1,0b1100);break; // 4 case 5: data_101(raz,0b1010+dp);data_101(raz+1,0b1101);break; // 5 case 6: data_101(raz,0b1010+dp);data_101(raz+1,0b1111);break; // 6 case 7: data_101(raz,0b1110+dp);data_101(raz+1,0b0000);break; // 7 case 8: data_101(raz,0b1110+dp);data_101(raz+1,0b1111);break; // 8 case 9: data_101(raz,0b1110+dp);data_101(raz+1,0b1101);break; // 9 case 10: data_101(raz,0);data_101(raz+1,0);break; // пусто case 11: data_101(raz,0b0000);data_101(raz+1,0b0100);break; // - } } void clear_lcd(){for(int a=0;a<=32;a++){data_101(a,0);}} void data_100(int data){ digitalWrite(LCD_CS,LOW); for(int i = 2; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (0b100 >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 8; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (data >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } delayMicroseconds(10);digitalWrite(LCD_CS,HIGH); delayMicroseconds(10);digitalWrite(LCD_WR,LOW); delayMicroseconds(10); } void data_101(int addr, int data){ digitalWrite(LCD_CS,LOW); for(int i = 2; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (0b101 >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 5; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (addr >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 0; i <=3; i++){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (data >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } delayMicroseconds(10);digitalWrite(LCD_CS,HIGH); delayMicroseconds(10);digitalWrite(LCD_WR,LOW); }
Практический пример использования ЖК-модуля на контроллере HT1621
Соберем для примера простые часы на базе DS3231. На дисплей будет выводится время (часы—минуты) и температура (датчик температуры встроен в DS3231). Так же часы будут иметь две кнопки коррекции времени.
Соберите схему
Скетч
#define LCD_CS 6 #define LCD_WR 7 #define LCD_DATA 8 #define HH 2 #define MM 3 #define HT1621_BIAS 0x52 // 1/3duty 4com #define HT1621_SYS_DIS 0x00 // Turn off the oscillator system oscillator and LCD bias generator #define HT1621_SYS_EN 0x02 // Turn on the system oscillator #define HT1621_LCD_OFF 0x04 // Turn off LCD bias #define HT1621_LCD_ON 0x06 // Turn on the LCE bias #define HT1621_XTAL 0x28 // external clock #define HT1621_RC256 0x30 // internal clock #include <Wire.h> #include <DS3231.h> // http://rcl-radio.ru/wp-content/uploads/2022/10/DS3231.zip DS3231 clock; RTCDateTime DateTime; unsigned long times; int hour,minut,secon,temp; void setup() { clock.begin(); //clock.setDateTime(__DATE__, __TIME__); // Устанавливаем время на часах, основываясь на времени компиляции скетча pinMode(LCD_CS, OUTPUT); pinMode(LCD_WR, OUTPUT); pinMode(LCD_DATA, OUTPUT); pinMode(HH,INPUT_PULLUP); pinMode(MM,INPUT_PULLUP); digitalWrite(LCD_CS,HIGH); digitalWrite(LCD_WR,HIGH); data_100(HT1621_SYS_EN); // Turn on the system oscillator data_100(HT1621_BIAS); // BIAS 13 4 public ports data_100(HT1621_RC256); // Use RC_256K system clock source, on-chip RC oscillator data_100(HT1621_LCD_ON); clear_lcd(); } void loop(){ DateTime = clock.getDateTime(); hour = DateTime.hour; minut = DateTime.minute; secon = DateTime.second; if(digitalRead(HH)==LOW){hour++;if(hour>23){hour=0;} clock.setDateTime(2024, 2, 22, hour, minut, 0);} if(digitalRead(MM)==LOW){minut++;if(minut>59){minut=0;} clock.setDateTime(2024, 2, 22, hour, minut, 0);} if(secon<50){ print_lcd(0, minut%10,false); print_lcd(1, minut/10%10,false); if(millis()-times<500){print_lcd(2, 11,false);print_lcd(3, 11,false);} if(millis()-times>=500){print_lcd(2, 10,false);print_lcd(3, 10,false);} if(millis()-times>=1000){times=millis();} print_lcd(4, hour%10,false); print_lcd(5, hour/10%10,false); } if(secon>=50){ temp=clock.readTemperature()*10; print_lcd(5, 10,false); print_lcd(4, temp/100%10,false); print_lcd(3, temp/10%10,false); print_lcd(2, temp%10,true); print_lcd(1, 12,false); print_lcd(0, 13,false); } delay(200); } void print_lcd(byte raz, byte num, bool dp){ switch(raz){ case 0: raz=0;break; case 1: raz=2;break; case 2: raz=4;break; case 3: raz=6;break; case 4: raz=8;break; case 5: raz=10;break; } switch(num){ case 0: data_101(raz,0b1110+dp);data_101(raz+1,0b1011);break; // 0 case 1: data_101(raz,0b0110+dp);data_101(raz+1,0b0000);break; // 1 case 2: data_101(raz,0b1100+dp);data_101(raz+1,0b0111);break; // 2 case 3: data_101(raz,0b1110+dp);data_101(raz+1,0b0101);break; // 3 case 4: data_101(raz,0b0110+dp);data_101(raz+1,0b1100);break; // 4 case 5: data_101(raz,0b1010+dp);data_101(raz+1,0b1101);break; // 5 case 6: data_101(raz,0b1010+dp);data_101(raz+1,0b1111);break; // 6 case 7: data_101(raz,0b1110+dp);data_101(raz+1,0b0000);break; // 7 case 8: data_101(raz,0b1110+dp);data_101(raz+1,0b1111);break; // 8 case 9: data_101(raz,0b1110+dp);data_101(raz+1,0b1101);break; // 9 case 10: data_101(raz,0);data_101(raz+1,0);break; // пусто case 11: data_101(raz,0b0000);data_101(raz+1,0b0100);break; // - case 12: data_101(raz,0b1100);data_101(raz+1,0b1100);break; // градус case 13: data_101(raz,0b1000);data_101(raz+1,0b1011);break; // C } } void clear_lcd(){for(int a=0;a<=32;a++){data_101(a,0);}} void data_100(int data){ digitalWrite(LCD_CS,LOW); for(int i = 2; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (0b100 >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 8; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (data >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } delayMicroseconds(10);digitalWrite(LCD_CS,HIGH); delayMicroseconds(10);digitalWrite(LCD_WR,LOW); delayMicroseconds(10); } void data_101(int addr, int data){ digitalWrite(LCD_CS,LOW); for(int i = 2; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (0b101 >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 5; i >=0; i--){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (addr >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } for(int i = 0; i <=3; i++){ digitalWrite(LCD_WR,LOW);delayMicroseconds(10); digitalWrite(LCD_DATA, (data >> i) & 1); digitalWrite(LCD_WR,HIGH);delayMicroseconds(10); } delayMicroseconds(10);digitalWrite(LCD_CS,HIGH); delayMicroseconds(10);digitalWrite(LCD_WR,LOW); }
Дополнительные материалы:
- HT1621.pdf
- Скетч > Термометр DS18B20+ЖК-модуль HT1621, 2,4 дюйма (Arduino) > http://forum.rcl-radio.ru/viewtopic.php?pid=10544#p10544
- Скетч > Терморегулятор DS18B20+ЖК-модуль HT1621, 2,4 дюйма (Arduino) > http://forum.rcl-radio.ru/viewtopic.php?pid=10545#p10545
- Скетч > Часы с будильником ЖК-модуль HT1621, 2,4 дюйма (Arduino) > http://forum.rcl-radio.ru/viewtopic.php?pid=10546#p10546