ГЛАВНАЯ Визы Виза в Грецию Виза в Грецию для россиян в 2016 году: нужна ли, как сделать

Динамическая индикация на avr. Семисегментный индикатор. Программирование работы многоразрядного семисегментного индикатора

В продолжение урока, рассмотрим динамическую индикацию. Если вы внимательно изучили статическую индикацию, то знаете, что сегментный индикатор это набор светодиодов. Для того, чтобы подключить индикатор, нужно 7 ножек микроконтроллера. Но, вдруг нам понадобилось использовать несколько индикаторов, например 2, 3, 4…

Тогда нам понадобится уже 14, 21, 28 ножек, а ножек итак мало… Тут нам на помощь приходит динамическая индикация. Основная задача динамической индикации — снизить количество используемых ножек микроконтроллера. Обратите внимание на схеме задействовано 9, а не 14 ножек. Ножки управления все подключены параллельно.

В общем смысле работает данная конструкция следующим образом: вначале выводится конфигурация первого числа на общую шину и включаем PB1. Первый индикатор загорается, с нужным числом. Затем его гасим, выводим конфигурацию второго числа на шину данных, зажигаем второй индикатор, выключаем.

Более детально. В первый момент времени все выключено PORTB=0x00; PORTD=0xFF; так как схема с общим «+», анодом. Далее на PORTD посылается конфигурация первого числа, например «0». Из статической индикации мы помним:

case 0 : { PORTD= 0xC0 ; break ; }

case 0: { PORTD=0xC0; break; }

Но обратите внимание, «+» подключен к PORTB.1, т.е. чтобы зажечь сегмент нужно включить ножку PORTB.1=1;

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

При высоких частотах, человеческий глаз не способен разглядеть эти переключения, и кажется что индикатор горит постоянно. Рекомендуется не использовать частоты кратные 50Гц. В своем тестовом проекте я использовал 120Гц. Таймер настроен на частоту 1МГц. Код обрабатывается в прерывании таймера1. Прерывание вызывается 240 раз в секунду, потому что индикаторов два, поэтому 1000 000/240=4166 или 0x1046 пихаем в регистр сравнения. Протеус подружить с динамическим индикатором не удалось, зато на железе заработало сразу.

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

Для схемы с общим анодом

Для схемы с общим катодом

В качестве тестовой прошивки использовал таймер из предыдущего проекта.

Видео работы прошивки

Обновлено 3.04.15. Всем привет. В прошлой статье мы с Вами рассмотрели алгоритм общения с ЖКИ, а также вывод информациина нее, и протестировали в симуляторе. В этой записи я кратенько расскажу о “недорогом” способе вывода информации — это семисегментный индикатор , который является наиболее простым из индикаторов, для отображения арабских цифр, а также некоторых символов, которые возможно на нем вывести. Также рассмотрим программу на Си для AVR, и подключение в железе и симуляторе.

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

Если индикаторов несколько, следовательно и управляются катоды несколькими ножками МК. !!! Но всегда используйте транзисторы, т.к. порты ввода-вывода могут сгореть из-за относительно большого тока. Я использовал обычные 315 транзисторы. На рисунке ниже я отобразил примерное подключение, через них, управляющего вывода индикатора и контроллера. Для монтажа нам потребуется 11 ножек микроконтроллера, т.е. для отображения информации на сегментах 8 ножек (7 +точка) и по одной ножки на каждый индикатор для его управления, у меня их три, поэтому и ножки управления тоже три. Ниже я привел и описал программу. Для управления сегментами будем использовать пины одного порта, что б не путаться. Писал под микроконтроллер ATmega8 . Если Вы хотите отвязаться от “камня” то это не проблема, например в , где без проблем можно изменить настройки под другой “камень”, в основном это номера пинов и портов. Там же описаны общие правила универсальности и переноса.

Рисунок подключения транзистора к МК и индикатору.

Перейдем к программе. В этой небольшой программе (на Си ) я привел пример включения трех элементов индикатора и вывод числа с запятой. Использование таймера и прерывания для вывода на индикатор. В ходе написания программы мы должны определиться какой пин порта должен соответствовать сегменту на индикаторе. Сам элемент индикатора приведен на рисунке ниже. Сбоку описание подключения выводов к сегментам элемента индикатора(пин порта – номер ножки элемента (рис. выше) – буква сегмента – число в массиве, отвечающее за включение сегментов на элементе).

PB0 — 12 — управление первым элементом

PB6 — 9 — управление вторым элементом
PB7 — 8 — управление третьим элементом
PD7 – 11 — (A) — 128
PD6 – 10 — (F) — 64
PD5 – 7 — (B) — 32
PD4 – 5 — (G) — 16
PD3 – 4 — — 8
PD2 – 3 — (DP) — 4
PD1 – 2 — (D) — 2
PD0 – 1 — (E) — 1

#include
#include
#include
/*Определим каждому пину порта элемент семи сегментника (риунок выше)*/
#define a 128
#define b 32
#define c 8
#define d 2
#define e 1
#define f 64
#define g 16
#define dp 4
/*Эти макросы содержат числа, соответствующие двойке, возведенной в степень, равной номеру "ножки" того порта, к которому подключен сегмент индикатора с одноименным макросу названием. */
short unsigned int j, k = 0; /*переменные исп-ся в макросе прерывания*/
float i = 0; /*Переменная для вывода на индикатор*/
short unsigned int w = 0; /*Переменная индикатор для включения точки*/
unsigned char Slot; /*Массив в котором хранятся числа, которые нужно
вывести через порт на индикатор, чтобы он показал цифру, равную номеру
элемента массива. Числа зависят только от макросов.*/
void Slot_init () /*Функция инициализации индикатора*/
{
Slot = (a+b+c+d+e+f);
Slot = (b+c);
Slot = (a+b+g+e+d);
Slot = (a+b+g+c+d); .
Slot = (f+g+b+c); /*Имена макросов соответствуют именам сегментов индик*/
Slot = (a+f+g+c+d);
Slot = (a+f+g+c+d+e);
Slot = (a+b+c);
Slot = (a+b+c+d+e+f+g);
Slot = (a+b+c+d+f+g);
Slot = dp; /*Точка*/
}
/*В этих переменных хранятся цифры, которые нужно отобразить*/
char Elem1, Elem2, Elem3;
/* Функция выделяет цифры из трехзначного числа Number*/
void Display (float Number)
{
float N1, N2; /*Переменные для функции modf*/
N1 = modf (Number, &N2); /*Разбиваем число на целую и дробную части, N1 = дробной N2 = целой*/
if (N1 != 0) /*Еслине равно нулю то присутствует дробь*/
{
Number= Number*10; /*тогда умножаем число на 10, для обычного вывода на индикатор трехзначного дробного числа*/
w = 1; /* переменная индикатор которая используется в цикле ниже, чтобы включать точку*/
}
short unsigned int Num1, Num2, Num3;
Num1=Num2=0;
while (Number >= 100) /*Сотни*/
{
Number -= 100;
Num1++;
}
while (Number >= 10) /*Десятки*/
{
Number -= 10;
Num2++;
}
Num3 = Number; /*Еденицы*/
Elem1 = Slot;
if (w == 1) /*Условие дя включения точки на втором элементе*/
{
Elem2 = (Slot|0×04); /*логическое сложение с пином отвечающим за точку*/
w = 0; /*Выключаем точку*/
}
else
Elem2 = Slot;
Elem3 = Slot;
}
int main (void) /*начало основой программы*/
{
DDRB = 0Xff; /*все выводы порта B сконфигурировать как выходы*/
DDRD = 0xff; /*все выводы порта D сконфигурировать как выходы*/
PORTD = 0×00; /*Устанавливаем 0*/
PORTB |= _BV (PB6);
PORTB |= _BV (PB0);
PORTB |= _BV (PB7);
Slot_init ();
sei (); /*Разрешить общее прерыввание*/
/*Инициализация таймера Т0*/
TIMSK = (1</*Флаг разрешенияя по переполнению таймера счетчика Т0*/
TCCR0 = (0</* 1000000/8 = 125000= 125000/256 = 488,28 гц */
while (1) /*Бесконечный цикл, выводим переменную на индикатор*/
{
_delay_ms (1000);
i= i+0.1;
if (i == 99.9)
i = 0.0;
Display (i);
} /*Закрывающая скобка бесконечного цикла*/
} /*Закрывающая скобка основной программы*/

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

ISR (TIMER0_OVF_vect)
{
PORTB &= 0x3e; //Очистка PB7, PB6, PB0
_for (j = 0; j<=30; j++) { } // Задержка для выключения транзистора
(k == 3) ? k = 0: k++; /*Переменная, которая отвечает за очередность загорания трехэлементного индикатора, 0,1 и 2. При определенной цифре, на определенной ножке усатнавливается 1, далее открывается транзистор и загорается сегменты индикатора, соответсвующие переменной Elemn */
switch (k)
{
case 0: PORTB |= (1 << PINB7); // Единицы
PORTD = Elem3;
break;
case 1: PORTB |= (1 << PINB6); // Десятки
PORTD = Elem2;
break;
case 2: PORTB |= (1 << PINB0); // Сотни
PORTD = Elem1;
}
}

Выше приведенная программа испытана в железе и в симуляторе. Ниже выложены рисунки соответственно. В железе я все спаял навесом, на быструю руку. Как видите три элемента индикатора соответственно три транзистора (обведено кружочком) . В симуляторе(Proteus) транзисторы нам не нужны. Так же одно существенное отличие в программе, а именно в прерывании, где происходит з адержка для выключения транзистора - в симуляторе пропишите 50 тактов. Все должно работать.

По ходу публикации постов, программа для индикатора у нас немного изменилась, а именно добавился четвертый элемент, вывод на него знака минус, символов “H” и “C", формат вывода времени и объединение всех режимов . Так что читайте, анализируйте и экспериментируйте.

Ниже исходники и проект по выше приведенному материалу.

(Скачали: 795 чел.)

На этом все. В следущей статье я опишу подключение датчиков температуры и выведем инфорацию на индикатор. До скорой встречи!

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

По количеству разрядов (цифр) динамические семисегментные индикаторы бывают одноразрядные, двухразрядные, трехразрядные, четырехразрядные и очень редко – шестиразрядные. Основное внимание мы уделим четырехразрядным семисегментным индикаторам, как наиболее применяемому типу динамической индикации. Изготовляются они с общим анодом и общим катодом. Схемы соединения светодиодов отдельных сегментов представлены на рисунках.

Как видно из рисунков, каждый разряд, называемый digit, имеет свой отдельный общий в пределах разряда вывод. Поскольку рассматривается 4-х разрядная динамическая индикация, то таких выводы четыре – digit1, digit2, digit3, digit4.

Распиновка выводов 4-х разрядного семисегментного индикатора приведена на рисунке ниже. В данном случае показан вид сверху, то есть индикатор не нужно переворачивать вверх ногами.

Как работает динамическая индикация

Теперь рассмотрим, как работает динамическая индикация с общим катодом. Например, нам необходимо отобразить число 1987. Для этого следует в первый момент времени подать высокий потенциал на аноды сегментов, образующих единицу – b и c, а на общий катод первого разряда подать низкий потенциал. Общие катоды оставшихся трех разрядов – digit2, digit3 и digit4 остаются не подключенными.

Во второй момент времени получают питания сегменты, образующие цифру 9, общий катод второго разряда подключается к минусу, а digit1 теряет питание; digit2, digit3, как и раннее – остаются не подключенными.

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

В четвертый момент времени получает питание последний индикатор и отображается цифра 7.

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

Схема подключения динамической индикации к микроконтроллеру ATmega 8

Сегменты динамической индикации будем подключать через токоограничивающие номиналом 330 Ом к выводам порта D микроконтроллера ATmega8. Выводы, отвечающие digit1, digit2, digit3, digit4 подсоединим через транзисторы n-p-n тип, например BC547 или 2n2222 к выводам порта B.

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

Для большей конкретизации действий будем применять 4-х разрядный семисегментный индикатор с общим катодом. Первым делом следует создать массив цифр от 0 до 9. Этому мы уже научились ранее, . Далее необходимо разбить 4-х значное число на четыре отдельных цифры. Например, число 1987 нужно разбить на 1, 9, 8 и 7. Затем единицу нужно отобразить в первом разряде индикатора, девятку – во втором, восьмерку – в третьем и семерку – в четвертом.

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

1987/1000 → 1

1987%1000/100 → 9

1987%100/10 → 8

1987%10 → 7

В языке С при использовании целочисленного типа данных int при выполнении деления все десятые, сотые и т. д., то есть все числа меньше единицы отбрасываются. Остаются только целые числа. Математическое округление здесь не работает, то есть 1,9 в данном случае будет 1, а не 2.

Команда «остаток от деления» обозначается знаком процента «% ». Данная команда отбрасывает все целые числа и оставляет остальную часть числа. Например, 1987%1000 → 987; 1987%100 → 87; 1987%10 → 7.

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

КОД

#define F_CPU 1000000L

#include

#include

#define CHISLO PORTD

#define RAZRIAD PORTB

unsigned int razr1 = 0, razr2 = 0, razr3 = 0, razr4 = 0;

unsigned int chisla = {

// числа от 0 до 10

0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f

};

void vse_chislo (unsigned int rabivka_chisla)

{

razr1 = rabivka_chisla/1000; // тысячи

razr2 = rabivka_chisla%1000/100; // сотни

razr3 = rabivka_chisla%100/10; // десятки

razr4 = rabivka_chisla%10; // единицы

}

int main(void)

{

DDRB = 0b00001111;

DDRD = 0b11111111;

RAZRIAD = 0b00000001; // изначально 1-й разряд

CHISLO = 0x3f; // число 0

while (1)

{

vse_chislo(1987); // отображаемое число

RAZRIAD = 0b00000001; // включаем 1-й разряд, остальные выключаем

CHISLO = chisla ; // отображаем 1-ю цифру

_delay_ms (3);

RAZRIAD = 0b00000010; // включаем 2-й разряд, остальные выключаем

CHISLO = chisla ; // отображаем 2-ю цифру

_delay_ms (3);

RAZRIAD = 0b00000100; // включаем 3-й разряд, остальные выключаем

CHISLO = chisla ; // отображаем 3-ю цифру

_delay_ms (3);

RAZRIAD = 0b00001000; // включаем 4-й разряд, остальные выключаем

CHISLO = chisla ; // отображаем 4-ю цифру

_delay_ms (3);

}

}

Улучшаем программу для работы динамической индикации

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

Для того, чтобы при каждом прерывании числа отображались последовательно в каждом разряде индикатора, добавлена переменная bc547, которая увеличивается на единицу при последующем вызове прерывания ISR (TIMER0_OVF_vect). Затем выполняется проверка значения переменной bc547 и получает питания соответствующий разряд. Когда bc547 становится больше четырех, происходит сброс в единицу.

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


Для отображения четырехразрядного числа необходимо завести одну общую переменную в которой будет лежать число которое хотим вывести (переменная W ), четыре переменные в которых будут храниться данные для каждого знака (N ) и еще четыре переменные для промежуточных преобразований (M ), чтобы не трогать главную переменную. Переменная должна соответствовать тому значению, которое будет в ней хранитс я. Так для переменной W достаточным будет тип integer , так как переменная такого типа способна хр анить значения от -32768 до +32767 (или word если не планируется использование отрицательных чисел). В переменных N будут лежать числа от 0 до 9 поэтому достаточным будет использование переменной типа byte
. А в переменных M будут находиться те же значения что и в переменной W , поэтому ставим тип integer .

Dim W As Integer
Dim N1 As Byte
Dim N2 As Byte
Dim N3 As Byte
Dim N4 As Byte
Dim M1 As Integer
Dim M2 As Integer
Dim M3 As Integer
Dim M4 As Integer


После объявления переменных настраиваем порты на выход которые будут использоваться для подключения индикатора:

DDRC = &B11111111
DDRD = &B11111111


DDRC =& B 00001111 и DDRD = & B 01111111 (четыре первых ноги порта С под аноды и шесть первых порта D под сегменты).

Затем присваиваем переменной W то значение, которое собираемся вывести на индикатор:

W = 1234

"Arial","sans-serif""> В основном цикле программы присваиваем переменным М значение переменной W , я делаю так:

M1 = W
M2 = M1
M3 = M1
M4 = M1


"Arial","sans-serif""> Это не паранойя)), это сделано с той целью, чтобы в во всех переменных М лежало одно и тоже число, так как во время операции присваивания легко может ворваться прерывание (если такое имеется и не отключено), в обработчике которого переменная W может измениться. И в случае если присваивание шло таким образом: М1= W , M 2= W , M 3= W , M 4= W в переменных М будут лежать разные значения что приведет к каше в показаниях.

После присвоения переменным значения начинаем работать с
каждой из них, преобразуя таким образом, чтобы в переменную N попало то значение, которое будет
отображаться на индикаторе: в переменной
N 1 должна оказаться «1», в N 2 – «2», в N 3 – «3», а в N 4 – «4».

M1 = M1 / 1000 " M1 = 1234/1000 = 1,234
N1 = Abs (m1) " N1 = Abs (1,234) = 1

Abs – функция возвращающая целое число переменной.В переменную N 1 попала единица, что собственно и требовалось.

Для присвоения двойки переменной N 2 операция будет немного сложнее:

M2= M2 Mod 1000 " M2 =1234 Mod 1000 = 234
M2 = M2 / 100 " M2 = 234 / 100 = 2,34
N2= Abs (m2) " N2 = Abs (2,34) = 2

"Arial","sans-serif""> Для начала функцией Mod мы возвращаем переменной первые три
цифры числа (остаток от деления на 1000), а дальше все как в первом случае.

С двумя последними разрядами практически тоже самое:

M3 = M3 Mod100
M3 = M3 / 10
N3 = Abs (m3)

M4 = M4 Mod 10
N4= Abs (m4)


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

"Arial","sans-serif"">

Gosub Led

"Arial","sans-serif"">Процессор перепрыгнет на подпрограмму с меткой Led :

Led:

Portc = &B00001000

"Arial","sans-serif""> Здесь подаем высокий уровень на PORTC .3 , к этой ноге у нас подсоединен анод первого разряда. Затем выбираем, какие сегменты необходимо зажечь, чтобы отобразить значение первой переменной. Она у нас единица поэтому ноль будет на ногах Portd .1и Portd .2, что соответствует сегментам B и С индикатора.

Select Case N1









End Select
Waitms 5

"Arial","sans-serif""> После того как зажгли нужные сегменты ждем 5 мс и переходим к отображению следующих чисел:

Portc = &B00000100
Select Case N2
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select

Waitms 5

Portc = &B00000010

Select Case N3
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select

Waitms 5

Portc = &B00000001

Select Case N4
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select

Waitms 5

"Arial","sans-serif""> После отображения информации на индикаторе необходимо возвратится в основной цикл программы, где нужно завершить цикл и обозначить конец программы.

"Arial","sans-serif"">Вот что получим в итоге:

"Arial","sans-serif"">

"Arial","sans-serif""> За счет маленькой задержки переключения не будут заметны человеческому глазу и мы увидим целое число 1234.

Скачать исходник и проект в протеусе можно ниже: "Arial","sans-serif"">