Мы уже не раз обращали свой взор к сегментным индикаторам и к тому, как с ними работать (урок про динамическую индикацию). Благодаря простоте изготовления они имеют низкую стоимость и применяются повсеместно, где требуется показать пользователю простые числовые данные.
Наиболее распространены цифровые индикаторы, которые применяются для отображения цифр (хотя могут показать и некоторые буквы). Такие индикаторы называются семисегментными, так как цифра в них представлена семью сегментами.
Однако, существуют и более редкие цифро-буквенные индикаторы, в которых сегментов в два раза больше. То есть они могут отображать не только цифры, но и буквы.
В этом уроке мы рассмотрим модуль индикатора с микросхемой VK16K33, которая является аналогом более известной HK16K33.
Спецификация
- напряжение питания: от 3,3 до 5 В;
- высота символа: 13,7 мм;
- I2C адрес по-умолчанию: 0x70;
- размеры: 28 x 51 x 10 мм.
Список необходимых компонентов
Для выполнения всех инструкций в данном уроке, кроме самого индикатора, потребуется Ардуино-совместимый контроллер и немного распространённых компонентов. Если вам не хватает каких-то, можно добавить их в корзину прямо здесь и затем оформить заказ в нашем интернет-магазине.
Подключение
Микросхема VK16K133 имеет 5 выводов:
- SCL,SDA — контакты I2C шины;
- GND — земля;
- VCC — питание;
- VI2C — питание I2C шины (допускается 5 В и 3,3 В).
Подключаем к Arduino по известной схеме:
Arduino | +5V | GND | A4 | A5 |
VK16K33 | VCC,VI2C | GND | SDA | SCL |
На обратной стороне модуля есть три перемычки (A0, A1, A2), которые позволяют менять адрес устройства. Пригодится если потребуется подключить сразу несколько индикаторов к одной шине.
Изначально, все перемычки разомкнуты и адрес устройства равен 0x70. Таблица допустимых адресов выглядит следующим образом:
Перемычки | Адрес |
— | 0x70 |
A0 | 0x71 |
A1 | 0x72 |
A0,A1 | 0x73 |
A2 | 0x74 |
A0,A2 | 0x75 |
A1,A2 | 0x76 |
A0,A1,A2 | 0x77 |
Программа
Для вывода всех четырех знаков в микросхему VK16K33 необходимо передать 9 байт данных. Первый байт — это код команды, оставшиеся 8 — коды сегментов. Почему байт целых 8, если цифры всего четыре? Потому что для кодирования всех 14 сегментов необходимо 2 байта. В первом содержатся все сегменты для цифр, а во втором байте — дополнительные сегменты для букв.
Пусть все эти 8 байт хранятся в некотором массиве data. Напишем две вспомогательные функции: writeData — для передачи этих байт через I2C интерфейс в микросхему и clear — для очистки индикатора.
#include <Wire.h>
#define I2C_ADDRESS 0x70
void writeData( uint8_t *data ){
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(0);
for(byte i=0; i<8; i++){
Wire.write(data[i]);
}
Wire.endTransmission();
}
Единственный вопрос здесь может вызывать строчка Wire.write(0). Это мы передаём команду с кодом 0, которая отвечает как раз за отображение символов на индикаторе. Хотя у VK16K33 есть и другие команды (например, управление яркостью), в данном уроке мы не будем их рассматривать.
void clear(){
for(byte i=0; i<8; i++){
data[i] = 0x00;
}
writeData(data);
}
Для очистки индикатора мы забиваем его память нулями.
Откуда узнать коды сегментов? Код зависит от того, как на плате разведены дорожки от микросхемы к индикатору. Если никакой документации на модуль нет, можно узнать эти коды перебрав все возможные варианты с помощью программы на той же Arduino. Для данного модуля мы уже сделали это:
uint16_t segments[15] = {
// прямые
0x0001, // верх
0x0002, // прав верх
0x0004, // прав низ
0x0008, // низ
0x0010, // лев низ
0x0020, // лев верх
0x0040, // лев центр
0x0080, // прав центр
// косые
0x0100, // лев верх
0x0200, // центр верх
0x0400, // прав верх
0x0800, // лев низ
0x1000, // центр низ
0x2000, // прав низ
0x4000 // точка
};
Можно заметить, что сегменты из первой группы занимают строго один байт (младший), а из второй группы — строго следующий байт (старший). Будем хранить их в массиве, где каждый элемент представлен двумя байтами. uint16_t — это как раз тип данных для двухбайтовой переменной (16 бит).
Теперь зная коды и имея вспомогательные функции, напишем процедуру для отображения нужного сегмента, а заодно сольём воедино все блоки программы.
#include <Wire.h>
#define I2C_ADDRESS 0x70
uint16_t segments[15] = {
// прямые
0x0001, // верх
0x0002, // прав верх
0x0004, // прав низ
0x0008, // низ
0x0010, // лев низ
0x0020, // лев верх
0x0040, // лев центр
0x0080, // прав центр
// косые
0x0100, // лев верх
0x0200, // центр верх
0x0400, // прав верх
0x0800, // лев низ
0x1000, // центр низ
0x2000, // прав низ
0x4000 // точка
};
uint8_t data[8];
void setup() {
Wire.begin();
for(byte i=0; i<15; i++){
clear();
segment( 0, i );
delay(1000);
}
}
void loop() {
}
void segment( uint8_t digit, uint8_t idx ){
data[digit*2] = segments[idx] & 0xFF;
data[digit*2+1] = segments[idx] >> 8;
writeData(data);
}
void writeData( uint8_t *data ){
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(0);
for(byte i=0; i<8; i++){
Wire.write(data[i]);
}
Wire.endTransmission();
}
void clear(){
for(byte i=0; i<8; i++){
data[i] = 0x00;
}
writeData(data);
}
Процедура segment принимает в качестве аргументов индекс знака на индикаторе и индекс сегмента, который необходимо высветить в рамках данного знака.
Загружаем программу на контроллер и наблюдаем перебор всех сегментов, включая точку.
Вывод цифр на VK16K33
Усложним программу. Отображение отдельных сегментов полезно лишь для понимания работы модуля. Будем отображать цифры. Каждая цифра от 0 до 9 состоит из сегментов. Например 1 — это всего два сегмента: правый верхний и правый нижний. Их коды: 0x0002 и 0x0004. Складываем их и получаем код для единицы — 0x0006.
Таким образом можно составить массив всех цифр. Добавим процедуру для высвечивания цифры и программа готова.
#include <Wire.h>
#define I2C_ADDRESS 0x70
uint8_t digits[10] = {
0x003F, // 0
0x0006, // 1
0x00DB, // 2
0x00CF, // 3
0x00E6, // 4
0x00ED, // 5
0x00FD, // 6
0x0007, // 7
0x00FF, // 8
0x00EF // 9
};
uint8_t data[8];
void setup() {
Wire.begin();
for(byte i=0; i<10; i++){
clear();
digit( 0, i );
delay(1000);
}
}
void loop() {
}
void digit( uint8_t digit, uint8_t idx ){
data[digit*2] = digits[idx] & 0xFF;
data[digit*2+1] = digits[idx] >> 8;
writeData(data);
}
void writeData( uint8_t *data ){
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(0);
for(byte i=0; i<8; i++){
Wire.write(data[i]);
}
Wire.endTransmission();
}
void clear(){
for(byte i=0; i<8; i++){
data[i] = 0x00;
}
writeData(data);
}
Как можно заметить, процедура digit идентична процедуре segment из предыдущей программы.
Загружаем программу и наблюдаем в первом знаке перебор всех цифр.
Вывод букв на VK16K33
Наконец, то, о чем весь этот урок — отображение букв. В отличие от цифр, буквы, действительно требуют дополнительных сегментов из-за своей сложности. Особенно, буквы кириллицы. Даже на этом индикаторе некоторые кириллические буквы проблематично отобразить: Ц, Щ, Ъ, Й, Ё, Д.
Точно также, как и в случае цифр, простым складыванием кодов сегментов получаем нужные нам буквы и храним их в массиве. Напишем программу, которая заставит индикатор мигать словами: СВЕТ и ТЬМА.
#include <Wire.h>
#define I2C_ADDRESS 0x70
uint16_t symbols[7] = {
0x1201, // Т
0x04FD, // В
0x0039, // С
0x00F9, // Е
0x0C86, // А
0x00FC, // Ь
0x0536 // М
};
uint8_t data[8];
uint8_t flag;
void setup() {
Wire.begin();
}
void loop() {
clear();
if(flag){
symbol( 0, 2 );
symbol( 1, 1 );
symbol( 2, 3 );
symbol( 3, 0 );
} else {
symbol( 0, 0 );
symbol( 1, 5 );
symbol( 2, 6 );
symbol( 3, 4 );
}
flag = !flag;
delay(1000);
}
void symbol( uint8_t digit, uint8_t idx ){
data[digit*2] = symbols[idx] & 0xFF;
data[digit*2+1] = symbols[idx] >> 8;
writeData(data);
}
void writeData( uint8_t *data ){
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(0);
for(byte i=0; i<8; i++){
Wire.write(data[i]);
}
Wire.endTransmission();
}
void clear(){
for(byte i=0; i<8; i++){
data[i] = 0x00;
}
writeData(data);
}
Загружаем на контроллер и наблюдаем такую феерию: