Цифро-буквенный индикатор на VK16K33

Мы уже не раз обращали свой взор к сегментным индикаторам и к тому, как с ними работать (урок про динамическую индикацию). Благодаря простоте изготовления они имеют низкую стоимость и применяются повсеместно, где требуется показать пользователю простые числовые данные.

Наиболее распространены цифровые индикаторы, которые применяются для отображения цифр (хотя могут показать и некоторые буквы). Такие индикаторы называются семисегментными, так как цифра в них представлена семью сегментами.

Однако, существуют и более редкие цифро-буквенные индикаторы, в которых сегментов в два раза больше. То есть они могут отображать не только цифры, но и буквы.

В этом уроке мы рассмотрим модуль индикатора с микросхемой VK16K33, которая является аналогом более известной HK16K33.

цифро-буквенный индикатор VK16K33

Спецификация

  • напряжение питания: от 3,3 до 5 В;
  • высота символа: 13,7 мм;
  • I2C адрес по-умолчанию: 0x70;
  • размеры: 28 x 51 x 10 мм.

Список необходимых компонентов

Для выполнения всех инструкций в данном уроке, кроме самого индикатора, потребуется Ардуино-совместимый контроллер и немного распространённых компонентов. Если вам не хватает каких-то, можно добавить их в корзину прямо здесь и затем оформить заказ в нашем интернет-магазине.

В корзину
В корзину
В корзину

Подключение

Микросхема VK16K133 имеет 5 выводов:

  • SCL,SDA — контакты I2C шины;
  • GND — земля;
  • VCC — питание;
  • VI2C — питание I2C шины (допускается 5 В и 3,3 В).
цифро-буквенный индикатор VK16K33

Подключаем к Arduino по известной схеме:

Arduino+5VGNDA4A5
VK16K33VCC,VI2CGNDSDASCL

На обратной стороне модуля есть три перемычки (A0, A1, A2), которые позволяют менять адрес устройства. Пригодится если потребуется подключить сразу несколько индикаторов к одной шине.

Изначально, все перемычки разомкнуты и адрес устройства равен 0x70. Таблица допустимых адресов выглядит следующим образом:

ПеремычкиАдрес
0x70
A00x71
A10x72
A0,A10x73
A20x74
A0,A20x75
A1,A20x76
A0,A1,A20x77

Программа

Для вывода всех четырех знаков в микросхему 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);
}

Загружаем на контроллер и наблюдаем такую феерию:


Изменено:

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.