Arduino и сегментный ЖК индикатор

в 21:45, , рубрики: arduino, Чулан

image

Arduino, как известно, имеет прекрасную встроенную библиотеку для вывода информации на ЖК индикаторы, которая, однако, поддерживает только матричные знакосинтезирующие индикаторы. Так получилось, что у меня в руках оказался обычный сегментный индикатор, простейший 10-ти разрядный MT-10T8. Поставленная задача вполне соответствовала способностям этого индикатора – выводить текущую температуру для одного прибора, который для простоты назовём лабораторным измерителем температуры. Прибор управляется с помощью Arduino, конкретно его разновидностью Seeduino Mega (аналог Arduino Mega). После беглого знакомства с достаточно лаконичным даташитом на MT-10T8 было принято решение потратить немножко времени и подружить Arduino с данным индикатором.

В индикаторе используется простенький протокол управления, адрес и данные передаются по параллельной 4-х битной шине. Сложностей протокол никаких не представляет, адрес символа умещается в 4 бита шины, данные передаются двумя тетрадами по 4 бита (и того 8 бит на один символ). Единственное, на что следует обратить внимание и не напутать — это рекомендуемые тайминги управляющих сигналов индикатора. Желательно, чтобы на обращение к индикатору тратилось минимум процессорного времени, но при этом надо обеспечить стабильность, ибо как показала практика, индикатор очень запросто сходит с ума.

Требуемые по даташиту минимальные задержки в десятки наносекунд с помощью Arduino не обеспечить, однако после недолгих поисков на родном сайте arduino нашелся совет использовать для коротких задержек команду ассемблера “nop”

__asm__("nopnt");

Данная конструкция на 16 Мгц кристалле даёт задержку в 62.5 нс. Даташит оперирует минимальными таймингами в 50, 100 и 200 наносекунд, у нас же будет 62.5, 125 и 250 наносекунд. Вполне устраивает.

image
Подключение индикатора было произведено подручными средствами, получившийся «франкенштейн» представлен на фотографии. В управляющей программе используется аппаратная функция индикатора выводить символы «подряд», т.е. если после вывода символа по определенному адресу вызвать следующую команду передачи данных без установки адреса на шине, то символ выведется по адресу, следующему за текущим.

Теперь наверное стоит ответить на вопрос – а зачем? В самом деле, если 10ти разрядный матричный индикатор стоит 250 руб., зачем использовать минималистический сегментный индикатор, который всего на 70 руб. дешевле? Ну, во-первых, копейка рубль бережет, во-вторых, библиотека LiquidCrystal мягко говоря не маленькая по объёму. И тут минимализм рулит.

Собственно код, думаю, он не требует особых пояснений. Сначала сами функции вывода инфы на индикатор
// writeSymbol - отображает символ в заданную позицию
// address - номер знакоместа (0..9)
// symbol - код символа
// dot - отображение десятичной точки
void writeSymbol(byte address, byte symbol, boolean dot)
{
//устанавливаем адрес символа
//установка режима записи адреса
digitalWrite(A0_PIN, LOW);
//устанавливаем адрес на шине
digitalWrite(DB0_PIN, bitRead(address,0));
digitalWrite(DB1_PIN, bitRead(address,1));
digitalWrite(DB2_PIN, bitRead(address,2));
digitalWrite(DB3_PIN, bitRead(address,3));
__asm__("nopnt"); //62.5ns
//адрес отправляем в индикатор
digitalWrite(WR1_PIN, HIGH);
__asm__("nopnt""nopnt"); //125.5ns
digitalWrite(WR1_PIN, LOW);
__asm__("nopnt"); //62.5ns
//пишем данные
writeNextSymbol(symbol, dot);
}

// writeNextSymbol - отображает символ в позиции, следующей за текущей
// symbol - код символа
// dot - отображение десятичной точки
void writeNextSymbol(byte symbol, boolean dot)
{
if (dot)
symbol+=16;
//устанавливаем данные на шине
digitalWrite(DB0_PIN, bitRead(symbol,0));
digitalWrite(DB1_PIN, bitRead(symbol,1));
digitalWrite(DB2_PIN, bitRead(symbol,2));
digitalWrite(DB3_PIN, bitRead(symbol,3));
//установка режима записи данных
digitalWrite(A0_PIN, HIGH);
//делаем паузу между сигналами WR
__asm__("nopnt""nopnt""nopnt""nopnt"); //250ns
//записываем первую тетраду данных на шину
digitalWrite(WR1_PIN, HIGH);
__asm__("nopnt""nopnt"); //125.5ns
digitalWrite(WR1_PIN, LOW);
//следом пишем данные второй тетрады
//устанавливаем данные второй тетрады
digitalWrite(DB0_PIN, bitRead(symbol,4));
digitalWrite(DB1_PIN, bitRead(symbol,5));
digitalWrite(DB2_PIN, bitRead(symbol,6));
digitalWrite(DB3_PIN, bitRead(symbol,7));
//делаем паузу между сигналами WR
__asm__("nopnt""nopnt""nopnt""nopnt"); //250ns
//записываем на шину
digitalWrite(WR1_PIN, HIGH);
__asm__("nopnt""nopnt"); //125.5ns
digitalWrite(WR1_PIN, LOW);
}

// clearAll - очистка экрана
void clearAll()
{
for (int i=0;i<10;i++)
{
writeSymbol(i, 0, false);
}
}

// unblockBus- разблокировка шины адреса/данных.
// Требуется сразу после включения индикатора, так как
// состояние блокировки шины после включения не определено
void unblockBus()
{
//установка режима записи адреса
digitalWrite(A0_PIN, LOW);
//пишем адрес 0F - адрес для разблокировки шины
digitalWrite(DB0_PIN, HIGH);
digitalWrite(DB1_PIN, HIGH);
digitalWrite(DB2_PIN, HIGH);
digitalWrite(DB3_PIN, HIGH);
__asm__("nopnt"); //62.5ns
//записываем на шину
digitalWrite(WR1_PIN, HIGH);
__asm__("nopnt""nopnt"); //125.5ns
digitalWrite(WR1_PIN, LOW);
__asm__("nopnt"); //62.5ns
//данные для разблокировки: DB0=1
digitalWrite(DB0_PIN, HIGH);
digitalWrite(DB1_PIN, LOW);
digitalWrite(DB2_PIN, LOW);
digitalWrite(DB3_PIN, LOW);
//установка режима записи данных
digitalWrite(A0_PIN, HIGH);
//делаем паузу между сигналами WR
__asm__("nopnt""nopnt""nopnt""nopnt"); //250ns
//записываем на шину
digitalWrite(WR1_PIN, HIGH);
__asm__("nopnt""nopnt"); //125.5ns
digitalWrite(WR1_PIN, LOW);
}

И пример боевого применения

// Определяем, к каким пинам Arduino подключен индикатор
const byte A0_PIN=10;
const byte WR1_PIN=8;
const byte WR2_PIN=9;
const byte DB0_PIN=2;
const byte DB1_PIN=3;
const byte DB2_PIN=4;
const byte DB3_PIN=5;

// Константы подсветки определенных
// сегментов одного знакоместа индикатора
const byte SEG_A=8;
const byte SEG_B=32;
const byte SEG_C=64;
const byte SEG_D=4;
const byte SEG_E=2;
const byte SEG_F=128;
const byte SEG_G=1;
const byte SEG_H=16;

//Кодовая "таблица символов". Каждый символ это сумма констант подсвеченных сегментов
const byte WHITESPACE = 0; // ничего не подсвечено
// цифры
const byte DIGIT_ZERO = SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F;
const byte DIGIT_ONE = SEG_B + SEG_C;
const byte DIGIT_TWO = SEG_A + SEG_B + SEG_G + SEG_E + SEG_D;
const byte DIGIT_THREE = SEG_A + SEG_B + SEG_G + SEG_C+ SEG_D;
const byte DIGIT_FOUR = SEG_F + SEG_G + SEG_B + SEG_C;
const byte DIGIT_FIVE = SEG_A + SEG_F + SEG_G + SEG_C + SEG_D;
const byte DIGIT_SIX = SEG_A + SEG_F + SEG_G + SEG_C + SEG_D + SEG_E;
const byte DIGIT_SEVEN = SEG_A + SEG_B + SEG_C;
const byte DIGIT_EIGHT = SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F + SEG_G;
const byte DIGIT_NINE = SEG_A + SEG_B + SEG_C + SEG_D + SEG_F + SEG_G;

// спец. символы
const byte SYMBOL_MINUS = SEG_G;
const byte SYMBOL_C = SEG_A + SEG_F + SEG_E + SEG_D;
const byte SYMBOL_DEGREE= SEG_A + SEG_B + SEG_G + SEG_F;
const byte SYMBOL_UPDASH = SEG_A;
const byte SYMBOL_LOWDASH = SEG_D;

void setup() {
//все пины, подключенные к индикатору - на вывод
pinMode(A0_PIN, OUTPUT);
pinMode(WR1_PIN, OUTPUT);
pinMode(WR2_PIN, OUTPUT);
pinMode(DB0_PIN, OUTPUT);
pinMode(DB1_PIN, OUTPUT);
pinMode(DB2_PIN, OUTPUT);
pinMode(DB3_PIN, OUTPUT);
unblockBus();
}

void loop() {
//чистим экран первым делом
clearAll();
delay(1000);
//поехали
writeSymbol(0, DIGIT_ZERO, false);
delay(100);
writeNextSymbol( DIGIT_ONE, false);
delay(100);
writeNextSymbol( DIGIT_TWO, false);
delay(100);
writeNextSymbol( DIGIT_THREE, false);
delay(100);
writeNextSymbol( DIGIT_FOUR, false);
delay(100);
writeNextSymbol( DIGIT_FIVE, false);
delay(100);
writeNextSymbol( DIGIT_SIX, false);
delay(100);
writeNextSymbol( DIGIT_SEVEN, false);
delay(100);
writeNextSymbol( DIGIT_EIGHT, false);
delay(100);
writeNextSymbol( DIGIT_NINE, true);
delay(500);
clearAll();
writeSymbol(0, SYMBOL_MINUS, false);
delay(100);
writeNextSymbol( SYMBOL_C, false);
delay(100);
writeNextSymbol( SYMBOL_DEGREE, false);
delay(100);
writeNextSymbol( SYMBOL_UPDASH, false);
delay(100);
writeNextSymbol( SYMBOL_LOWDASH, false);
delay(1000);
clearAll();
// выведем значение минус 156.4 градуса цельсия
writeSymbol(3, SYMBOL_MINUS, false);
writeNextSymbol( DIGIT_ONE, false);
writeNextSymbol( DIGIT_FIVE, false);
writeNextSymbol( DIGIT_SIX, true);
writeNextSymbol( DIGIT_FOUR, false);
writeNextSymbol( SYMBOL_DEGREE, false);
writeNextSymbol( SYMBOL_C, false);
delay(1000);
clearAll();
// и немножко побалуемся
for (int i=0;i<10;i++)
{
writeSymbol(i, SYMBOL_UPDASH, false);
if (i>0) writeSymbol(i-1, WHITESPACE, false);
delay(200);
}
for (int i=0;i<10;i++)
{
writeSymbol(9-i, SYMBOL_LOWDASH, false);
delay(200);
}
delay(1000);
}

Автор: eshkinkot

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js