Дисплей на сдвиговых регистрах 74НС595 - обзор и возможности

19.02.2017 16:27

jazon



        Итак, сегодня мы познакомимся с таким интересным девайсом как 7-ми сегментный дисплей управляемый сдвиговыми регистрами на базе микросхемы 74HC595. К нам на обзор попал дисплей от фирмы RobotDyn, он управляется именно при помощи двух выходных сдвиговых регистров 74HC595 включенных последовательно, но тут есть один нюанс - один регистр управляет включением разряда, а другой регистр - включением сегментов в выбранном разряде. То есть в данном случае, цифры в разрядах на дисплее зажигаются динамически. Не углубляясь в подробности внутренней работы сдвигового регистра, просто попробуем подключить дисплей от фирмы RobotDyn к плате Arduino Nano, поработать с командами сдвига(shifOut()) и отобразить нужные символы и цифры. Вот так выглядит этот дисплей:


Основные характеристики дисплея:

  • Количество отображаемых цифр - 6 (шести разрядный)
  • Цвет сегментов - белый
  • Напряжение питания 5 вольт
  • Управление сегментами на базе микросхемы 74HC595

На фото(вид сзади) можно видеть пины подключения:

Всего их пять, это:

  • DIO - Пин данных
  • SCK - Пин тактирования
  • RCK - Пин "защелка"
  • GND - Подключение к земле
  • +5V  - Подключение питания 

        На фото видно - что пины подключения расположены с правой и левой стороны, и имеют одинаковые названия. Казалось бы, нужно подключаться к пинам правой стороны(если перевернуть прибор, то они будут слева), что выглядит логично, ведь к примеру, дисплеи на базе драйвера MAX7219 подключаются именно так, а пины с дугой стороны являются пинами подключения каскадом следующих устройств. Но, как выяснилось, дисплей на базе сдвигового регистра можно подключать к платам Arduino используя любую(правая либо левая разницы нет) группу пинов. Что же, подключим дисплей к плате Aruino Nano следующим образом:

  • Display 74HC595[PIN DIO]        -        Arduino Nano [PIN 3]
  • Display 74HC595[PIN SCK]        -        Arduino Nano [PIN 4]
  • Display 74HC595[PIN RCK]        -        Arduino Nano [PIN 2]

        Пины питания(+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. Описание различных датчиков и модулей. Советы и наставления начинающим. Пишите и размещайте свои статьи в соответсвующей ветке форума.