Программирование работы многоразрядного семисегментного индикатора
Подключение многоразрядного семисегментного индикатора
Организация динамической индикации
Алгоритм работы программы
Программа индикации на многоразрядном семисегментном индикаторе
Во второй части статьи о семисегментных индикаторах мы рассмотрели вопросы подключения одноразрядного индикатора к микроконтроллеру, перевели десятичные цифры в коды семисегментного индикатора, создали программу для ввода данных на индикатор. Сегодня мы узнаем как подключить к микроконтроллеру многоразрядный семисегментный индикатор, организовать динамическую индикацию и напишем программу для вывода информации на многоразрядный индикатор
Подключение многоразрядного семисегментного индикатора
Давайте еще раз посмотрим схему подключения многоразрядного семисегментного индикатора к микроконтроллеру:
На этой схеме выводы порта РВ (РВ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: Перевод двоичного кода десятичного числа в код семисегментного индикатора. Программа вывода цифры на одноразрядный светодиодный индикатор.
Здравствуйте. А без прерываний многоразрядную индикацию никак не организовать?
Добрый вечер. Можно, посмотрите эту статью - счетчик 0-99 на микроконтроллере AVR ATmega32.
Дело в том, что индикация есть, но на движок потенциометра не реагирует. Подключал вместо него делитель из резисторов, показания никак не меняются.
А я уже сам разобрался: сделал свой вариант, но ваша статья помогла структурировать программу) Спасибо!
Я рад что у вас получилось, я был в отпуске, не мог оперативно вам ответить
Выставили сегменты в разряде 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.