Опубликовано: 19.02.2017 16:27
Автор: jazon
Итак, сегодня мы познакомимся с таким интересным девайсом как 7-ми сегментный дисплей управляемый сдвиговыми регистрами на базе микросхемы 74HC595. К нам на обзор попал дисплей от фирмы RobotDyn, он управляется именно при помощи двух выходных сдвиговых регистров 74HC595 включенных последовательно, но тут есть один нюанс - один регистр управляет включением разряда, а другой регистр - включением сегментов в выбранном разряде. То есть в данном случае, цифры в разрядах на дисплее зажигаются динамически. Не углубляясь в подробности внутренней работы сдвигового регистра, просто попробуем подключить дисплей от фирмы RobotDyn к плате Arduino Nano, поработать с командами сдвига(shifOut()) и отобразить нужные символы и цифры. Вот так выглядит этот дисплей:
Основные характеристики дисплея:
На фото(вид сзади) можно видеть пины подключения:
Всего их пять, это:
На фото видно - что пины подключения расположены с правой и левой стороны, и имеют одинаковые названия. Казалось бы, нужно подключаться к пинам правой стороны(если перевернуть прибор, то они будут слева), что выглядит логично, ведь к примеру, дисплеи на базе драйвера MAX7219 подключаются именно так, а пины с дугой стороны являются пинами подключения каскадом следующих устройств. Но, как выяснилось, дисплей на базе сдвигового регистра можно подключать к платам Arduino используя любую(правая либо левая разницы нет) группу пинов. Что же, подключим дисплей к плате Aruino Nano следующим образом:
Пины питания(+5V и GND) нужно подключить также соответствующим образом, думаю что с этим ни у кого проблем не возникнет. И начнем пожалуй с простейшего скетча, попробуем отобразить цифру 5 в самом крайнем левом разряде(индекс разряда в таком случае будет равен 0). Скетч достаточно хорошо прокомментирован, но далее всё равно будет разъяснен каждый шаг, потому что этот дисплей не так прост как это может показаться в первый раз. Итак, скетч:
/*
* Пины для подсоединения к I/O Wireless Shield for Arduino Nano
* Display[DIO] to PIN3
* Display[SCK] to PIN4
* Display[RCK] to PIN2
* Display[GND]
* Display[+5V]
*/
const int Latch = 2; //RCK 2
const int Data = 3; //DIO 3
const int Clock = 4; //SCK 4
/*
* Определяем разряды на трубке, начнем с крайнего левого
* он будет первым(самым младшим), крайний правый будет шестым
* самым старшим.
*/
// Разряды 0-й 1-й 2-й 3-й* 4-й 5-й 6-й 7-й*
byte numbDigits[8] = {0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08};
/*
* Формируем из сегментов цифру
*/
// Цифры 0 1 2 3 4 5 6 7 8 9
byte numbSerial[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};
/*
* Точка для разряда
*/
byte pointDigit = 0x80;
void setup()
{
//Определяем все назначенные пины как выходы
pinMode(Latch, OUTPUT);
pinMode(Clock, OUTPUT);
pinMode(Data, OUTPUT);
}
void loop()
{
//Чтобы начать работу с дисплеем нужно установить пин "защелку"
//в низкий уровень, пока есть низкий уровень можно записывать
//в регистр значения, кодирующие включение нужных сегментов
//в нужном разряде
digitalWrite(Latch, LOW);
//Сначала записывается байт кодирующий цифру 5
shiftOut(Data, Clock, MSBFIRST, numbSerial[5]);
//Затем записывается байт зажигающий разряд 0
shiftOut(Data, Clock, MSBFIRST, numbDigits[0]);
//Затем нужно "защелкнуть данные", установить пин Latch
//в высокий уровень, если этого не сделать то на дисплее будет
//видна непонятная "каша" из разных зажженых сегментов
digitalWrite(Latch, HIGH);
}
На фото в самом начале видно, что модуль состоит из двух трехразрядных дисплеев. Поэтому, если попробовать записать байт зажигающий разряд под номером 3, либо 7, то дисплей не отобразит ничего, у него эти разряды отсутствуют. В скетче есть небольшие поправки - массив байт, хранящий адреса разрядов, прокомментирован так что адрес отсутствующего разряда помечен звёздочкой. Но если бы это был дисплей с восемью разрядами, то попытка записать байт зажигающий разряд под номером 3 либо 7 была бы успешна. А сейчас смотрим результат работы скетча, на фото:
Следующий интересный момент - как зажечь десятичную точку? Даже если дисплей и называется семисегментным, он всё равно имеет восьмой сегмент - это и есть десятичная точка на разряде. Итак, в вышеуказанном скетче есть такая строка:
/*
* Десятичная точка для разряда
*/
byte pointDigit = 0x80;
И чтобы зажечь цифру вместе с точкой нужно дополнить строку, в которой происходит сдвиг байта кодирующего цифру, следующим образом:
//Сначала записывается байт кодирующий цифру 5
shiftOut(Data, Clock, MSBFIRST, numbSerial[5] ^ pointDigit);
В Си подобных языках программирования эта операция называется "Исключающее ИЛИ", результат можно видеть на фото:
Следующий скетч выполняет поочередное заполнение разрядов цифрами. Опять же, следует заметить, что массив с адресами байтов разрядов, хранит также и адреса отсутствующих разрядов. То есть по сути этот массив определенно для восьми разрядного дисплея, но подойдет он и для шести разрядного. Смотрим скетч:
/*
* Пины для подсоединения к I/O Wireless Shield for Arduino Nano
* Display[DIO] to PIN3
* Display[SCK] to PIN4
* Display[RCK] to PIN2
* Display[GND]
* Display[+5V]
*/
const int Latch = 6; //RCK 2
const int Data = 8; //DIO 3
const int Clock = 7; //SCK 4
/*
* Переменная cycleCounter - счетчик циклов сканирования
* за каждый цикл сканирования loop, будет отрисовываться одна цифра
* в заданном разряде.
*/
static unsigned char cycleCounter = 0;
/*
* Определяем разряды на трубке, начнем с крайнего левого
* он будет первым(самым младшим), крайний правый будет шестым
* самым старшим.
*/
// Разряды 0-й 1-й 2-й 3-й* 4-й 5-й 6-й 7-й*
byte numbDigits[8] = {0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08};
/*
* Формируем из сегментов цифру
*/
// Цифры 0 1 2 3 4 5 6 7 8 9
byte numbSerial[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};
/*
* Точка для разряда
*/
byte pointDigit = 0x80;
void setup()
{
//Определяем все назначенные пины как выходы
pinMode(Latch, OUTPUT);
pinMode(Clock, OUTPUT);
pinMode(Data, OUTPUT);
}
void loop()
{
//Перед началом цикла сканирования, сравниваем счетчик
//если прошло 7 циклов, и счетчик был инкрементирован
//то сбрасываем
if(cycleCounter == 8)
{
cycleCounter = 0;
}
//Чтобы начать работу с дисплеем нужно установить пин "защелку"
//в низкий уровень, пока есть низкий уровень можно записывать
//в регистр значения, кодирующие включение нужных сегментов
//в нужном разряде
digitalWrite(Latch, LOW);
//Сначала сдвигаем байт кодирующий цифру
shiftOut(Data, Clock, MSBFIRST, numbSerial[cycleCounter]);
//Затем сдвигаем байт кодирующий разряд
shiftOut(Data, Clock, MSBFIRST, numbDigits[cycleCounter]);
//Затем нужно "защелкнуть данные", установить пин Latch
//в высокий уровень, если этого не сделать то на дисплее будет
//видна непонятная "каша" из разных зажженых сегментов
digitalWrite(Latch, HIGH);
cycleCounter ++;
delayMicroseconds(1000);
}
Результат работы скетча можно видеть на фото:
В скетче добавлены несколько новых строк, теперь отрисовка цифр в разрядах происходит в цикле. На фото видно что пропущены третий и седьмой разряд, так как они отсутствуют физически. Причем происходит это в теле самой функции loop(), которая сама по себе является циклом сканирования программы. Переменная cycleCounter является счетчиком циклов, при достижении значения счетчика равного 8 счетчик обнуляется. За один цикл сканирования программы отрисовывается одна цифра в одном разряде начиная с нулевого, потом зажигание передаётся на следующий по порядку разряд. Инструкция delayMicroseconds() добавлена для того чтобы избежать визуального эффекта тусклого свечения дисплея, при слишком быстрой динамической отрисовке сегменты просто не успевают зажечься в нормальную силу яркости. Но и здесь главное не переусердствовать, увеличение значения передаваемое в функцию delayMicroseconds() может привести к другому неприятному визуальному эффекту - мерцанию. Самый оптимальный диапазон значений микросекунд - от 1000 до 3000, подбирается так сказать "под себя".
Изменив в программе одну строку можно отобразить точку для каждого из разрядов. Выше уже было описана операция "исключающее ИЛИ", применение её в следующей строке скетча:
//Сначала сдвигаем байт кодирующий цифру
shiftOut(Data, Clock, MSBFIRST, numbSerial[cycleCounter] ^ pointDigit);
Получим следующий результат, смотрим фото:
Ну и напоследок важно уделить внимание созданию своих символов для этого типа дисплея. Каких либо генераторов для этой задачи не придумано, поэтому кодировать символы будет по таблице. В общем то здесь нет ничего сложного, просто меняем значение бита в байте адресованного определённому сегменту. Чтобы зажечь определённый сегмент нужно сбросить бит в ноль, чтобы погасить определённый сегмент нужно установить бит в единицу. Слева - все биты в байте установлены в единицу, следовательно все сегменты погашены, справа - зажжены сегменты генерирующие символ J.
Для примера можно взять слово Sensor, состоящее из 6-ти символов, как раз по размерности дисплея. Закодировать каждый символ этого слова нетрудно если следовать вышеуказанной таблице. Вывести это слово посимвольно можно изменив и дополнив скетч который выводит цифры. Добавим массив байт кодирующий слово Sensor :
/*
* Сообщение "Sensor"
*/ //* //*
byte Sensor[8] = {0x92, 0x86, 0xAB, 0x00, 0x92, 0xA3, 0xAF, 0x00};
Вместо массива с кодами цифр, выводим массив с сообщением Sensor
shiftOut(Data, Clock, MSBFIRST, Sensor[cycleCounter]);
Результат можно видеть на фото ниже:
На этом вроде бы и всё, обзор был задуман как "быстрый пуск", думаю что у нас это вполне получилось. К недостаткам этого модуля можно отнести следующее - к нему нельзя подключить ещё один такой же последовательно, как например можно подключать дисплеи на базе драйвера MAX7219. Но всё же модули дисплеи такого типа найдут свою нишу применения в абсолютно разных проектах, всё зависит от задумки автора. Пишите свои отзывы и комментируйте статью, предлагайте свои решения по применению. Спасибо за внимание.
Если у Вас есть опыт в работе с Arduino и собственно есть время для творчества, мы приглашаем всех желающих стать авторами статей публикуемых на нашем портале. Это могут быть как уроки, так и рассказы о ваших экспериментах с Arduino. Описание различных датчиков и модулей. Советы и наставления начинающим. Пишите и размещайте свои статьи в соответсвующей ветке форума.