Еще раз о динамической индикации на led-индикаторах

AVR Урок 27. SPI. LED – динамическая индикация

Урок 27

SPI. LED – динамическая индикация

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

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

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

Соответственно, для такой цели нам не хватит одного регистра, и мы возьмём ещё один.

Это также даёт нам уникальную возможность позаниматься с интерфейсом SPI, подключив одновременно два ведомых устройства. Причём попробуем мы их подключить, используя кольцевой (или каскадный способ).

Вот наша схема в протеусе (нажмите на картинку для увеличения изображения)

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

Теперь левая часть схемы. Здесь подключены параллельно ножки синхронизации и ножки выбора а также подачи команды сдвига SS. А вот c ножки MOSI контроллера сигнал идёт на цифровой вход микросхемы отвечающей за аноды, а уже с цифрового выхода этой микросхемы – на цифровой вход микросхемы, отвечающей за кактоды. Соответственно здесь у нас кольцо рвётся, так как подключать вход контроллера MISO с цифрового выхода верхней микросхемы мы не будем за ненадобностью такого мониторинга.

То есть, первым делом мы будем посылать байт для катодов, а затем без задержки – байт для анодов. Тем самым байт для катодов попадёт по кругу на нужную микросхему, а байт для катодов придёт в свою микросхему. Затем мы уже сгенерируем импульс для подачи байтов в регистры хранения микросхем, и немного подождём. И в это время уже наши индикаторы получат нужные для себя сигналы.

Вообщем как-то вот так.

Также давайте посмотрим всё это по-настоящему, с настоящим контроллером, регистрами и индикаторами

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

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

Также из проекта, который мы писали в занятии по сборке часов на светодиодном индикаторе, мы возьмём библиотеку для индикаторов led.c и led.h и подключим её в наш проект. Как подключать библиотеки, я уже объяснять не буду, это уже, я думаю, знают все. И, понятное дело, данную библиотеку для работы с шиной SPI придется как-то модифицировать. Но это немного позже.

А пока мы займёмся всё-таки основным модулем.

Сначала мы в функцию main() добавим переменную, которая нам будет считать до 9999

unsigned int i =0;

Дальше давайте процедуру инициализации шины SPI оформим в отдельную функцию и напишем её выше функции main

void SPI_init ( void )

DDRB |= ((1 PORTB2 )|(1 PORTB3 )|(1 PORTB5 )); //ножки SPI на выход

((1 PORTB2 )|(1 PORTB3 )|(1 PORTB5 )); //низкий уровень

SPCR = ((1 SPE )|(1 MSTR )); //включим шину, объявим ведущим

Соответственно, этот код мы из main() удалим и вместо него вызовем данную функцию

unsigned int i =0;

SPI_init ();

Далее инициализируем наш таймер и включим глобальные прерывания

timer_ini ();

sei ();

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

while (!( SPSR & (1 SPIF ))); //подождем пока данные передадутся

SPDR = 0b00000000;

while (!( SPSR & (1 SPIF ))); //подождем пока данные передадутся

//сгенерируем отрицательный фронт дл¤ записи в STORAGE REGISTER

PORTB |= (1 PORTB2 ); //высокий уровень

(1 PORTB2 ); //низкий уровень

Из бесконечного цикла удалим всё полностью. Затем мы в нём напишем наш стандартный код счёта индикатора

for ( i =0; i i ++)

ledprint ( i );

_delay_ms (500);

У нас в функции ledprint также во входных аргументах есть тип проме самого числа, но он нам будет не нужен и мы его уберём в библиотеке.

Сначала мы это подправим в прототипе функции

void ledprint ( unsigned int number );

Уберём теперь из led.c все макросы, так как они нам не нужны, а также уберём переменную для режима

#define MODETIMEVIEW 100

#define MODETEMPERVIEW 101

#define MODEDATEVIEW 102

#define MODEDAYVIEW 103

#define MODEYEARVIEW 104

#define MODENONEEDIT 0

#define MODEHOUREDIT 1

#define MODEMINEDIT 2

#define MODEDATEEDIT 3

#define MODEMONTHEDIT 4

#define MODEYEAREDIT 5

#define MODEDAYEDIT 6

#define MODEALARMHOUREDIT 7

#define MODEALARMMINEDIT 8

unsigned char R1 =0, R2 =0, R3 =0, R4 =0;

unsigned char clockmode ;

extern unsigned char clockeditmode ;

Сначала в функции segchar мы просто заменим PORTD на SPDR по понятным причинам

void segchar ( unsigned char seg )

case 1: SPDR = 0b11111001; break ;

case 2: SPDR = 0b10100100; break ;

case 3: SPDR = 0b10110000; break ;

case 4: SPDR = 0b10011001; break ;

case 5: SPDR = 0b10010010; break ;

case 6: SPDR = 0b10000010; break ;

case 7: SPDR = 0b11111000; break ;

case 8: SPDR = 0b10000000; break ;

case 9: SPDR = 0b10010000; break ;

case 0: SPDR = 0b11000000; break ;

case 10: SPDR = 0b10111111; break ; // знак –

case 11: SPDR = 0b11111111; break ; // пустое место

case 12: SPDR = 0b11000110; break ; // буква С для показаний температуры

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

Еще раз о динамической индикации на LED-индикаторах

В этой статье пойдет речь о некоторых, на мой взгляд, немаловажных аспектах организации динамической индикации (далее ДИ) на 7-сегментных LED индикаторах для микроконтроллерных систем. Ее можно рассматривать как продолжение и дополнение ранее опубликованной моей статьи.

В самом начале коснемся вопроса, который часто вызывает споры на форумах, а именно, насколько эффективна ДИ с точки зрения субъективной яркости по сравнению со статической индикацией, при условии равенства среднего тока, протекающего через сегмент индикатора (эффективность ДИ с позиции аппаратных затрат, я думаю, очевидна). Чтобы расставить все точки над «i» в данном вопросе, была собрана на макетной плате простая схема на МК PIC12F629, изображенная на схеме.

В этой простейшей схеме реализована индикация стилизованной буквы «С» таким образом, что через нижний сегмент (“d”) проходит постоянный ток 1 мА, далее снизу вверх: через сегмент “e” протекает ток 2 мА со скважностью 2, через сегмент “f” – ток 4 мА со скважностью 4 и, наконец, через сегмент “a” – ток 8 мА со скважностью 8. В итоге, имеем средний ток через каждый сегмент в 1 мА, но при разных скважностях от 1 до 8. Во вложении прилагается модель Proteus, в котором, при помощи логического анализатора, можно убедиться, что сигнал подается вышеописанным способом. Резисторы подобраны так, что обеспечиваются указанные токи (с учетом того, что падение напряжения на светодиоде при токах 1-4 мА примерно равно 1.8 В, а при 8мА – около 1.9 В). Ниже приведены фотографии свечения индикатора, как для указанных токов, так и для примерно в два раза меньших при напряжении питания 3.3 Вольт.

Как оказалось, субъективно видимая яркость свечения сегментов практически одинакова во всех четырех случаях (показалось, что есть едва-едва уловимое уменьшение яркости при возрастании тока и скважности, но оно настолько мало заметно, что я не уверен, что ее существование не плод моей фантазии). Исходя из этого эксперимента, утверждаю, что не правы как те, кто утверждает о субъективном снижении яркости, так и те, кто говорит о ее возрастании. ПРИ ОДНОМ И ТОМ ЖЕ СРЕДНЕМ ТОКЕ ЧЕРЕЗ СЕГМЕНТ ЯРКОСТЬ ПРИМЕРНО ОДИНАКОВА КАК ДЛЯ СТАТИЧЕСКОЙ ИНДИКАЦИИ, ТАК И ДЛЯ ДИНАМИЧЕСКОЙ ПРИ СКВАЖНОСТИ ОТ 1 ДО 8 . На том и закончим с этим вопросом.

Современные светодиодные индикаторы, особенно типов “Super Red” и “Hi Red”, начинают светиться вполне достаточно для помещений уже при токах от 0.5 мА. С учетом этого фактора возникает соблазн организовать ДИ без применения дополнительных ключей, управляя разрядами непосредственно с портов МК, например, как на нижеприведенной схеме.

Читайте также  Аналоги микросхем памяти на различную аппаратуру

При этом, ограничительные резисторы подбираются такого номинала, чтобы на «разрядных» выходах суммарный ток не превышал допустимый выходной ток вывода. Тут следует отметить, что хотя существуют разного рода доводы о допустимости превышения выходного тока на «разрядных» выходах ввиду большой скважности протекающего через них тока, я сторонник того, чтобы придерживаться Datasheet производителей, а в них (по крайней мере у Microchip) об этом ничего не сказано, для PIC — это максимум 25 мА. Исходя из этого, скажем так «правильные разработчики» выбирают номинал токоограничительного резистора таким, чтобы ток сегмента был не более 3мА (тогда 8 сегментов, включая точку, в сумме дадут 24 мА). При этом, обычно пользуются

формулой (1) R=(Upp-Uled)/I ,

где Upp – напряжение питания, Uled – падение напряжения на сегменте индикатора, I – ток через сегмент. По ней для Upp=5V и принимая для индикатора красного цвета свечения Uled=1.8V, получают R=(5-1.8)/3=1.067кОм, ближайший из ряда — 1.1кОм.

Однако, получаемый номинал, как будет показано ниже, завышен и ток получится меньше 3 мА, что не очень хорошо в ситуации дефицита тока, а значит, яркости сегмента. Дело в том, что формула (1) не учитывает выходного сопротивления порта, а он имеет значение, особенно для «разрядных» выводов, через которые протекает предельный ток, а значит и падает существенное напряжение. К сожалению, я не нашел в Datasheet для PIC среднего семейства точной величины выходного сопротивления порта. Косвенные вычисления, исходя из сведений из Datasheet, а так же прямые измерения на конкретных чипах, дают результат примерно: при напряжении питания 5V – 20 Ом для состояния порта «0» и 60 Ом для состояния «1», при напряжении питания 3V – соответственно 30 Ом и 80 Ом. В дальнейших выкладках будем ориентироваться на эти значения.

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

формулу (2) R = (Upp – UledI*N*Rout) / I – Rseg, где N – число включенных разрядов, Rout– выходное «разрядное» сопротивление, Rseg — выходное «сегментное» сопротивление.

Из этой формулы для вычисления тока сегмента для данного ограничительного сопротивления получим

формулу (3) I = (Upp – Uled) / (R + N*Rout+Rseg).

Вычисляя требуемый для тока 3мА резистор по формуле (2), получим: 846 Ом (ближайший – 820 Ом) для индикатора с ОК и 576 Ом (ближайший 560 Ом) для индикатора с ОА. Согласитесь, было бы обидно установить ток сегмента на 30 – 40 % меньше возможного, когда «каждый миллиампер на счету».

Для более низких напряжений питания эта разница еще больше. Так для питания в 3 Вольта для сегментного тока в 3мА по формуле (2) даже для индикатора с ОК имеем R=(3-1.8-8*30*0.003)/0.003-80=80 Ом, тогда как по упрощенной формуле (1) мы бы имели 400 Ом, то есть ток был бы 1.67 мА вместо допустимых 3 мА!!

Полученное значение R=80 Ом и формула (3) говорят о том, что для низких питающих напряжений максимальные токи определяются уже в большей степени Rout, чем R. Это может означать при разном количестве горящих сегментов (разных индицируемых знаках) разные токи через них и разную их яркость. Например, вычислим для Upp=3V и R=80 Om ток через один сегмент для цифр 1 (светятся два сегмента) и 8 с точкой (8 сегментов) для индикатора с ОК по формуле (3):

I(1) = (3-1.8)/(80+2*30+80) = 5.5 mA

I(8.)= (3-1.8)/(80+8*30+80) = 3 mA

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

Для наблюдения разницы яркости при токах через сегмент в 1, 2 и 4 мА, соответствующие выводы резисторов с выходов PIC12F629 были переключены непосредственно на плюсовую шину.

В заключении, коснемся еще одного вопроса, также нередко обсуждаемого на просторах Интернета – схем, где индикатор подключен к микроконтроллеру вообще без токоограничивающих резисторов, напрямую как сегменты, так и разряды. Исходя из формулы (3), кажется, что это в принципе возможно для малых питающих напряжений. Действительно, для Upp=3V и R=0 имеем:

I(1)=(3-1.9)/(2*30+80)=7.9 mA, I(8.)=(3-1.8)/(8*30+80)=3.8 mA для индикатора с ОК.

I(1)=(3-1.9)/(2*80+30)=5.8 mA, I(8.)=(3-1.8)/(8*80+30)=1.8 mA для ОА.

Из формул видно, что для индикатора с ОК суммарный ток разрядов превышает допустимый 3.8*8=30.4 мА, но разность яркости 1 и 8 всего в 2 раза. Для ОА ток вполне в пределах нормы, но вот разница в токах сегментов для цифр 1 и 8 слишком велика – в 3 раза, а значит, визуально 1 будет светиться существенно ярче, чем 8, 9 или 6. Вывод: все таки, резисторы нужны при любом напряжении питания.

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

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

Динамическая индикация. Подключение светодиодной матрицы к микроконтроллеру


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

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

Для получения цельного изображения без мерцаний, переключение необходимо выполнять с большой скоростью, для исключения мерцания светодиодов частоту обновления необходимо устанавливать от 70 Гц и более, я обычно устанавливаю 100 Гц. Для вышерассмотренной конструкции пауза рассчитывается следующим образом: для частоты в 100 Гц период равен 10 мс, всего 4 индикатора, соответственно время свечения каждого индикатора устанавливаем на уровне 10/4=2,5 мс. Существуют многоразрядные семисегментные индикаторы в одном корпусе, в которых одноименные сегменты соединены внутри самого корпуса, естественно для их использования необходимо применять динамическую индикацию.

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

В основной программе сначала настраиваем таймер с помощью регистра OPTION_REG, ранее я рассказывал про использование таймеров для временной задержки. Далее очищаем регистр shet, предназначенный для введения счета от 1 до 4, для каждого индикатора. Этот регистр инкрементируется в подпрограмме обработки прерываний и там же корректируется (он будет считать от 1 до 4), поэтому данная очистка выполняется однократно после включения питания. По этому регистру будем определять, какой индикатор включать и выдавать данные соответствующие ему. Следующим шагом будет очистка регистров хранения информации, четыре регистра dataind1,2,3,4 соответствующие четырем индикаторам. Очистка равнозначна выключению индикаторов, так как в подпрограмме обработки прерываний, содержимое этих регистров передается в регистр PORTB, к которому подключены аноды индикаторов. Это необходимо для того чтобы на индикаторах не высвечивался всякий мусор после разрешения прерываний, в принципе этого можно и не делать, если сразу записывать правильную информацию для вывода. Далее сбрасываем флаг прерывания по переполнению таймера, разрешаем прерывания по переполнению TMR0, и наконец, разрешаем глобальные прерывания.

В подпрограмме обработки прерываний, первым делом выключаем все индикаторы (подав низкие логические уровни на базы транзисторов), потому что неизвестно какой из них включен. Производим инкремент регистра shet, с проверкой на равенство числу 5, при наличии такого совпадения записываем в регистр число 1, так как необходимо вести счет от 1 до 4. Далее проверяем, какое именно число лежит в регистре shet, по которому загружаем в PORTB данные из регистров хранения информации (dataind) для соответствующего индикатора и включаем его. После чего сбрасываем флаг прерывания по переполнению TMR0, записываем число 100 в таймер (ниже приведен расчет этого значения), для временной задержки и выходим из обработчика прерываний. При первом прерывании включается первый индикатор, во втором прерывании второй и так по круговому циклу. В основной программе остается только загружать данные в регистры хранения информации для каждого индикатора. В подпрограмме прерываний не забываем сохранять и восстанавливать значения ключевых регистров, об этом я писал в статье про прерывания.

Читайте также  Пробник на dc-dc регуляторе для поиска короткого замыкания

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

Частоту тактового генератора примем за 4 МГц, машинный цикл 1 мкс. Частота обновления каждого индикатора пускай составит 100 Гц (период T=10 мс), соответственно необходимая временная задержка равна 10/4 = 2,5 мс. Коэффициент предделителя для TMR0 устанавливаем равным 1:16, при этом максимально возможная задержка равна 256х16 = 4096 мкс, а нам требуется пауза в 2,5 мс. Рассчитаем число для записи в TMR0: 256-((256х2,5)/4,096) = 256-156,25 = 99,75. После округления получим число 100.

Ниже можно скачать модель для программы Proteus, прошивку и исходник с реализацией динамической индикации на 4-х разрядный индикатор с общим катодом с применением микроконтроллера PIC16F628A. Для примера на индикатор выводятся числа 0000; 0001; 0002; 13,52; 9764.
Прошивка МК и исходник + файл проекта Proteus_7.7

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

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

Самым распространенным последовательным регистром является микросхема 74НС595, которая содержит в себе сдвиговый регистр для загрузки данных, и регистр хранения через который данные передаются на выходные линии. Загружать данные в него просто, устанавливаем логический 0 на входе тактирования SH_CP, далее устанавливаем требуемый логический уровень на входе данных DS, после чего переключаем вход тактирования в 1, при этом происходит сохранение значения уровня (на входе DS) внутри сдвигового регистра. Одновременно с этим происходит сдвиг данных на один разряд. Снова сбрасываем вывод SH_CP в 0, устанавливаем требуемый уровень на входе DS и поднимаем SH_CP в 1. После полной загрузки сдвигового регистра (8 бит), устанавливаем в 1 вывод ST_CP, в этот момент данные передаются в регистр хранения и поступают на выходные линии Q0…Q7, после чего сбрасываем вывод ST_CP. Во время последовательной загрузки, данные сдвигаются от Q0 до Q7. Вывод Q7’ подключен к последнему разряду сдвигового регистра, этот вывод можно подключить на вход второй микросхемы, таким образом можно загружать данные сразу в две и более микросхемы. Вывод OE переключает выходные линии в третье (высокоомное) состояние, при подаче на него логической 1. Вывод MR предназначен для сброса сдвигового регистра, то есть установка низких логических уровней на выходах триггеров регистра, что эквивалентно загрузке восьми нулей. Ниже представлена диаграмма загрузки данных в микросхему 74НС595, установка значения 11010001 на выходных линиях Q0…Q7, при условии, что изначально там были нули:

Рассмотрим подключение матрицы 8×8 к микроконтроллеру PIC16F628A с помощью двух сдвиговых регистров 74HC595, схема представлена ниже:

Данные загружаются в микросхему DD2 (управление логическими уровнями на строках, аноды светодиодов) затем через вывод Q7’ передаются в DD3 (управление столбцами), соответственно сначала загружаем байт для включения столбца, затем байт с логическими уровнями на строках. К выходным линиям DD3 подключены транзисторы коммутирующие столбцы матрицы (катоды светодиодов). Ниже приведен код программы для вывода изображения на матрицу:

«Бегущая строка» — динамическая индикация на примере светодиодной матрицы RL-M2388 и Arduino Mega

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

Немного теории

В данной светодиодной матрице находится 64 красных светодиода. Если выводить контакты каждого светодиода отдельно, то понадобится 64 контакта на корпусе матрицы и микроконтроллер с 65 цифровыми портами ввода/вывода. Это нецелесообразно и невыгодно. Поэтому на заводе-изготовителе светодиоды объединяют в матрицы различных размеров (в нашем случае 8х8), то есть в 8 групп по строкам и столбцам следующим образом:

В таком случае нам понадобится лишь 16 цифровых вводов/выводов. Например, чтобы зажечь светодиод в левом верхнем углу, нам нужно на pin13 (см. картинку) подать лог 1, а на pin9 лог 0. Такой способ отображения называется статическая индикация.

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

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

Быстро мелькающий объект кажется человеческому глазу постоянно светящимся. Это свойство человеческого глаза – инертность. Как вы догадались, именно на этом свойстве и основан метод вывода информации в светодиодной матрице. Например, чтобы вывести на «экран» некий символ, нужно последовательно, проходя все «пиксели» матрицы с высокой скоростью, включать светодиод в нужном месте.

Условия, которые необходимо соблюдать при программировании матриц динамической индикации:
1. Длительность отображения каждого столбца/строки («пикселя» в моем случае) постоянна, одинакова для всех столбцов/строк («пикселей» в моем случае).
2. Частота смены столбцов/строк («пикселей» в моем случае) не меняется.

Принцип действия прошивки микроконтроллера

Признаюсь, когда у меня появилась отладочная плата на МК Atmega1280 и светодиодная матрица, я и понятия не имел что такое динамическая индикация и для чего она нужна. До понимания необходимости ее использования я дошел методом проб и методом «тыка».

Принцип действия прошивки:

1. Вывод символа на экран

Давайте представим, что наша светодиодная матрица – это двухмерный массив размерностью I на J, совпадающей с размерностью нашей матрицы. В нашем случае это 8 на 8 пикселей. Итак, есть двухмерный массив типа boolean. К примеру:

В цикле мы проверяем, если элемент массива A[i][j]=1, тогда включаем светодиод на матрице, находящийся в позиции (I;J), делаем паузу на отображения светодиода и выключаем светодиод в позиции (I;J). В результате работы программы по такому алгоритму на экране матрицы выведется символ «А» (в двумерном массиве именно этот символ отображен). Назовем этот алгоритм «Вывод на экран». Итак, с выводом информации на экран с помощью динамической индикации разобрались.

2. Прокрутка информации на экране

Конечно, классно выводить на экран неподвижную информацию, но было бы интереснее «оживить» ее – заставить передвигаться. Рассмотрим алгоритм перемещения информации:

В бесконечном цикле выполняем:
1. Вызываем алгоритм «Вывод на экран».
2. Берем 1й столбец матрицы А и записываем его в буфер (двухмерный массив BUFFER[8][1]).
3. Записываем содержимое матрицы с позиции A[i][j+1] в позицию A[i][j]. То есть мы по сути «сдвинули» матрицу влево на один столбец.
4. Записываем в последний столбец матрицы А (в свободный, так как мы сдвинули матрицу влево на один столбец) содержание буфера BUFFER. Назовем этот алгоритм «Прокрутка информации».

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

Подключение матрицы к отладочной плате

Каждый из 16ти выводов матрицы пронумерован. В соответствии этим номерам были подпаяны и выведены 8 контактов, отвечающих за строки, и 8 контактов, отвечающих за столбцы. У меня, например, матрица подключена так:

Выводы, отвечающие за строки 23,25,27,29,31,33,35,37 – это номера выводов ножек микроконтроллера;
Выводы, отвечающие за столбцы 39,41,43,45,47,49,51,53 — это номера выводов ножек микроконтроллера.

Программирование

Программирование и прошивка микроконтроллера производится в среде разработки Arduino IDE.

Что необходимо знать для понимания кода программы, написанного ниже:

Функция pinMode()
Устанавливает режим работы заданного вход/выхода(pin) как входа или как выхода.
pinMode(pin, mode), где pin: номер вход/выхода(pin), который Вы хотите установить; mode: режим одно из двух значение — INPUT или OUTPUT, устанавливает на вход или выход соответственно.

Читайте также  Работаем с жки. часть 1

Функция digitalWrite()
Подает HIGH или LOW значение на цифровой вход/выход (pin).
digitalWrite(pin, value), где pin: номер вход/выхода(pin); value: значение HIGH или LOW

Функция delayMicroseconds()
Останавливает выполнение программы на заданное в параметре количество микросекунд (1 000 000 микросекунд в 1 секунде).
delayMicroseconds(us), где us: количество микросекунд, на которое приостанавливается выполнение программы. (unsigned int)

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

В функции setup() производятся настройки портов ввода/вывода МК, настройка подключенных устройств к МК, периферии МК, а так же выполняется все то, что нужно выполнить ОДИН раз.
В функции loop() пишется тело программы, которое будет выполняться циклически, пока микроконтроллер включен.

Выполним настройку выводов микроконтроллера. Состояний портов ввода/вывода существует два вида: настроен на вход либо на выход. В нашем случае нужно настроить на выход.

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

Напишем функцию, которая реализует алгоритм «Вывод на экран».

Напишем функцию, которая реализует алгоритм «Прокрутка информации».

Теперь приведу полный код программы:

Вот и все. Заливайте эту программу в свой микроконтроллер и наблюдайте, как буква «А» будет «бежать» влево. Добавлю, что массивом размерностью 8 на 8 элементов ограничиваться не стоит. Например, я использовал массив размером 8 на 86. Написал в нем целую фразу, как видно на этом видео.

Динамическая индикация: экономим выводы МК

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

Рассмотрим самый обычный светодиод. Чем-то он похож на обычную лампочку накаливания — если через него пропустить ток, то он начнет излучать свет. На этом сходство заканчивается. У светодиода есть анод и катод, и для того, чтобы он начал излучать свет, необходимо анод подключить к плюсу источника тока, а катод к минусу. Если перепутать полярность, то ни чего страшного не произойдет (кроме отдельных случаев, которые мы сейчас рассматривать не будем), просто светодиод не будет светиться. Это, собственно, и демонстрирует следующая картинка, не первой схеме светодиод не горит, так как неправильно установлена батарейка, на второй это поправили:

Следует обратить внимание на токоограничивающий резистор R1 (или R2). Для чего он нужен, расскажу в одной из следующих статей.

Возьмем теперь восемь светодиодов и соединим их катоды вместе, а аноды от каждого оставим свободными. Или наоборот, аноды светодиодов соединим в одной точке, а катоды трогать не будем. Каждый светодиод обзовем своей буквой, а именно A, B, C, D, E, F, G и DP. Затем, расположим их так, как показано на рисунке и засунем в корпус:

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

Рассмотрим индикатор с общим катодом (Common Cathode). Чтобы вывести, например, цифру «1», нужно зажечь сегменты B и С (см. картинку выше). Для этого общий провод (тот, к которому все светодиоды подключены одним концом) цепляем на минус источника тока, а сегменты B и C на плюс, т.е. сегменты в таком индикаторе зажигаются плюсом источника тока. При этом остальные сегменты можно ни куда не подключать, либо повесить на минус. Таким образом, включая нужные сегменты, можно отобразить любую цифру от 0 до 9, некоторые символы и буквы.

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

Давайте теперь посчитаем ноги. Для управления одним семисегментным индикатором нужно 8 ног микроконтроллера (7 сегментов и 1 десятичная точка). 2 индикатора — 2*8=16 ног, 4 индикатора — 4*8=32. Как-то грустно… Для того, чтобы сделать электронные часы, нужно минимум 4-ре индикатора, а у AVR-ке в большом корпусе DIP-40 как раз 32 порта ввода-вывода. Даже кнопки прицепить некуда. Но выход есть! (нет, не в окно)

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

Все одноименные сегменты соединены вместе, а общие выводы оставлены как есть. А как же теперь управлять этими индикаторами? На просторах интернета нашел 2 отличные gif-ки, которые отображают суть процесса:

То есть, на общем проводе интересующей нас цифре устанавливаем активный уровень (для Common Cathode подключаем к минусу, Common Anode к плюсу) и зажигаем нужные сегменты. Такой тип индикации называется динамический. Возникает вопрос: а как сделать, чтобы горели все цифры, а не только одна, да еще чтоб каждая показывала что-то свое? Ведь одновременно мы уже не модем зажигать все цифры в таком включении. Да, одновременно не можем, но это и не надо. Если мы будем очень быстро переключать цифры, как показано на gif-ках, то для глазу человека будет казаться, что все они горят одновременно! Уже при 25 герцах можно без проблем прочитать все значение целиком. А если сделать частоту 100 Гц, то проблемы вообще не будет.

Посчитаем теперь количество выводов микроконтроллера, которое необходимо задействовать в такой схеме включения индикаторов. На 2-е цифры на мнужно 8+2=10 выводов, 8 для сегментов (7, если нам не нужна десятичная точка) и 2 общих провода от каждой цифры; для 4-х цифр 8+4=12 выводов. То есть каждая дополнительная цифра прибавляет только один дополнительный вывод, а не 8. Промышленностью выпускаются готовые индикаторы на 2, 3, 4 и так далее цифр, имеющие внутри все необходимые соединения для динамической индикации, наружу торчит только то, что надо, самому что-то соединять нет необходимости. Красота!

Правда есть один небольшой минус. Если у нас 4-ре цифры, то каждая будет гореть только 1/4 времени от полного цикла обновления (эдакий ШИМ с заполнением 25%), что негативно сказывается на яркости индикации, поэтому, если стоИт задача получить максимально яркое свечение индикатора, этот способ не прокатит. Устранить эту проблему можно уменьшением номинала токоограничительных резисторов каждой группы сегментов, но тут нельзя выходить за максимальное импульсное значение силы тока через сегмент.

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

  • Исходные условия: в наличии имеем семисегментный индикатор на 4-ре цифры для динамической индикации и микроконтроллер AVR ATMega16.
  • Задача: подключить этот индикатор к AVR-ке, написать управляющую программу, которая будет иметь возможность вывода произвольного числового значения на дисплей.

Поехали. Для начала в Proteus-e нарисуем вот такую схему:

Проще некуда: сегменты подключены к выводам PD0-PD7 микроконтроллера, цифры к PC0-PC3.

Перейдем теперь в Atmel Studio. Создадим новый проект, выберем микроконтроллер atmega16.

Где-нибудь вначале файла напишем define-ы, которые будут определять, куда подключен наш индикатор

Теперь поговорим немного об алгоритме. Я предлагаю следующий. У нас будет функция, которая будет вызываться с частотой 400 Гц. В ней мы будем выполнять следующие действия:

  1. гасим все индикаторы;
  2. выводим на порт, к которому подключены сегменты, необходимое значение, которое должна отображать цифра с номером led_index;
  3. зажигаем цифру led_index;
  4. увеличиваем led_index на единицу;
  5. если led_index стал больше количества цифр индикатора, то обнуляем его.

Таким образом, мы будем перебирать все цифры индикатора с частотой 400 Гц, и так как у нас 4-е цифры, то частота обновление всего дисплея полностью будет составлять 100 Гц.

Напишем необходимые функции

Ну и функция main:

Смотрим результат в симуляторе:

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

Теперь main выглядит так:

Ну и демонстрация работы:

Теперь можно даже так:

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