Сетевое оборудование        01.03.2024   

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

Динамическая индикация - это одна из проблем, с которой сталкиваются начинающие программисты микроконтроллеров. О ней сказано многое, но я решил подкрепить известное картинками и примерами исходных текстов на Си, чтобы было проще освоить этот метод индикации.

1 Основы основ, или вступление

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

Если требуется управлять дисплеем, состоящим из единственного семисегментного знакоместа, это не вызывает никаких проблем - его можно представить, как 8 независимых светодиодов. Если требуется вывести информации побольше, чем единственный символ, начинаются проблемы: 2 знакоместа составляют 16 светодиодов, три - 24 и т.д., то есть уже для трехразрядного дисплея может элементарно не хватить выводов микроконтроллера, не говоря о 6-и или более разрядных дисплеях и, тем более, матричных индикаторах.

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

Чтобы весь дисплей воспринимался, как светящийся непрерывно, следует переключать разряды быстро - чаще 25 раз в секунду. Как видим, уровни всех выводов (которых, кстати, стало существенно меньше, чем при обычном подходе) непрерывно изменяются, т.е. имеют не статические уровни, а динамические, отсюда и название способа индикации - динамическая.

Изображение при динамической индикации

2 Разновидности аппаратной реализации

2.1 Плоские матрицы

Если абстрагироваться от семисегментных индикаторов, то наш дисплей можно представить в виде матрицы отдельных светодиодов, аноды которых объединены в строки матрицы, а катоды - в столбцы. Собственно, именно так оно и есть.

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

  • по строкам;
  • по столбцам;
  • посегментно (попиксельно);
  • смешанным образом.

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

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

2.2 Многомерные матрицы

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

Первый - это просто увеличить число строк (или столбцов) нашей матрицы, рассматривая каждый RGB-светодиод как 3 независимых отдельных светодода. Большой минус этого подхода - увеличение в 3 раза числа строк (или столбцов). На простом примере легко видно, во что это выливается практически: при помощи двух восьмиразрядных протров микроконтроллера мы можем иметь монохромную матрицу 8х8 сегментов или цветную 4х4. Согласитесь, что во втором случае ничего вразумительного отобразить практически нельзя...

Второй способ - это перейти от «плоской» матрицы сегментов к «многомерной». Если сигнал каждой строки пропустить через мультиплексор 1х3, то систему дисплея из RGB-светодиодов мы можем представить как 3 независимых матрицы исходной размерности: каждая матрица состоит из светодиодов одного цвета, а сигналами управления мультиплексорами мы выбираем нужную матрицу. Рисунок поясняет сказанное.

Очевидно, что в случае многомерной матрицы так же требуется дополнительное количество линий управления, однако, это число не так велико: на тех же двух портах контроллера мы можем получить цветной дисплей 7х7!!!

2.3 Способы уменьшения размерности матриц

Если число выводов микроконтроллера очень ограничено, нам придется искать способы уменьшить число строк и столбцов нашей матрицы. Разумеется, чудес не бывает, и в этом случае нам придется заплатить тем, что кроме микроконтроллера будут применяться дополнительные микросхемы. Как вы уже догадались, тут можно использовать ранее рассмотренный способ «многомерных» матриц - ведь никто не запретит нам вместо RGB-светодиодов просто использовать утроенное количество одноцветных? Главное, расположить их соответствующим образом...

Итак, уменьшить размерность матрицы мы можем, применив:

  • дешифраторы или мультиплексоры;
  • сдвиговые регистры.

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

Сдвиговые регистры могут помочь намного лучше дешифраторов. Рассмотрим схему на рисунке ниже.

Легко видеть, что любое количество строк и столбцов потребует только увеличения числа регистров, а число задействованных линий управления микроконтроллера останется прежним! Небольшим минусом данного подхода является то, что по мере роста числа регистров в цепочке придется увеличивать скорость последовательного вывода информации в них, что не всегда легко достижимо. Так, например, распространенные микроконтроллеры семейства AVR , практически не позволяют превзойти скорость последовательного вывода в 10 мегабит/сек. С другой стороны, если применять иные контроллеры, способные выдавать сигналы быстрее, могут начаться проблемы другого порядка: распространение высокочастотного тактового сигнала по длинной линии (а при большом числе регистров она неизбежно будет таковой) происходит совсем не так, как низкочастотного, поэтому потребуются особые меры при разводке печатной платы и другие вещи, которые в рамках данной статьи мы рассматривать не будем.

3 Способы программной реализации

Рассматривать программную реализацию всех упомянутых вариантов динамической индикации мы не будем - это необоснованно раздует статью. Мы ограничимся лишь тремя наиболее «ходовыми» примерами: плоская матрица с непосредственным управлением строками и столбцами, то же самое с применением дешифратора, и, наконец, вариант с применением сдвиговых регистров. Во всех случаях особое внимание будет уделяться всяким нюансам программной реализации, то есть код на Си будет сопровождаться пояснениями только в тех случаях, когда это совпадает с намерением автора, а отнюдь не с вашим уровнем подготовки. Этим я намекаю, что основы Си вы должны знать и без меня.

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

3.1 Простейший способ

Очевидно, что в программе было бы наиболее удобно иметь некий массив, содержимое котрого однозначно определяло бы то, какие сегменты в каких знакоместах дисплея светятся - эдакий аналог экранного ОЗУ.

Введем определение следующих констант:

#define SCR_SZ 6 /* число знакомест дисплея */ #define ROWS PORTB /* порт «строк» дисплея, т.е. управления сегментами */ #define COLS PORTD /* порт управления «столбцами», т.е. общими катодами */

Теперь объявим массив-экран:

Unsigned char SCR;

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

Как же заставить этот массив «отобразиться» на дисплее? Напишем такой код:

< SCR_SZ; pos++){ ROWS = SCR; COLS = ~(1 << pos); }

Будет ли он выполнять требуемую функцию? Да. Но плоховато.

Прежде всего, обратите внимание, что мы никак не управляем скоростью обновления содержимого строк и столбцов. Во-вторых, обратите внимание, что к моменту вывода нового элемента массива в ROWS на линиях COLS еще присутствует старое значение! К чему это приведет? Да к тому, что какую-то долю секунды на знакоместе будут отображаться сегменты соседнего знакоместа, т.е. некоторые сегменты будут ложно засвечены.

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

Unsigned char pos; while(1) for(pos = 0; pos < SCR_SZ; pos++){ COLS = 0xFF; ROWS = SCR; COLS = ~(1 << pos); delay(); }

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

Дело в том, что бесконечный цикл индикации while просто не позволит нам сделать что-то еще. Что ж за программа такая у нас выйдет, которая только и умеет, что что-то выводить на индикатор?! Конечно, все плохо не на все 100%, так как что-то полезное можно делать по прерываниям... да и вместо задержки delay() можно выполнять какие-то действия... Но все это очень и очень криво: в обработчиках прерывания не желательно выполнять что-то сложное и громоздкое; с другой стороны, если что-то сложное и громоздкое делать вместо задержки, то сложно обеспечить одинаковость времени вычислений, в противном случае получится, что знакоместа светятся в течение разного промежутка времени, что визуально будет выглядеть как разнояркое их свечение или мерцание.

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

Как же следует поступать? Ответ, как всегда, прост: все процессы, которые должны выполняться незаметно от решения основной задачи (а индикация, безусловно, является таким процессом), следует выполнять по прерываниям от таймера.
Прерывания будут поступать через строго определенные промежутки времени, что обеспечит равномерность свечения знакомест. Фоновая индикация позволит нам в главном цикле просто записать в нужный момент что-то в массив SCR - и оно мгновенно отобразится на дисплее! А все переделки кода сведутся к тому, что вместо циклов мы используем функцию-обработчик прерывания:

ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; COLS = 0xFF; ROWS = SCR; COLS = ~(1 << pos); if(++pos == SCR_SZ) pos = 0; }

Несколько комментариев.

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

В основной программе нам надо будет лишь проинициализировать порты и таймер (в данном случае - Timer 0 ), чтобы он переполнялся через нужные нам промежутки времени, да разрешить прерывания. После этого об индикации можно не вспоминать - она будет тихо-мирно работать сама по себе. Но как определить нужный интервал переполнения таймера? Очень просто. Человеческий глаз воспринимает мерцание с частотой более 25 Гц, как непрерывное свечение. Индикаторов у нас 6, каждый из них должен мерцать с такой частотой, значит, обновление информации на дисплее должно происходить с частотой 25 х 6 = 150 Гц или более. Теперь рассчитаем значение предделителя таймера: поделим тактовую частоту МК на 256 (Timer 0 у всех AVR восьмибитный, а значит, переполняется, досчитав до 256) - это и будет желаемое значение предделителя таймера. Разумеется, маловероятно, что результат получится совпадающим с одним из стандартных значений предделителя - это не проблема, можно взять ближайшиее меньшее подходящее значение. Индикация будет работать на более высокой частоте, но ведь это не ухудшит ее качество! Побочным эффектом будет большая загрузка ядра МК на индикацию. Если это будет сильно мешать основной программе - придется перевести индикацию на другой таймер, например, 16-битный Timer 1 , или ввести счетчик пропускаемых переполнений таймера:

#define SKIP 15 /* число пропускаемых прерываний таймера */ ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; static unsigned char skip = SKIP; if (--skip) return; skip = SKIP; COLS = 0xFF; ROWS = SCR; COLS = ~(1 << pos); if(++pos == SCR_SZ) pos = 0; }

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

COLS |= 0x3F; // так гасим все знакоместа COLS &= ~(1<

Оба оператора не изменяют значение старших битов порта COLS .

3.2 Способ с дешифратором

Дешифратор может использоваться либо для преобразования HEX или BCD кода в семисегментные символы, либо для выбора одного из столбцов матрицы. Оба варианта будут отличаться от рассмотренного ранее простейшего способа лишь тем, как будет организован вывод в порты ROWS и/или COLS , к которым будут подключены входы дешифратора.
Вариант использования дешифратора для получения семисегментного символа:

ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; COLS |= 0x3F; ROWS = (ROWS & 0xF0) | (SCR & 0x0F); COLS &= ~(1 << pos); if(++pos == SCR_SZ) pos = 0; }

Как видите, изменения минимальны - перед тем, как вывести в ROWS код символа из массива SCR , маскируются старшие биты, после чего младшие устанавливаются в соответствии с кодом символа. То есть мы считаем, что дешифратор подключен к 4-ем младшим битам порта ROWS .

Приводить пример для дешифрации столбцов, надеюсь, смысла нет - все и так понятно.

3.3 Способ с регистрами

Хотя динамическая индикация при помощи сдвиговых регистров принципиально не отличается от ранее рассмотренных способов, есть несколько вариантов ее реализации. Мы рассмотрим наиболее простой - вывод битов чисто программными средствами. А в реализации других (с использованием USI /USART /SPI /TWI ) вы можете попробовать свои силы самостоятельно.

Для варианта ранее выбранного дисплея из 6-и семисегментных знакомест мы используем 2 сдвиговых регистра типа 74HC595 . Управляется этот регистр тремя сигналами: тактовые импульсы последовательного ввода данных CLK , собственно данные DATA и импульс одновременного параллельного вывода записанной в регистр информации SET . Объявим соответствующие макросы (для простоты, все сигналы «заведем» на один порт):

#define CLK _BV(PB0) #define DATA _BV(PB1) #define SET _BV(PB2) #define REG_PORT PORTB

Для записи в регистр удобно написать отдельную функцию:

Static void shift(unsigned char d){ unsigned char i; for (i=0; i < 8; i++){ // устанавливаем нужный уровень DATA if(d & 1) REG_PORT |= DATA; else REG_PORT &= ~DATA; REG_PORT |= CLK; // даем импульс CLK REG_PORT &= ~CLK; d >>= 1; } }

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

Теперь наш обработчик прерывания станет выглядеть так:

ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; shift(SCR); shift(~(1<< pos)); REG_PORT |= SET; // выдаем импульс SET REG_PORT &= ~SET; if(++pos == SCR_SZ) pos = 0; }

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

4 Для тех, кому всегда всего мало

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

4.1 Аноды, катоды - что выбрать?

Все, что мы рассматривали ранее, относилось к индикаторам с общими катодами. А если требуется использовать с общими анодами? В общем, все остается по-прежнему, кроме того, что перед выводом нужно будет проинвертировать данные - гашение знакоместа осуществлять выводом нулей в COLS , зажигание - соответственно, единиц, а сегменты в ROWS будут включаться нулями вместо единиц. Так что обработчик прерывания станет примерно таким:

ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; COLS &= 0xC0; ROWS = ~SCR; COLS |= (1 << pos); if(++pos == SCR_SZ) pos = 0; }

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

#define COMMON_ANODE 1 ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; #if COMMON_ANODE != 1 COLS &= 0xC0; ROWS = ~SCR; COLS |= (1 << pos); #else COLS |= 0x3F; ROWS = SCR; COLS &= ~(1 << pos); #endif if(++pos == SCR_SZ) pos = 0; }

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

4.2 Мерцание

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

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

unsigned char blink = 0;

Теперь немного модифицируем обработчик прерывания:

ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; static unsigned char entry = 0; COLS |= 0x3F; if(!(blink & (1<

Как видите, добавлена всего одна статическая переменная - счетчик входов в обработчик прерывания entry , и оператор проверки условия. Логика проста: вывод очередного знакоместа осуществляется лишь в том случае, если либо в соответствующем бите blink ноль, либо старший бит счетчика entry равен 1. Если, предположим, blink содержит все нули, то данное условие выполняется всегда - выводятся все знакоместа. Если же blink содержит 1 в одном из своих битов, то соответствующее знакоместо будет высвечиваться лишь в то время, когда старший бит счетчика равен 1. Так как счетчик увеличивается каждый раз при входе в обработчик прерываний, то соответствующее знакоместо будет мерцать с частотой в 128 раз меньшей, чем частота прерываний.

4.3 Регулировка яркости сегментов

О регулировке яркости я писал в отдельной статье, которая так и называлась .

4.4 Произвольное распределение выводов

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

Рассмотрим некий абстрактный пример. Пусть наилучшая трассировка обеспечивается при следующем распределении сигналов по линиям портов МК:

Сегмент А

Сегмент В

Сегмент H

Сегмент С

Сегмент D

Сегмент G

Сегмент E

Сегмент F

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

Разработку функции динамической индикации для этого случая лучше всего начать с распределения сегментов по битам символа. Раньше мы считали, что в массиве SCR у нас хранятся битовые маски символов, т.е. единичками в байте обозначены светящиеся сегменты. О том, какой бит какому сегменту соответствует, мы не задумывались. Так вот, сейчас пора задуматься об этом.

Удобно разрисовать назначение линий портовв виде трех табличек:

1

A

0

4

H

3

2

B

F

E

5

G

D

C

Мы должны собрать все сегменты в один байт. Делать это придется операциями сдвига, поэтому следует постараться распределить их так, чтобы делать минимум сдвигов. Будем рассуждать.

Если биты сегментов FEGDC оставить в символе так, чтобы они попадали в PORTD без сдвигов, тогда сегмент H так же может остаться в 6-ом бите символа, и его тоже не придется сдвигать перед выводом PORTC , зато для сегментов А и В останутся биты 7 и 3, то есть скорее всего сегмент В придется сдвигать на 3 позиции перед выводом, а сегмент А - на 6. Я остановлюсь на этом варианте, а вы можете продолжить поиск минимума сдвигов (сдвиги на несколько позиций - не такая уж быстрая операция, поэтому желательно свести их число к минимуму).

Итак, в нашем случае получилось такое распределение битов по байту-символу:

A

H

F

E

B

G

D

C

Отметим маски битов для вывода в соответствующие порты:

D

0

0

1

1

0

1

1

1

0x37

B

1

0

0

0

0

0

0

0

0x80

C

0

1

0

0

1

0

0

0

0x48

При помощи этих масок операцией «побитовое И» мы выделим нужные биты для вывода в порт.

Объявим константы масок:

#define MASKD 0x37 #define MASKB 0x80 #define MASKC 0x48

Ранее мы выводили символ в единственный порт ROWS , теперь же эта процедура разделится на три части:

PORTD = (PORTD & ~MASKD) | (SCR & MASKD); PORTB = (PORTB & ~MASKB) | ((SCR & MASKB) >> 6); PORTC = (PORTC & ~MASKC) | ((SCR & _BV(6)) | (((SCR & _BV(3)) >> 3);

Обратите внимание, что для вывода в PORTC один бит надо выводить без сдвига, а второй - со сдвигом, поэтому вместо MASKC пришлось использовать отдельные макросы _BV() .

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

#define COM0 _BV(0) #define COM1 _BV(3) #define COM2 _BV(4) #define COM3 _BV(5) #define COM4 _BV(7) #define COM5 _BV(3) #define COM_D (COM5) #define COM_C (COM2 | COM3 | COM4) #define COM_B (COM0 | COM1)

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

PORTD |= COM_D; PORTC |= COM_C; PORTB |= COM_B;

А вот для включения знакоместа придется мудрить (нет смысла осуществлять вывод во все три порта, ведь активным будет только один-единственный бит в определенном порту в зависимости от значения pos ), например, при помощи оператора switch :

Switch(pos){ case 0: PORTB &= ~COM0; break; case 1: PORTB &= ~COM1; break; case 2: PORTC &= ~COM2; break; case 3: PORTC &= ~COM3; break; case 4: PORTC &= ~COM4; break; case 5: PORTD &= ~COM5; break; }

Это не самый красивый способ, но он работает.

Таким образом, наш обработчик прерывания приобретает следующий вид:

ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; static unsigned char entry = 0; // гасим PORTD |= COM_D; PORTC |= COM_C; PORTB |= COM_B; // выводим PORTD = (PORTD & ~MASKD) | (SCR & MASKD); PORTB = (PORTB & ~MASKB) | ((SCR & MASKB) >> 6); PORTC = (PORTC & ~MASKC) | ((SCR & _BV(6)) | (((SCR & _BV(3)) >> 3); // мигаем if(!(blink & (1<< pos)) || (++entry & 0x80)) { switch(pos){ case 0: PORTB &= ~COM0; break; case 1: PORTB &= ~COM1; break; case 2: PORTC &= ~COM2; break; case 3: PORTC &= ~COM3; break; case 4: PORTC &= ~COM4; break; case 5: PORTD &= ~COM5; break; } } if(++pos == SCR_SZ) pos = 0; }

Теперь осталось разобраться, как же поудобнее описать символы для вывода... Я предлагаю поступить следующим образом: определить константы, соответствующие битам сегментов, а затем из этих констант «сконструировать» нужные символы:

// элементарные сегменты #define _A _BV(7) #define _B _BV(3) #define _C _BV(0) #define _D _BV(1) #define _E _BV(4) #define _F _BV(5) #define _G _BV(2) #define _H _BV(6) // символы цифр #define d_0 (_A | _B | _C | _D | _E | _F) #define d_1 (_B | _C) #define d_2 (_A | _B | _G | _D | _E) // и так далее

Таким образом, если вам понадобится вывести в крайнюю правую позицию дисплея нолик, вы всего-навсего должны написать в нужном месте:

SCR = d_0;

Если в другом проекте вам понадобится иначе распределить биты, вы поменяете только цифры в макросах _BV() для элементарных сегментов, и все символы «переделаются» автоматически. Для описанных в начале простейших случаев не придется больше ничего делать вообще, а для варианта с «перестановкой битов», придется, конечно повозиться.

4.5 Поддержка кнопок

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

Схемотехнически это показано на рисунке.

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

#define keypin() (!(PIND & _BV(KEY))) ISR(TIMER0_OVF_vect){ static unsigned char pos = 0; static unsigned char entry = 0; static unsigned char tmp_key = 0; ROWS = 0; if(keypin()) tmp_key |= 1<< pos; COLS |= 0x3F; if(!(blink & (1<< pos)) || (++entry &0x80)){ ROWS = (ROWS & 0xF0) | (SCR & 0x0F); COLS &= ~(1 << pos); } if(++pos == SCR_SZ){ pos = 0; key = tmp_key; tmp_key = 0; } }

Здесь KEY - это макрос, задающий бит выбранного порта, на котором «соединяются» все кнопки, макрос keypin() возвращает логическое значение ИСТИНА, если на выбранном пине присутствует низкий логический уровень. В примере кнопки подключены к PORTD .

Каждый раз, когда возникает прерывание таймера, сначала гасятся все сегменты - это необходимо для того, чтобы ток через светодиоды не приводил к ошибочному не обнаружению нажатой кнопки. После этого происходит опрос кнопочного входа - если там низкий уровень, значит, нажата кнопка, подключенная к соответствующему pos катоду. В переменной tmp_key накапливаются состояния кнопок, которые переписываются в глобальную переменную key после завершения цикла индикации. Вам остается лишь время от времени анализировать значение key и обрабатывать обнаруженные нажатия:

Static unsigned char get_key(){ unsigned char tmp = 0; tmp = key; _delay_ms(10); if(key == tmp) return tmp; else return 0; }

Эта несложная функция гарантирует отсутствие дребезга кнопок, не смотря на то, что из-за «динамического» характера опроса кнопок вероятность дребезга и без того низкая.

5 Что еще?

Итак, вы освоили достаточно характерные приемы реализации динамической индикации. Думаю, этого хватит вам на первое время, а может, и вообще на всю оставшуюся жизнь. В конце концов, главное - это понимание приемов и алгоритмов, а тонкости и нюансы всегда можно прибавить самостоятельно. Но что же еще может ждать «вблизи» динамической индикации?

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

Можно подумать над оптимальностью обработчика прерывания - в учебных целях я писал достаточно грубый код, например, всюду использовал SCR , хотя оптимальнее было бы один раз считать значение в локальную переменнную, а потом оперировать уже с ее значением. Хотя оптимизатор наверняка выручит и с моим подходом, в целях практики стоит попробовать и самостоятельно пооптимизировать, контролируя себя по размеру получающегося кода и/или быстродействию программы.

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

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

Интересный вариант полностью универсального подхода к динамической индикации, с которым так же рекомендую познакомиться, предложил MOLCHEC . Кратко суть: распределение сегментов по битам символа, назначение портов для управления индикатором и даже тип индикатора - короче говоря, все-все-все параметры, - задаются в виде таблицы конфигурации в EEPROM . Программно на основе этой таблицы организуется все: от инверсии в зависимости типа индикатора, до перестановки битов по разным портам. При этом исходный код программы динамической индикации остается неизменным всегда, а таблица конфигурации составляется конечным пользователем в зависимости от своих предпочтений. Метод действиетльно универсальный и гибкий, однако сопряжен с повышенным расходом памяти программ.


3 Написал(а) ARV , в 06:48 25.08.2010
Миша , я бы на вашем месте не стал бы давать такие безапелляционные заявления "не можете сделать", "никто не написал" или "копирайтят", потому что это во-первых, не вежливо, а во-вторых:
1. я давно сделал бегущую строку на матрице 10х16 (уж какая была) - вы можете найти видео ее работы в этой заметке http://сайт/content/view/160/38/
2. я написал статью (вы ее найдете в новостях - последняя на сегодня) о том, как сделать бегущую строку на ЖКИ. если немного напрячь мозг, то переделать алгоритм для вывода на матрицу - пустячок.
3. на моем сайте нет ни одной статьи, откуда-то скопированной (копипаст - не копирайт, вы опечатались), все материалы полностью оригинальные. на многих сайтах есть копии этих материалов с моего разрешения (или разрешения авторов материалов, которые имеют полное право публиковать свои материалы сразу во многих местах).

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

Одним из распространённых способов вывода информации является симисегментный индикатор. Он без труда позволяет выводить цифровую и псевдотекстовую информацию (так же есть специальный текстовые многосегментые индикаторы), но требует большого количества свободных выводов у контроллера. Сократить число используемых выводов можно двумя способами: применить специальный драйвер (или буферный элемент) или применить динамическую индикацию. Где на дисплей пойдут 7 выводов на каждый из сегментов и по одному на каждый символ, а загораться символы будут по очереди с настолько большой скоростью, что человек не будет это различать и ему будет казаться равномерно горящий индикатор.

OPTION=0b00000000; // Настройка TMR0 INTCON=0b10100000; // Прерывания разрешены и только от TMR0

Для работы понадобятся следующие глобальные переменные:

Unsigned char v1,v2,v3, vn;

Первые три отвечают за значения соответствующих сегментов, а последняя за номер горящего сегмента в данный момент (динамическая индикация).

Обработчик прерываний занят обработкой таймера, который постоянно по очереди сменяет сегменты.

Void interrupt isr(void) { if(T0IF) // при переполнение TMR0 { vn++; // переключение сегмента switch(vn) // выбор сегмента { case 1:seg7(v1,1);break; case 2:seg7(v2,2);break; case 3:{seg7(v3,3);vn=0;}break; } TMR0=100; // установка таймера не на начало T0IF=0; // сбрасываем флаг } }

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

Void seg7(unsigned char c, unsigned char s) { unsigned char t=0; // g сегмент PORTA=0b00000000; // сброс порта A switch(s) // выбор анода { case 1:{t=1;}break; case 2:{t=2;}break; case 3:{t=32;}break; } switch(c%10) // выбор сегментов (катодов) { // 00bfaed g case 0: {PORTC=0b00000000;PORTA=t+4;}break; case 1: {PORTC=0b00011110;PORTA=t+4;}break; case 2: {PORTC=0b00010001;PORTA=t;}break; case 3: {PORTC=0b00010100;PORTA=t;}break; case 4: {PORTC=0b00001110;PORTA=t;}break; case 5: {PORTC=0b00100100;PORTA=t;}break; case 6: {PORTC=0b00100000;PORTA=t;}break; case 7: {PORTC=0b00010110;PORTA=t+4;}break; case 8: {PORTC=0b00000000;PORTA=t;}break; case 9: {PORTC=0b00000100;PORTA=t;}break; } }

У PIC16F676 неполный порты вывода, поэтому все сегменты на порт C не влезли, сегмент g пришлось разместить на порте A, а точку приходиться подключать схематически к одному из общих анодов через инвертор. Схема такого подключения выглядит следующим образом:

Если использовать другой микроконтроллер, у которого есть хотя бы один полноценный порт, например PIC16F628A

функция примет следующий вид:

Void seg7(unsigned char c, unsigned char s) { unsigned char t=0; switch(c%10) // выбор сегментов (катодов) { // gcPdeafb case 0: {t=0b10100000;}break; case 1: {t=0b10111110;}break; case 2: {t=0b01100010;}break; case 3: {t=0b00101010;}break; case 4: {t=0b00111100;}break; case 5: {t=0b00101001;}break; case 6: {t=0b00100001;}break; case 7: {t=0b10111010;}break; case 8: {t=0b00100000;}break; case 9: {t=0b00101000;}break; } PORTA=0b00000000; // сброс порта A switch(s) // выбор анода { case 1:{ PORTA =4;}break; case 2:{ PORTA =8;}break; case 3:{ PORTA =64;}break; } PORTB=t; if(c>9) { PORTB=t&0b11011111; } }

Точка у символа загорается, если с больше 9, значение на индикатор выводиться всё равно будет, т.е. будет выводиться только младший разряд, остальное всё отбрасываться (например: 19 , 39 = 9. ).

В основной функции (main) МК спокойно выполняет свою работу, для вывода новой информации на дисплей ему достаточно изменить значения переменных v1 ,v2 ,v3 , но изменять их следует одновременно, что бы избежать неразберихи на индикаторе.

Для аппаратуры с батарейным питанием применение LCD-индикаторов, как правило, считается более предпочтительным, чем светодиодных (LED) из-за большого тока потребления последних. Данный постулат мне кажется совсем не очевидным по следующим причинам: 1) в современных LCD-индикаторах существует подсветка, которая потребляет до 100 мА; 2) они относительно хрупки и боятся прямых лучей солнца; 3) современные LED-индикаторы (особенно superRED и ultraRED) обладают достаточной яркостью даже при токе в 1 мА через них, и при оперативной регулировке яркости в зависимости от условий освещения, средний ток потребления 4-разрядного индикатора получается не более 30 мА даже на улице, что меньше чем потребление подсветки LCD.

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

В статье рассматривается способ управления светодиодной матрицей микроконтроллером PIC среднего семейства, используя прерывания от таймеров TMR0 и TMR2. Таймер TMR2 используется для ШИМ-регулирования среднего тока через включенные сегменты. Алгоритм организации работы следующий:

1. Инициализация. Настраиваем порты микроконтроллера в соответствии со схемой подключения индикатора. Для таймеров 1 и 2 включается режим внутреннего тактирования с предделителем, равным 16 . Разрешается прерывания от периферии.

2. Создаем таблицу-знакогенератор для вывода на индикатор цифр и некоторых (преимущественно латинских) букв и знаков.

3. Резервируем два четырехразрядных переменных. В одну заносим последовательно цифровой код (для цифр – просто цифру) необходимого для вывода знака в соответствии с таблицей из п.2. В другую переменную передаются преобразованные значения из таблицы для постоянного высвечивания на индикаторе.

4. В прерывании от TMR0 последовательно высвечиваются разряды знаков в соответствии с таблицей. Перед сменой разрядов индикатор гасится. В каждом прерывании высвечивается один разряд. После этого обнуляется таймер TMR2, сбрасывается флаг прерывания от TMR2 и разрешаются от него прерывания.

5. В прерывании от TMR2 гасится индикатор и запрещается прерывание от TMR2.

6. В основной программе осуществляется регулировка периода прерывания от TMR2, а значит, времени включенного состояния индикатора путем занесения в регистр PR2 чисел от 7 до 255 в десятичном исчислении по формуле X(n+1)=2*X(n)+1. Получается шесть градаций яркости с разницей между ними в 2 раза. При PR2=255 длительность максимальна (4мс из 4мс), при PR2=7 длительность равна примерно 0.25мс.

Для демонстрации указанного принципа управления, ниже приводится схема на недорогом PIC16F628A и тестовая программа на Ассемблере, которая выводит на индикатор слово «test». При нажатии на кнопку, на индикаторе высвечивается яркость (условно цифрами от 0 до 5). При последующих нажатиях, яркость изменяется по кругу и это сразу видно на индикаторе. Сразу хочу предупредить начинающих: моделирование схемы на симуляторе типа Proteus не позволит увидеть изменение яркости в силу особенностей этой программы (Proteus). Макет схемы для проверки и экспериментов придется собирать в «железе». Впрочем, для наблюдения собственно за организацией динамической индикации (кроме изменения яркости) Proteus-модель прилагается.

Потребление схемы при минимальной яркости менее 4 мА, при максимальной – около 80 мА.

В архиве приведена тестовая программа на Ассемблере MPASM.

Для упрощения схемы и освобождения «ног» для различных действий, применена конфигурация с внутренним генератором и внутренним сбросом. При этом, у тех, что пользуется самодельным программатором без возможности подачи сигнала MCLR раньше Upp, могут быть проблемы с последующими верификацией, чтением и стиранием. Тем, кто не уверен в своем программаторе, а так же если требуется высокая стабильность генератора, можно установить кварц 4 МГц по типовой схеме с выбором в конфигурации “OSC_XT”. В случае, если в конечной схеме требуются прерывания с вывода INT0 (RB0), запятой можно управлять посредством вывода RA4, для индикатора с ОА индикатор к этому выводу подключается напрямую, несмотря на то, что он открытый. Освободившийся вывод RB0 можно использовать по назначению. В программу, в прерывании от TMR0, этом случае, добавляется после “movwf PORTB” код:

Andlw b"00000001" bsf PORTA,4 погасить запятую btfsc STATUS,Z учитываем, что в W инверсное знач. bcf PORTA,4 если 0-й бит = 0, зажечь запятую

Небольшие пояснения к программе:

Выводимое число помещается в переменные OUT_ - OUT+3 в соответствии с разрядом, а с нее в подпрограмме out__ после преобразования помещается в OUT_LED. Конечно можно было бы обойтись без переменной OUT_ и везде для вывода писать:

Movlw X call Table_s movwf OUT_LED

Однако, в исходном виде все гораздо проще и понятнее (поместил в OUT_ и забыл), а так же при множественных выводах с разных мест программы получается экономия кода (4 слова на 1 вывод) – думается хорошая компенсация за лишние 4 байта ОЗУ.

То же самое касается и вывода запятой через переменную comma_.

В подпрограмме-таблице Table_s приняты меры для корректной работы при помещении ее в любое место памяти программ без ограничений на пересечение блоков 256 байт.

Переменная pause_ в прерывании от TMR0 используется для задания временных интервалов 4 мс.

Остальное, я думаю, понятно из алгоритма и комментариев.

P.S. Для 2 или 3 разрядов в программе требуется произвести минимальные изменения, которые, думается, по силам даже для начинающих. Для управления индикатором с количеством разрядов от 5 до 8 необходимо или применить контроллер с большим количеством выводов или же для управления разрядами применить дешифратор 3 на 8.

В первом случае изменения в программе также минимальны (применение вместо порта А другого порта и т.д.). В случае применения дешифратора программа в части прерывания от TMR0 изменится довольно серьезно.

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
U1 МК PIC 8-бит

PIC16F628A

1 В блокнот
H1 Индикатор 4х7 FIQ-3641A 1 В блокнот
Q1-Q4 Биполярный транзистор

КТ361Е

4 В блокнот
C3 Конденсатор 22 нФ 1 В блокнот
R1-R7, R14 Резистор

150 Ом

8 В блокнот
R8 Резистор


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

Устройство отображения цифровой информации. Это - наиболее простая реализация индикатора, который может отображать арабские цифры. Для отображения букв используются более сложные многосегментные и матричные индикаторы.

Как говорит его название, состоит из семи элементов индикации (сегментов), включающихся и выключающихся по отдельности. Включая их в разных комбинациях, из них можно составить упрощённые изображения арабских цифр.
Сегменты обозначаются буквами от A до G; восьмой сегмент - десятичная точка (decimal point, DP), предназначенная для отображения дробных чисел.
Изредка на семисегментном индикаторе отображают буквы.

Бывают разных цветов, обычно это белый, красный, зеленый, желтый и голубой цвета. Кроме того, они могут быть разных размеров.

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

Кроме десяти цифр, семисегментные индикаторы способны отображать буквы. Но лишь немногие из букв имеют интуитивно понятное семисегментное представление.
В латинице : заглавные A, B, C, E, F, G, H, I, J, L, N, O, P, S, U, Y, Z, строчные a, b, c, d, e, g, h, i, n, o, q, r, t, u.
В кириллице : А, Б, В, Г, г, Е, и, Н, О, о, П, п, Р, С, с, У, Ч, Ы (два разряда), Ь, Э/З.
Поэтому семисегментные индикаторы используют только для отображения простейших сообщений.

Всего семисегментный светодиодный индикатор может отобразить 128 символов:

В обычном светодиодном индикаторе девять выводов: один идёт к катодам всех сегментов, а остальные восемь - к аноду каждого из сегментов. Эта схема называется «схема с общим катодом» , существуют также схемы с общим анодом (тогда все наоборот). Часто делают не один, а два общих вывода на разных концах цоколя - это упрощает разводку, не увеличивая габаритов. Есть еще, так называемые «универсальные», но я лично с такими не сталкивался. Кроме того существуют индикаторы со встроенным сдвиговым регистром, благодаря чему намного уменьшается количество задействованных выводов портов микроконтроллера, но они намного дороже и в практике применяются редко. А так как необъятное не объять, то такие индикаторы мы пока рассматривать не будем (а ведь есть еще индикаторы с гораздо большим количеством сегментов, матричные).

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

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

На схеме ниже, показано как подключается одноразрядный семисегментный индикатор к микроконтроллеру.
При этом следует учитывать, что если индикатор с ОБЩИМ КАТОДОМ , то его общий вывод подключается к «земле» , а зажигание сегментов происходит подачей логической единицы на вывод порта.
Если индикатор с ОБЩИМ АНОДОМ , то на его общий провод подают «плюс» напряжения, а зажигание сегментов происходит переводом вывода порта в состояние логического нуля .

Осуществление индикации в одноразрядном светодиодном индикаторе осуществляется подачей на выводы порта микроконтроллера двоичного кода соответствующей цифры соответствующего логического уровня (для индикаторов с ОК — логические единицы, для индикаторов с ОА — логические нули).

Токоограничительные резисторы могут присутствовать в схеме, а могут и не присутствовать. Все зависит от напряжения питания, которое подается на индикатор и технических характеристик индикаторов. Если, к примеру, напряжение подаваемое на сегменты равно 5 вольтам, а они рассчитаны на рабочее напряжение 2 вольта, то токоограничительные резисторы ставить необходимо (чтобы ограничить ток через них для повышенного напряжении питания и не сжечь не только индикатор, но и порт микроконтроллера).
Рассчитать номинал токоограничительных резисторов очень легко, по формуле дедушки Ома .
К примеру, характеристики индикатора следующие (берем из даташита):
— рабочее напряжение — 2 вольта
— рабочий ток — 10 мА (=0,01 А)
— напряжение питания 5 вольт
Формула для расчета:
R= U/I (все значения в этой формуле должны быть в Омах, Вольтах и Амперах)
R= (напряжение питания — рабочее напряжение)/рабочий ток
R= (5-2)/0.01 = 300 Ом

Схема подключения многоразрядного семисегментного светодиодного индикатора в основном та-же, что и при подключении одноразрядного индикатора. Единственное, добавляются управляющие транзисторы в катодах (анодах) индикаторов:

На схеме не показано, но между базами транзисторов и выводами порта микроконтроллера необходимо включать резисторы, сопротивление которых зависит от типа транзистора (номиналы резисторов рассчитываются, но можно и попробовать применить резисторы номиналом 5-10 кОм).

Осуществление индикации разрядами осуществляется динамическим путем:
— выставляется двоичный код соответствующей цифры на выходах порта РВ для 1 разряда, затем подается логический уровень на управляющий транзистор первого разряда
— выставляется двоичный код соответствующей цифры на выходах порта РВ для 2 разряда, затем подается логический уровень на управляющий транзистор второго разряда
— выставляется двоичный код соответствующей цифры на выходах порта РВ для 3 разряда, затем подается логический уровень на управляющий транзистор третьего разряда
— итак по кругу
При этом надо учитывать:
— для индикаторов с ОК применяется управляющий транзистор структуры NPN (управляется логической единицей)
— для индикатора с ОА — транзистор структуры PNP (управляется логическим нулем)

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

Светодиодные семисегментные индикаторы остаются одними из самых популярных элементов для отображения цифровой информации.

Этому способствуют следующие их качества.

  • Низкая цена. В средствах индикации нет ничего дешевле светодиодных цифровых индикаторов.
  • Разнообразие размеров. Самые маленькие и самые большие индикаторы – светодиодные. Мне известны светодиодные индикаторы с высотой цифры от 2,5 мм, до 32 см.
  • Светятся в темноте. В некоторых приложениях это свойство чуть ли не решающее.
  • Имеют различные цвета свечения. Бывают даже двухцветные.
  • Достаточно малые токи управления. Современные светодиодные индикаторы могут подключаться к выводам микроконтроллеров без дополнительных ключей.
  • Допускают жесткие условия эксплуатации (температурный диапазон, высокая влажность, вибрации, агрессивные среды и т.п.). По этому качеству светодиодным индикаторам нет равных среди других типов элементов индикации.
  • Неограниченный срок службы.

Типы светодиодных индикаторов.

Семисегментный светодиодный индикатор отображает символ с помощью семи светодиодов – сегментов цифры. Восьмой светодиод засвечивает децимальную точку. Так что в семисегментном индикаторе 8 сегментов.

Сегменты обозначаются латинскими буквами от ”A” до ”H”.

Аноды или катоды каждого светодиода объединяются в индикаторе и образуют общий провод. Поэтому существуют индикаторы с общим анодом и общим катодом.

Светодиодный индикатор с общим анодом.

Светодиодный индикатор с общим катодом.

Статическое управление светодиодным индикатором.

Подключать светодиодные индикаторы к микроконтроллеру необходимо через резисторы, ограничивающие ток.

Расчет резисторов такой же, как для отдельных светодиодов.

R = (U питания - U сегмента) / I сегмента

Для этой схемы: I сегмента = (5 – 1,5) / 1000 = 3,5 мА

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

В схеме подключения индикатора с общим катодом меняется полярность питания и сигналов управления.

Засветится сегмент, на управляющем выводе которого будет сформирован высокий уровень (5 В).

Мультиплексированный режим управления светодиодными (LED) индикаторами.

Для подключения каждого семисегментного индикатора к микроконтроллеру требуется восемь выводов. Если индикаторов (разрядов) 3 – 4, то задача становится практически не выполнимой. Просто не хватит выводов микроконтроллера. В этом случае индикаторы можно подключить в мультиплексированном режиме, в режиме динамической индикации.

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

Для подключения трех индикаторов потребовалось 11 выводов, а не 24, как при статическом режиме управления.

При динамической индикации в каждый момент времени горит только одна цифра. На общий вывод одного из разрядов подается сигнал высокого уровня (5 В), а на выводы сегментов поступают сигналы низкого уровня для тех сегментов, какие должны светиться в этом разряде. Через определенное время зажигается следующий разряд. На его общий вывод подается высокий уровень, а на выводы сегментов сигналы состояния для этого разряда. И так для всех разрядов в бесконечном цикле. Время цикла называется временем регенерации индикаторов. Если время регенерации достаточно мало, то человеческий глаз не заметит переключения разрядов. Будет казаться, что все разряды светятся постоянно. Для исключения мерцания индикаторов считается, что частота цикла регенерации должно быть не менее 70 Гц. Я стараюсь использовать не менее 100 Гц.

Схема динамической индикации для светодиодов с общим катодом выглядит так.

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

Расчет элементов динамической индикации светодиодных (LED) индикаторов.

Расчет несколько сложнее, чем для статического режима. В ходе расчета необходимо определить:

  • средний ток сегментов;
  • импульсный ток сегментов;
  • сопротивление резисторов сегментов;
  • импульсный ток общих выводов разрядов.

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

Выберем средний ток сегмента 1 мА.

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

I сегм. имп. = I сегм. средн. * N

Для нашей схемы I сегм. имп. = 1 * 3 = 3 мА.

Рассчитываем сопротивление резисторов, ограничивающих ток.

R = (U питания - U сегмента) / I сегм. имп.

R = (5 – 1,5) / 0.003 = 1166 Ом

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

I разряда имп. = I сегм. имп. * 8

Для нашей схемы I разряда имп. = 3 * 8 = 24 мА.

  • сопротивление резисторов выбираем 1,1 кОм;
  • выводы микроконтроллера управления сегментами должны обеспечивать ток не менее 3 мА;
  • выводы микроконтроллера выбора разряда индикатора должны обеспечивать ток не менее 24 мА.

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

Схемы с дополнительными ключами.

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

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

Для выбора разряда в этой схеме необходимо сформировать сигнал низкого уровня. Соответствующий ключ откроется и подаст питание на разряд индикатора.

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

Для выбора разряда в этой схеме необходимо сформировать сигнал высокого уровня. Соответствующий ключ откроется и замкнет общий вывод разряда на землю.

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

Ключи для индикаторов с повышенным напряжением питания .

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

Схема ключей, замыкающих сигналы индикатора на землю, остается неизмененной. А ключи питания должны строиться по другой схеме, например, такой.

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

Между переключением разрядов индикатора на короткое время (1-5 мкс) должны выключаться все сегменты. Это время необходимо на завершение переходных процессов коммутации ключей.

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

В следующем уроке подключим семисегментный светодиодный индикатор к плате Ардуино, напишем библиотеку для управления им.

Рубрика: . Вы можете добавить в закладки.