Пусть и не так часто, в настоящее время, но иногда все таки возникает необходимость микроконтроллером управлять большим количеством единичных светодиодов. При этом приходится или использовать большое количество выводов, подключая светодиоды напрямую, или использовать внешние регистры, обычно сдвиговые, например, популярный 74HC595.
При этом давно существуют решения, позволяющие обходиться меньшим количеством выводов микроконтроллеров. К сожалению, в любительских конструкциях их применяют не так часто.
Сегодня я рассмотрю одно из таких решений, которое было описано в документе TB029 "Complementary LED Drive" (Jean-Claude Rebic, Pioneer-Standard) опубликованным 14 марта 2004 года на сайте Microchip.
Это же решение можно найти и в другом документе AN234 "Hardware Techniques for PICmicro Microcontrollers" (Joseph Julicher, Microchip Technology Inc). Дата публикации та же.
Я практически уверен, что этот метод многим известен. Но все таки не всем. И новички могут о нем даже не догадываться.
Поскольку я знаю, что многие (и зря!) не любят 8-битные микроконтроллеры PIC, в статье будет использоваться микроконтроллер STM8S003F3P6 (STM8S103F3P6), как один из самых дешевых среди STM8. Однако, микроконтроллер может быть вообще любым, метод универсальный.
Как это работает. Схемотехника.
В основе метода лежит встречно-параллельное включение светодиодов. При таком включении мы можем управлять выбором включенного светодиода изменяя полярность. И для управления двумя светодиодами будет достаточно одного вывода микроконтроллера.
В таком включении гореть может лишь один светодиод в каждый момент времени. Однако, переключая уровни на выводе можно получить и одновременное свечение обоих светодиодов. Такая "динамическая индикация".
Такие вот пары встречно-параллельно включенных светодиодов можно подключать и непосредственно между выводами микроконтроллера. А это и позволит экономить выводы. Количество светодиодов, которыми можно таким способом управлять определяется удвоенным количеством сочетаний по 2 из N (количество выводов). Что бы вам не пришлось вспоминать формулу для числа сочетаний и выполнять расчет, в документе TB029 приводится вот такая таблица
В обоих документах приводится пример управления 12 светодиодами с помощью 4 выводов микроконтроллера. Я не буду отступать от традиции, поэтому использую подключение 12 светодиодов к STM8.
Теперь давайте подробнее рассмотрим, как в этом случае производится управление светодиодами.
Предположим, что нам надо зажечь HL6. Для этого необходимо установить на выводе PD4 высокий уровень напряжения (логическую 1), а на выводе PD2 низкий (логический 0). Но возникает вопрос, какие уровни должны быть на остальных выводах?
Для примера рассмотрим вывод PD3. Если на нем установить высокий уровень, то кроме нужного нам HL6 будет гореть и HL12. А если низкий, то лишним будет HL2.
Единственным правильным вариантом будет перевод PD3 в третье состояние, или состояние цифрового входа. Но будет ли этого достаточно? Да, будет. Так как два последовательно включенных светодиода HL2 и HL12, которые подключены параллельно HL6, светиться не будут. Просто напряжение будет недостаточным.
Итак, мы теперь знаем все требования для успешного использования такого метода управления светодиодами:
- Выводы микроконтроллера обязательно должны быть двухтактными, так как ток может быть и втекающим, и вытекающим.
- Выводы микроконтроллера должны иметь возможность переключаться и в режим цифрового выхода, и в режим цифрового входа.
- Подтягивающие резисторы выводов, в режиме цифрового входа, должны быть отключены. Это менее очевидное требование. Дело в том, что подтягивающие резисторы создают пути тока, который будет подсвечивать лишние светодиоды, которые гореть в данный момент не должны.
Есть еще один момент, который не сразу бросается в глаза. Дело в том, что каждый светодиод подключен к микроконтроллеру через два резистора. Так для нашего HL6 это резисторы R1 и R3. Поэтому сопротивление токозадающих резисторов должно быть в два раза меньше расчетного.
Точно так же, как для ранее рассмотренного примера с двумя светодиодами, в каждый момент времени может гореть лишь один светодиод из 12. Если нужно больше, придется использовать динамическую индикацию.
Как это работает. Программа.
Управление светодиодами при такой схеме подключения сложнее. Но примерно сравнимо с использованием дополнительных сдвиговых регистров.
Сначала нам необходима таблица декодирования, для преобразования номера горящего светодиода в данные записываемые в регистры управления портом ввода-вывода. Точнее, две таблицы, так как нужно управлять и направлением передачи (для перевода в 3-е состояние), и уровнями на выводах
Таблица tbl_ddr составлена исходя из работы регистров DDR в STM8, где 0 соответствует работе вывода как вход, а 1 как выход. При этом обе таблицы составлены исходя из того, что светодиоды подключены к младшей тетраде порта. Поскольку у нас это не так, мы будем использовать дополнительные операции.
Для других микроконтроллеров управление направление передачи информации может быть иным, что повлияет на tbl_ddr, но не отразится на tbl_port. Например, для PIC 1 будет соответствовать работе вывода как вход, а не как выход.
В STM8 нас поджидает еще одна сложность, связанная с управлением резисторами подтяжки. Дело в том, что на работу порта оказывают влияние еще два управляющих регистра: CR1 и CR2.
Если вывод порта работает как вход, то есть DDRn=0, отключенным подтягивающим резисторам соответствую два состояния управляющих регистров. Первый, CR1n=0 и CR2n=0, дополнительно запрещает прерывания при изменении состояния вывода. Второй, CR1n=0 и CR2n=1, разрешает прерывания.
Проблема в том, что при работе вывода как выход, то есть DDRn=1, оба этих состояния управляющих регистров настраивают вывод как открытый сток. А это нам совершенно не подходит.
К счастью, обойти проблему не столь сложно. Мы можем просто установить CR2n=0, а CR1n переключать, сбрасывая при необходимости переключения в режим входа, и устанавливая при необходимости переключения в режим выхода. То есть, мы можем просто считать, что CR1n=DDRn.
Правда при этом наш вывод будет работать как выход с ограничением скорости нарастания (ограничение частоты), но для нас это не будет иметь значения, в данном случае.
Теперь мы можем написать собственно программу нашего "драйвера"
Этот фрагмент кода, возможно, требует некоторых пояснений. Предполагается, что он будет компилироваться с помощью sdcc версии не ниже 4.0
Я не использую SPL. Просто она мне не нравится. Я предпочитаю описывать аппаратные регистры микроконтроллера так, как мне нравится. И как больше подходит для конкретной задачи. Поэтому вручную описал регистры порта PORT_D, к которому мы подключили светодиоды.
Для описания регистров я использовал подход, который описывал в статье "Микроконтроллеры для начинающих. Часть 34. Мост к Си. Описываем регистры аппаратуры". В данном случае я выделил те биты порта, к которым подключены светодиоды. Это позволило полностью избежать ручных операция маскирования и сдвига. Все необходимое компилятор сделает сам.
Так же, я сразу связал переменные, которые мы используем для управления светодиодами, с аппаратными регистрами порта указав их адреса.
Все вместе это сэкономило нам и время, и силы, на написание кода. Кроме того, в качестве побочного эффекта, позволило сэкономить один байт в памяти данных, где размещалась бы неизбежная временная переменная, при ручных логических операциях. Компилятор прекрасно обходится без этой переменной используя лишь аккумулятор. Объем кода при всем этом получается примерно такой же.
Процедура инициализации просто переводит все выводы в состояние входов с отключенными подтягивающими регистрами и прерываниями. И устанавливает на них низкий уровень напряжения (точнее, нулевой состояние бит в регистре ODR), при котором ни один светодиод гореть не может.
Процедура зажигания светодиода тоже очень простая. Сначала мы гасим светодиоды установив на выводах низкий уровень напряжения. После чего устанавливаем требуемое направление работы каждого вывода. И отключаем подтягивающие резисторы, или переключаем вывод в режим двухтактного выхода, как я выше описывал, с помощью регистра CR1. И, наконец, устанавливаем на выводах требуемые уровни напряжения.
Одновременное включение более одного светодиода
В данном случае, это не требует никакого изменения схемы. Просто надо использовать динамическую индикацию. Лучше всего для этого использовать прерывания от таймера и вызывать LedShow для включения очередного светодиода из группы одновременно горящих.
С учетом того, что цикл динамической индикации не должен превышать 20 мс, что бы не было заметно мерцания, можно определить период таймера для заданного количества светодиодов, которые могут гореть одновременно. Например, если у на одновременно может гореть до 5 светодиодов, то период таймера не должен превышать 4 мс.
Заключение
Как видно, управлять большим количеством светодиодов используя минимум выводов микроконтроллера не так и сложно. Как с точки зрения схемотехники, так и с точки зрения программирования. При этом метод имеет и ограничения, как это обычно и бывает.
Во первых, необходимо использовать именно отдельные светодиоды. Метод не применим для 7-сегментных индикаторов, например. Во вторых, чем больше светодиодов могут гореть одновременно, тем больше затраты (времени, машинных тактов) на реализацию динамической индикации. В третьих, сложно управлять яркостью свечения светодиодов.
Сложность управления яркостью объясняется тем, что здесь нет общего вывода светодиодов, на котором можно изменять напряжение используя, например, ШИМ. Можно реализовать программную ШИМ, введя циклы, когда все светодиоды погашены, в процедуру динамической индикации. Но глубина регулирования будет не большой, а количество ступеней малым.