Освой Arduino играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Arduino: 7-сегментный индикатор

Одноразрядный 7-сегментный индикатор
Четырёхразрядный 7-сегментный индикатор
Библиотека fDigitsSegtPin

Если вы научились пользоваться "световой шкалой", то следующий шаг в освоении нового компонента "7-сегментный индикатор" дастся вам легко. Он попадается практически во всех стартовых наборах.

Одноразрядный 7-сегментный индикатор

7-сегментный индикатор

Мы имеем дело опять с набором светодиодов, только на этот раз их 8 (семь полосок и один кружочек) и они расположены не друг за другом, а в определённом порядке, которые позволяют вам выводить цифры от 0 до 9.

Важная отличительная черта - у индикатора имеются общие ножки для катода (ножки 3 и 8). Всего их две и они равноценны. Это удобно, вам не нужно будет от каждого катода вести отдельный провод на землю. Достаточно выбрать один из общих катодов и от неё соединиться с GND. Аноды у всех отдельные.

Также при желании вы можете установить несколько таких индикаторов подряд для вывода больших двухзначных, трёхзначных и т.д. чисел. Но существуют готовые компактные наборы для этих целей.

На 7-сегментный индикатор распространяются те же правила, что и на стандартные светодиоды - у каждого должен быть свой резистор. Поэтому для опытов приготовьте 8 резисторов.

Схематично можно изобразить следующим образом.

7-сегментный индикатор

7-сегментный индикатор

Принципиальная схема

7-сегментный индикатор

Собираем на макетной плате. Соединяем провода по порядку, начиная с первой ножки, которая идёт на второй порт. На землю идёт восьмая ножка индикатора.

7-сегментный индикатор

Для проверки можно запустить стандартный пример Blink, только установите в качестве проверочного светодиода любой из ваших используемых портов. Я выбрал пятый порт, чтобы помигать точкой.


int led5 = 5;

Если мы хотим помигать цифрой 1, то нам надо использовать светодиоды 4 и 6, которые идут на порты 4 и 6 платы микроконтроллера.


int led4 = 4;
int led6 = 6;

Если мы захотим вывести цифру 5, то понадобится работать с пятью светодиодами, цифру 8 - уже семь светодиодов. При сложных проектах работать с таким количеством становится затруднительно. Придётся каждый раз смотреть на схему, что вспомнить, какие светодиоды нужно включить для отображения каждой цифры.

Но можно пойти другим путём. А поможет нам единица информации - байт. Байт в своём двоичном представлении состоит из 8 бит. Каждый бит может принимать значения 0 или 1. А наш светодиодный индикатор как раз и состоит из восьми светодиодов. Таким образом мы можем представить цифру на индикаторе в виде набора байт, где единица будет отвечать за включённый диод, а ноль - за выключенный диод.

Число в двоичном виде записывается следующим образом:


0b00000000

Первые два символа 0b дают понять, что речь идёт о двоичном счёте. Все нули означают, что все светодиоды будут выключены.

У нас задействованы порты от 2 по 9. Второй порт записывается в самую правую позицию. Чтобы его включить, поставим единицу.


0b00000001

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

Комбинируя набор нулей и единиц, можно создать нужные нам цифры. Например, цифра 0 будет представлена как 0b01110111.

Давайте напишем пример вывода цифры 0.


#define FIRST_SEGMENT_PIN   2
#define SEGMENT_COUNT       8

byte number0 = 0b01110111;

void setup() {
  for (int i = 0; i < SEGMENT_COUNT; ++i)
    pinMode(i + FIRST_SEGMENT_PIN, OUTPUT);
}

void loop() {
  int mask = number0;
  // для каждого из 7 сегментов индикатора определяем:
  // должен ли он быть включён. 
  // Для этого считываем бит, соответствующий текущему
  // сегменту «i». Истина — он установлен (1), ложь — нет (0)
  for (int i = 0; i < SEGMENT_COUNT; ++i) {
    boolean enableSegment = bitRead(mask, i);
    // включаем/выключаем сегмент на основе полученного значения
    digitalWrite(i + FIRST_SEGMENT_PIN, enableSegment);
  }
}

Код немного избыточен, переменная mask здесь лишняя, но она нам пригодится в следующем примере. Здесь важно, что мы пробегаемся в цикле по числу светодиодов и устанавливаем у всех режим OUTPUT. Затем также в цикле проходим через все светодиоды и узнаём, комбинацию бит с помощью метода bitRead(). Полученная информация помогает нам подсветить нужные светодиоды и получить цифру 0 на выходе.

Для остальных цифр можно также подготовить нужные наборы бит.


byte number0 = 0b01110111;
byte number1 = 0b00010100;
byte number2 = 0b10110011;
... и так далее

Но мы пойдём другим путём. Все эти значения мы поместим в массив. И будем вытаскивать по индексу. А индексом для примера нам послужит метод millis. С его помощью мы можем получить число секунд, прошедших с запуска скетча, но выводить будем только последнюю цифру прошедших секунд.


#define FIRST_SEGMENT_PIN   2
#define SEGMENT_COUNT       8

// Всего цифр 10, поэтому в массиве 10 чисел.
byte numberSegments[10] = {
  0b01110111, 
  0b00010100, 
  0b10110011, 
  0b10110110, 
  0b11010100, 
  0b11100110, 
  0b11100111, 
  0b00110100, 
  0b11110111,
  0b11110110,
};
 
void setup()
{
  for (int i = 0; i < SEGMENT_COUNT; ++i)
    pinMode(i + FIRST_SEGMENT_PIN, OUTPUT);
}
 
void loop()
{
  // определяем число, которое следует отобразить.
  //  Пусть им будет номер текущей секунды, зацикленный на десятке
  int number = (millis() / 1000) % 10;
  // получаем код из массива, в котором содержится полученная цифра
  int mask = numberSegments[number];
  // для каждого из 7 сегментов индикатора
  for (int i = 0; i < SEGMENT_COUNT; ++i) {
    // определяем: должен ли он быть включён.
    boolean enableSegment = bitRead(mask, i);
    // включаем/выключаем сегмент на основе полученного значения
    digitalWrite(i + FIRST_SEGMENT_PIN, enableSegment);
  }
}

Запустив пример, мы получим реальный секундомер. За точность не ручаюсь, но для простых задач подойдёт.

На видео некоторые цифры отображаются коряво, видимо из-за особенностей записи. В реальности все цифры работают как положено.

Позже я добавил на плату ещё один светодиод, который загорался при значении 0. При других значениях он был выключен.


// Перед циклом for
if(number == 0) {
    digitalWrite(ledPin, HIGH);
}else{
    digitalWrite(ledPin, LOW); 
}

На Амперке есть упоминания о двух компонентах, которые можно использовать для светодиодного индикатора. Я пока ими не пользовался:

http://amperka.ru/product/74hc595-shift-out-register
http://amperka.ru/product/cd4026-segment-driver

Четырёхразрядный 7-сегментный индикатор

4 segment

У четырёхразрядного 7-сегментного индикатора двенадцать выводов: 8 для каждого разряда с точкой и 4 для выбора нужного разряда. Чтобы разобраться в подключении, желательно иметь картинку перед глазами.

Если индикатор держать точкой вниз, то отсчёт идёт против часовой стрелки от нижнего левого угла.


Pin        12 11 10 9  8  7
           |  |  |  |  |  |
Resistor   |  R  R  |  |  R
           |  |  |  |  |  |
         +------------------+
Display  |    8. 8. 8. 8.   |
         +------------------+
           |  |  |  |  |  |
Resistor   R  R  R  R  R  |
           |  |  |  |  |  |
Pin        1  2  3  4  5  6

4 segment

Выводы 6, 8, 9 и 12 отвечают за конкретные разряды. Это могут быть общие катоды или общие аноды.

Выводы 1, 2, 3, 4, 5, 7, 10, 11 отвечают за конкретные сегменты. Например, самая верхняя полоска обозначена буквой A и имеет номер вывода 11. Таким образом, если подключить выводы 11 и 12 индикатора к выводу 11 и 12 на плате Arduino, то можем управлять этой полоской стандартным способом, например, помигать светодиодом.

4 segment

Соедините все двенадцать выводов индикатора с выводами на плате. В своих примерах я использовал следующую схему.


int commonPins[] = { 2, 3, 4, 5 };
int segmentsPins[] = { 6, 7, 8, 9, 10, 11, 12, 13 }; //{A, B, C, D, E, F, G, DP} - распиновка сегментов

При необходимости используйте резисторы, хотя во многих примерах в интернете встречал схемы без них.

Включаем букву H на всех разрядах.


int commonPins[] = { 2, 3, 4, 5 };
int segmentsPins[] = { 6, 7, 8, 9, 10, 11, 12, 13 }; //{A, B, C, D, E, F, G, DP} - распиновка сегментов

void setup() {
	for (int i = 0; i < 4; i++) {
		pinMode(commonPins[i], OUTPUT);
	}
	for (int i = 0; i < 8; i++) {
		pinMode(segmentsPins[i], OUTPUT);
	}
}

int seg[] = { 1, 0, 0, 1, 0, 0, 0, 1 }; //Буква H

void loop() {
	// отображаем букву H на всех разрядах
	for (int i = 0; i < 4; i++) {
		for (int k = 0; k < 8; k++) {
			digitalWrite(segmentsPins[k], ((seg[k] == 1) ? LOW : HIGH));
		}
		digitalWrite(commonPins[i], HIGH);
		delay(1);
		digitalWrite(commonPins[i], LOW);
	}
}

Работа с индикатором показалась мне слишком муторной и сложной. Дополнительные материалы можно посмотреть на сайте, с которого я взял часть примеров.

Библиотека fDigitsSegtPin

К счастью есть библиотека fDigitsSegtPin, которую можно установить через менеджер библиотек. Подключаем библиотеку, указываем все двенадцать выводов по порядку и выводим нужное число.


#include <fDigitsSegtPin.h>

fDigitsSegtPin Display(10, 9, 13, 8, 12, 3, 7, 4, 5, 11, 6, 2);

void setup() {
    Display.begin();
    Display.doPrint_lastDot = 1;
    Display.doPrint_firstZero = 1;
    Display.doReport_overRange = 0;
}

void loop() {
    Display.print(1234);
}
Реклама