Ардуино: динамическая индикация

В одном из предыдущих уроков мы научились зажигать сегменты светодиодного индикатора. Хотя это можно сделать только с помощью Ардуино, мы использовали в нашем уроке дополнительный компонент — сдвиговый регистр. Эта полезная микросхема сэкономила нам несколько ценных выводов микроконтроллера.

Но один семисегментный индикатор почти бесполезен. Он ведь отображает всего одну цифру. А что если мы хотим вывести большое число, скажем, от 0 до 500? Нам потребуется целых три цифры, а значит и три индикатора.

Как будем подключать их к контроллеру? Можем напрямую, но тогда мы займем 7*3 = 21 вывод! Это очень расточительно. К тому же, нужна будет уже другая плата, так как у Ардуино Уно просто не хватит цифровых выводов.

Попробуем использовать сдвиговый регистр? Уже лучше. Теперь нам понадобится три регистра, объединенных в цепочку, а также три вывода Ардуино для управления ими. В общем то на этом можно бы было и остановить оптимизацию нашей схемы, но мы пойдем дальше. Обойдемся всего одним сдвиговым регистром!

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

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

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

Динамическая индикация

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

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

Подключение к Ардуино

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

Кстати, вместо трёх отдельных индикаторов лучше применять один трехцифровой индикатор. Суть схемы от этого не изменится, но проводов будет меньше!

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

Принципиальная схема для динамической индикации на Ардуино

Внешний вид макета

Макетная схема для динамической индикации на Ардуино

В качестве электронных ключей рекомендуем использовать транзисторы в корпусе TO92, например 2N7000. Для подключения каждого транзистора понадобится два резистора: на 100-150 Ом и на 2.2-10 кОм. Первый резистор призван защитить вывод контроллера от бросков тока, возникающих на затворе во время создания поля. Второй же резистор поможет быстро выключить ключ, когда мы подадим низкий уровень на соответствующий вывод контроллера (через него на землю сольется остаточный заряд затвора).

На каждой линии от регистра к индикатору необходим токозадающий резистор 200-300 Ом, чтобы светодиоды в индикаторе не перегорели. Этот нюанс работы со светодиодами мы рассмотрели на одном из самых первых уроков про светодиоды.

Тщательно собираем схему и переходим к программе.

Программа для динамической индикации

const byte digit_pins[3] = {5,6,7};
const byte data_pin = 2;
const byte sh_pin = 4;
const byte st_pin = 3;

unsigned long tm, next_flick;
const unsigned int to_flick = 1;

byte digit = 0;
unsigned int counter = 125;

const byte digits[10] = {
    B11101110, 
    B10000010, 
    B11011100, 
    B11010110, 
    B10110010, 
    B01110110, 
    B01111110, 
    B11000010, 
    B11111110, 
    B11110110 
};

void fill( byte d ){
    for(char i=0; i<8; i++){
        digitalWrite(sh_pin, LOW);
        digitalWrite(data_pin, digits[d] & (1<<i));
        digitalWrite(sh_pin, HIGH);
    }
    digitalWrite(st_pin, HIGH); 
    digitalWrite(st_pin, LOW);
}

void setDigit( byte digit, unsigned int counter ){
    byte d = 0;
    switch ( digit ){
        case 0:
            digitalWrite(digit_pins[2], LOW);
            d = counter % 10;
            fill(d);
            digitalWrite(digit_pins[0], HIGH);
            break;
        case 1:
            digitalWrite(digit_pins[0], LOW);
            d = (counter % 100) / 10;
            fill(d);
            digitalWrite(digit_pins[1], HIGH);
            break;
        case 2:
            digitalWrite(digit_pins[1], LOW);
            d = ( counter % 1000 ) / 100;
            fill(d);
            digitalWrite(digit_pins[2], HIGH);
            break;
    }
}

void setup() {
    for(int i=0; i<3; i++){
        pinMode(digit_pins[i], OUTPUT);
    }
    pinMode(data_pin, OUTPUT);
    pinMode(sh_pin, OUTPUT);
    pinMode(st_pin, OUTPUT);
    pinMode(13, OUTPUT);
}

void loop() {
    tm = millis();
    if( tm > next_flick ){
        next_flick = tm + to_flick;
        digit++;
        if( digit == 3 )
            digit = 0;
        setDigit( digit, counter );
    }
}

Часть этой программы, включая переменные data_pin, sh_pin, st_pin и функцию fill уже известны нам из урока про сдвиговый регистр.

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

Чтобы переключать индикаторы воспользуемся таймером. Будем каждую миллисекунду заполнять регистр нужной комбинацией и включать соответствующий индикатор. Для этого мы используем функцию setDigit. Аргумент digit — это индекс включаемого в данный момент индикатора, а counter — трехзначное число, которые мы хотим высветить.

Параметр to_flick отвечает за период переключения индикатора. Он равен 1, а значит смена цифр для отображения происходит каждую миллисекунду. Что если увеличить этот параметр? Скажем до 100мс, или даже до 500мс. Эффект инерции зрения пропадет и мы начнем замечать смену цифр.

Динамическая индикация на Ардуино

Программа счетчика с динамической индикацией

В предыдущем примере переменная counter хранила число 125 по-умолчанию. Попробуем теперь добавить в программу счетчик секунд, чтобы counter увеличивался на единицу каждую секунду, вплоть до числа 999.

const byte digit_pins[3] = {5,6,7};
const byte data_pin = 2;
const byte sh_pin = 4;
const byte st_pin = 3;

unsigned long tm, next_sec, next_flick;
const unsigned int to_sec = 1000;
const unsigned int to_flick = 1;

unsigned int counter = 0;
byte digit = 0;

const byte digits[10] = {
    B11101110, 
    B10000010, 
    B11011100, 
    B11010110, 
    B10110010, 
    B01110110, 
    B01111110, 
    B11000010, 
    B11111110, 
    B11110110 
};

void fill( byte d ){
    for(char i=0; i<8; i++){
        digitalWrite(sh_pin, LOW);
        digitalWrite(data_pin, digits[d] & (1<<i));
        digitalWrite(sh_pin, HIGH);
    }
    digitalWrite(st_pin, HIGH); 
    digitalWrite(st_pin, LOW);
}

void setDigit( byte digit, unsigned int counter ){
    byte d = 0;
    switch ( digit ){
        case 0:
            digitalWrite(digit_pins[2], LOW);
            d = counter % 10;
            fill(d);
            digitalWrite(digit_pins[0], HIGH);
            break;
        case 1:
            digitalWrite(digit_pins[0], LOW);
            d = (counter % 100) / 10;
            fill(d);
            digitalWrite(digit_pins[1], HIGH);
            break;
        case 2:
            digitalWrite(digit_pins[1], LOW);
            d = ( counter % 1000 ) / 100;
            fill(d);
            digitalWrite(digit_pins[2], HIGH);
            break;
    }
}

void setup() {
    for(int i=0; i<3; i++){
        pinMode(digit_pins[i], OUTPUT);
    }
    pinMode(data_pin, OUTPUT);
    pinMode(sh_pin, OUTPUT);
    pinMode(st_pin, OUTPUT);

   pinMode(13, OUTPUT);
}

void loop() {
    tm = millis();
    if( tm > next_sec ){
        next_sec = tm + to_sec;
        counter++;
        if( counter == 1000 )
            counter = 0;
    }

    tm = millis();
    if( tm > next_flick ){
        next_flick = tm + to_flick;
        digit++;
        if( digit == 3 )
            digit = 0;
        setDigit( digit, counter );
    }
}

Загружаем программу на Ардуино и наблюдаем работу счетчика!

Задания

  1. Цифровой секундомер. Собрать схему с трехцифровым индикатором. Добавить в схему кнопку. При нажатии на кнопку, секундомер должен запускать отсчет. При повторном нажатии — останавливать. Дополнительно, к секундомеру можно добавить дробную часть, отображаемую на третьем индикаторе через точку.
  2. Цифровой вольтметр для напряжений от 0 до 10 Вольт. Собрать схему с трехцифровым индикатором. Добавить в схему делитель напряжения из двух резисторов на 10 кОм, подключенный к аналоговому входу Ардуино. Написать программу, которая будет каждые 100 мс считывать значение на аналоговом входе, переводить его в Вольты и выводить на индикатор. Для правильного отображения дробной части, необходимо подключить восьмой сегмент — точку.

Заключение

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

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


Изменено:

Ардуино: динамическая индикация: 17 комментариев

  1. Добрый день!
    Ведь нам всё равно, куда вешать токоограничивающий резистор (на катод или на анод)? И получается, что можно повесить по одному резистору на каждый общий катод (итого 3 шт.), вместо — по одному резистору на каждый запараллеленный анод (итого 7 шт., как на схеме). Или так не получится?

    • В данной схеме в один момент времени горит одна цифра. В цифре одновременно могут гореть сразу несколько сегментов, даже все семь. Соответственно, на каждый сегмент надо по резистору.

  2. Добрый день!
    Собрал вашу схему, правда на NPN-биполярниках PN2222, залил ваш скетч — всё заработало сразу!
    Стал разбираться с программой. Есть два момента:
    1. В void setup() есть строка pinMode(13, OUTPUT); — она не нужна;
    2. При определении цифры, которая стоит в разряде сотен индицируемого числа формулу d = ( counter % 1000 ) / 100; можно смело заменить на d = counter /100; т.к. наше индицируемое число нами же и ограничено пределами от 0 до 999.

  3. Можно ли использовать второй сдвиговый регистр как группу электронных ключей для динамической индикации? То-есть первый 8-битный регистр выдает символ, второй в этот момент определяет, к какому из восьми индикаторов подключить питание?

    • Можно. Только на выходе второго регистра нужно поставить транзисторы, которые будут коммутировать общие катоды индикаторов.

    • Нужно в массив цифр добавить еще и код знака «минус». Затем переделать функцию setDigit, а именно, добавить там проверку на отрицательный знак. Если переменная counter меньше нуля, то в самом первом знаке выводить тот самый «минус».

  4. Доброе утро. Подскажите как вывести цифры на 7 гементное табло сделанное из led ленты, через uln2003?

  5. Добрый день подскажите что ды подключить индикатор с общим анодом и без транзисторов надо переписать массив и ещё что?

  6. Здравствуйте! Прошу, опубликуйте пожалуйста ответ к первой задаче с секундомером! Необходимо свериться с вашей программой! Заранее спасибо!

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

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.