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

Программирование работы многоразрядного семисегментного индикатора

Подключение многоразрядного семисегментного индикатора
Организация динамической индикации
Алгоритм работы программы
Программа индикации на многоразрядном семисегментном индикаторе

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

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

Подключение многоразрядного семисегментного индикатора

Давайте еще раз посмотрим схему подключения многоразрядного семисегментного индикатора к микроконтроллеру:

На этой схеме выводы порта РВ (РВ0 — РВ7) микроконтроллера через токоограничительные резисторы подключены к соответствующим сегментам (a-g) многоразрядного семисегментного индикатора. Соответствующие сегменты всех разрядов индикатора соединены параллельно. Катоды (аноды) каждого разряда индикатора подключены через транзисторы к выводам порта PD.


 Организация динамической индикации

Работа многоразрядного индикатора осуществляется следующим образом:

1. На управляющий транзистор первого разряда индикатора (7Seg1), с вывода порта микроконтроллера PD0 подается логическая единица, которая открывает транзистор, в результате чего подается напряжение питания на данный разряд индикатора. На базах остальных транзисторов — логический ноль, транзисторы закрыты.
2. На выводах порта РВ0-РВ7 выставляется двоичный код соответствующей десятичной цифры — высвечивается нужная цифра в первом разряде.
3. На управляющий транзистор второго разряда (7Seg2) с вывода порта PD1 подается логическая единица (на остальные транзисторы — логический ноль) — подается питание на второй разряд индикатора.
4. На выводах порта РВ0-РВ7 выставляется двоичный код следующей (второй) десятичной цифры — высвечивается нужная цифра во втором разряде.
5. На управляющий транзистор третьего разряда (7Seg3) с вывода порта PD2 подается логическая единица (на остальные транзисторы — логический ноль) — подается питание на третий разряд индикатора.
6. На выводах порта РВ0-РВ7 выставляется двоичный код следующей (третьей) десятичной цифры — высвечивается нужная цифра во втором разряде.
7. И так, по кругу

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

Для переключения разрядов можно задействовать (на примере микроконтроллера ATtiny2313) таймер «TIMER 0«.
Настройка таймера производится следующим образом (при тактовой частоте 1 мГц — заводская установка):
— предделитель таймера устанавливаем в СК/8
— вызов прерывания по переполнению счетчика таймера

Вот так настройка таймера выглядит в программе:
Где:
SP — настройка стека
Timer 0 — настройка параметров таймера
TIMSK — настройка прерывания


Алгоритм работы программы

Рассмотрим алгоритм программы для осуществления динамической индикации и вывода данных в многоразрядный семисегментный индикатор:

Этот алгоритм, в принципе, иллюстрирует организацию динамической индикации данных на многоразрядном индикаторе. При этом надо учитывать, что при первом прерывании выполняется первый «прямоугольник», а затем происходит выход из подпрограммы, при втором прерывании выполняется второй «прямоугольник» и тоже выход из подпрограммы и при третьем прерывании — нижний «прямоугольник» с выходом из подпрограммы, и далее по кругу.

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


Программа индикации на многоразрядном семисегментном индикаторе

Как я уже писал в другой статье — АЛГОРИТМ — предшественник программы, и чем продуманнее он будет написан, тем легче будет писать программу.

Назначение переменных:

Давайте посмотрим какие переменные для работы подпрограммы вывода данных на индикатор назначил я:

Data0, Data1 и  Data2 — переменные, в которые основная программа записывает вычисленное значение (трехзначное)
Data — переменная, в которой записан адрес первой переменной данных — Data0
@Data — эта запись означает, что в переменной Data будет храниться адрес первой переменной данных — Data0
DataIndex — эта переменная хранит текущий номер переменной данных, которая выводилась на индикацию последней (0, 1 или 2, соответственно для Data0, Data1 или Data2)
PortDigits — эта переменная хранит данные о том, какой разряд индикатора зажигался последним

Настройка стека:

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

Настройка восьмиразрядного таймера Taimer0:

Taimer0 в подпрограмме используется как средство обеспечивающее динамическую индикацию разрядов индикатора

Настроенный таймер через определенные промежутки времени вызывает прерывание, в результате чего происходит остановка основной программы и осуществляется переход в подпрограмму обработки прерывания. В нашем случае — вызывается подпрограмма вывода данных на индикатор.
Как настраивается таймер: Частота переключения разрядов должна быть в пределах 100 Гц  для предотвращения мерцания индикаторов при их поочередном зажигании (дело это в принципе индивидуальное, и зависит от особенностей вашего зрения).
Тактовая частота микроконтроллера — 1 мГц, или 1 000 000 Гц
Устанавливаем внутренний делитель частоты таймера в СК/8 — рабочая частота таймера будет в 8 раз меньше тактовой частоты микроконтроллера
Получаем: 1000 000/8 = 125 000 Гц, или 125 кГц — тактовая частота таймера
Настраиваем вызов прерывания по переполнению счетчика таймера (счетчик таймера восьмиразрядный и считает до 255, после этого сбрасывается в ноль и вызывается прерывание)
Получаем: 125 000/255 = 490 Гц (что соответствует времени приблизительно в 2 миллисекунды)
Мы поочередно зажигаем три разряда:
Получаем: 490/3 = 163 Гц — разряды индикатора будут переключаться с частотой 163 Гц.
Настройка таймера производится соответствующей настройкой соответствующих регистров таймера.
Давайте посмотрим как это происходит в Algorithm Builder:

Инициализация индикатора

Инициализация индикатора — эта фраза подразумевает настройку разрядов портов, к которым подключены выводы индикатора на вывод, а также обнуление переменных данных Data0…2 и запись первоначальных данных в остальные переменные. Процесс инициализации индикатора прописывается в начале основной программы.
Назовем подпрограмму инициализации Ini_Indikator2/
Давайте посмотрим этот процесс на примере:


В первой строке разряды порта РВ с 0 по 6 (к которым подключены семь сегментов индикатора) настраиваются на вывод информации (десятичную точку индикатора не используем).
Во второй строке разряды порта PD с 0 по 2 (к которым подключены управляющие транзисторы) также настраиваются на вывод.
Третьей строкой на выходах порта РВ устанавливается логический ноль — сегменты индикатора погашены для индикаторов с общим катодом).
Четвертая строка — обнуляем переменную DataIndex
Пятая строка — в переменную PortDigits записываем единицу
Следующие три строки — обнуляем переменные данных

Теперь нам необходимо куда-то записать двоичные коды цифр которые будут подаваться на разряды порта PB для высвечивания соответствующей цифры на индикаторе.
В статье по программированию работы одноразрядного семисегментного индикатора, мы эти коды записывали программным путем в ОЗУ микроконтроллера. Сейчас мы сделаем по-другому — запишем двоичные коды в теле самой программы.
Для этого создадим таблицу двоичных кодов и присвоим ей имя, к примеру D0_9:

В этой таблице размещены двоичные коды (хотя и записаны в шестнадцатиричной системе) цифр от 0 до 9.

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


Подпрограмма вывода данных на многоразрядный семисегментный индикатор

Присвоим подпрограмме имя, к примеру Indikator2, посмотрим на нее и разберем построчно:

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

В переменной DataIndex храниться номер ячейки памяти (0, 1 или 2) с данными (Data0, Data1 или Data2) которые необходимо вывести на разряд индикатора в текущий момент. Первоначально мы записали в нее ноль.
Первой строкой мы записываем содержимое DataIndex в регистр R20, теперь в нем соответственно то-же ноль.
Во второй строчке мы увеличиваем содержимое регистра R20 на единицу (r20++), теперь в R20 записана единица, означающая, что данные мы будем брать из переменной Data1. При втором прерывании R20 увеличится еще на единицу, станет равным 2, и соответственно следующие данные мы будем брать из переменной Data2. При следующем прерывании R20 станет равным 3.
Следующей строчкой (r20<3) мы проверяем какая цифра записана в регистре R20 — если меньше трех (0,1 или 2), то переходим по стрелке, а если равно трем, то обнуляем регистр R20 и данные теперь берем из переменной Data0.
Далее записываем содержимое R20 в переменную DataIndex.
Следующей командой @Data -> Y записываем адрес переменной Data0 в двойной регистр Y (R28, R29).
Затем складываем содержимое двойного регистра Y с содержимым R20 (0,1 или 2).
Командой [Y] -> r21 записываем содержимое переменной данных (или Data0, или Data1, или Data2 — в зависимости от значения r20) в рабочий регистр R21. Теперь в регистре R21 записана цифра из соответствующей переменной данных (к примеру цифра 5).
Следующей командой @D0_9*2 -> Z мы загружаем начальный адрес таблицы с двоичными кодами в двойной регистр Z (R30, R31). По начальному адресу у нас находится двоичный код для цифры 0.
Теперь мы складываем содержимое Z с R21 (с пятеркой) и получаем в регистре Z адрес в таблице двоичного кода с цифрой 5.
Следующей командой LPM[Z] -> R21 мы записываем двоичный код цифры 5 в рабочий регистр R21.
Команду NOP — холостой ход, можно и не прописывать — она вставлена для разделения отдельных кусков программы для наглядности.
Следующей командой PortDidgit -> R20 мы загружаем в рабочий регистр R20 содержимое переменной PortDidgit, а в нее мы предварительно записали единицу. Теперь в R20 записана единица (#b 0000 0001).
Следующей командой <<R20 мы производим сдвиг содержимого регистра R20 на один разряд влево (получаем #b 0000 0010).
Следующей командой R20 -> PortD мы подаем напряжение на второй разряд индикатора. При следующем прерывании произойдет еще один сдвиг влево (#b 0000 0100) и будет подключен третий разряд индикатора.
С помощью команды R20.3=1 записанной в овале, мы проверяем — достигла ли логическая единица при сдвигах третьего разряда регистра, и если — да, то записываем в R20 единицу (начинается новый круг).
Командой R21 -> PortB мы выводим двоичный код соответствующей цифры на подключенный разряд индикатора.
Командой R20 -> PortDigits — мы сохраняем текущее значение в переменной (последний зажженный разряд индикатора).

Вот так полностью выглядит подпрограмма вывода данных на семисегментный индикатор с динамической индикацией и первоначальными настройками:

Вот, в принципе, и все. Если что-то не очень понятно, или совсем непонятно, пишите, отвечу на все вопросы.


Предыдущие статьи:
Часть 1: Семисегментный светодиодный индикатор: описание, подключение к микроконтроллеру
Часть 2: Перевод двоичного кода десятичного числа в код семисегментного индикатора. Программа вывода цифры на одноразрядный светодиодный индикатор.


Примечание: к этой записи прикреплена форма для оценки. Чтобы оценить её, зайдите на сайт.
Подключение многоразрядного семисегментного индикатора, организация динамической индикации, алгоритм работы программы, программа индикации на многоразрядном семисегментном индикаторе
Published by: Мир микроконтроллеров
Date Published: 04/20/2015

11 ответов к “Многоразрядный семисегментный индикатор”

Здравствуйте. А без прерываний многоразрядную индикацию никак не организовать?

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

А я уже сам разобрался: сделал свой вариант, но ваша статья помогла структурировать программу) Спасибо!

Я рад что у вас получилось, я был в отпуске, не мог оперативно вам ответить

Выставили сегменты в разряде 1. Подождали;
Переключили на разряд 2 (но горят сегменты разряда 1!);
Выставили сегменты в разряде 2;

Хоть сегменты «не того разряда» горят и долю секунды, но все равно неприятно выглядит такое мерцание. Это дилетанство. Нужно перед переключением гасить все сегменты полностью.

Здравствуйте Макс!
Можно, конечно, и гасить сегменты перед переходом на новый разряд.
Но когда устанавливать нужные сегменты? Получается — после перехода к новому разряду.
Следуя Вашей логике получается следующее:
— высвечиваем 1-й разряд
— в конце гасим сегменты
— переходим ко 2-му разряду
— зажигаем нужные сегменты
И что получается? В два раза хуже — два временных интервала будут с погашенными сегментами — до перехода к новому разряду и после перехода до установки нужных сегментов
Оптимальный вариант — устанавливать нужные сегменты после перехода к новому разряду.
Кстати, если устанавливать сегменты (не гася их) до перехода к новому разряду — будет заметно мерцание.
С уважением, Admin.

Благодарю, добрые человеки!! Эту тему и дс18б20 ещё не освоил, но с такими доходчиво разъяснениями — думаю дело стронется!
Будьте Здравы!

Добрый день!
Спасибо за объяснения.В опциях проекта — компилятор — регистр Z установлен как двойной регистр. В мануле в разделе непосредственное размещение данных в памяти программ приведен пример. Согласно примера строка @D0_9*2 ->z ,представлена как
D0_9*2 -> z. Если убрать значок «@»,ошибки не выдает. В вашем примере этот значок присутствует.Может это опечатка? Спасибо.

Добрый день! В принципе алгоритм понятен.Только при компилировании в строке @D0_9*2в Z, выдает ошибку,неизвестное имя @D0_9. Подскажите пожалуйста,что я делаю не так.Спасибо.

Доброго дня Александр!
Я тоже когда-то нарвался на такую штуку и долго мучился, пока внимательно не прочел инструкцию.
Дело в том, что «Z» имеет два назначения:
1. Двойной регистр Z
2. Флаг Z регистра SREG
По умолчанию в АБ Z подразумевается как флаг регистра SREG (поэтому и выдается ошибка).
Есть два пути устранения ошибки:
1. В меню ОПЦИИ -> ОПЦИИ ПРОЕКТА -> КОМПИЛЯТОР поменять «Интерпритацию шаблона»
2. Выделить Z жирным шрифтом нажав клавишу F2, и тогда Z будет интерпритироваться как двойной регистр Z.
С уважением, Admin.

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

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