Экономим выводы для Arduino. Управление сдвиговым регистром 74HC595 по одному проводу
Что не делай на микроконтроллере, все равно, в конечном итоге, получатся часы. А для проектов на Arduino апогеем бесспорно является метеостанция. И вот, обвесив плату всевозможными датчиками, достаточно часто у любителей микроконтроллеров возникает проблема с нехваткой выводов.
Обычно для расширения портов вода/вывода используют сдвиговые регистры типа 74HC595. Но для управления ими требуется целых три вывода! Невероятное расточительство, неправда ли? Ведь можно обойтись всего ОДНИМ! Всех тех, для кого два дополнительных свободных порта являются жизненно необходимыми, приглашаю под кат.
❯ Хорошая идея и плохая реализация от амперки
Совершенно случайно ко мне в руки попала вот такая игрушка — модуль с четырьмя семисегментными индикаторами от амперки. Устройство представляет собой четырех разрядный статический семисегментный дисплей, выполненный на основе сдвиговых регистров 74HC595.
К слову сказать, амперка нередко грешит со схемотехникой, и данный модуль не является исключением. Вероятно, по этому он давно снят с продажи. Ко мне он попал случайно, видимо завалялся в складских остатках, и был отправлен по ошибке.
Интересна схема коммутации входов модуля. Она выполнена с использованием двух RC цепочек на входах тактирования и управления выходной защелкой и позволяет управлять выходами сдвиговых регистров всего по одному проводу вместо трех. И тут кроется пару нюансов, которые разберем далее.
Так как модуль давно не производится, найти оригинальную схемы было непросто. Но срисовывать ее с платы тоже не хотелось. А для анализа было важно иметь точную схему, чтобы не оказаться голословным.
Сразу возникают вопросы к схеме подключения индикаторов. Токоограничивающие резисторы имеют номинал 220Ом, это создает эффект «выжги глаза» и перегружает индикаторы. Такая величина тока могла бы подойти при динамической индикации, но в статике яркость получается слишком высокая. В следующих ревизиях платы резисторы заменили на 510Ом, но для меня и этого много. Я заменил их на 620Ом, при этом ток для сегмента устанавливается примерно 5 мА.
Далее разберемся с подключением сигналов управления. Вход тактирования 11 сдвигового регистра «SHIFR» подключается к порту микроконтроллера напрямую. Вход данных 14 «DATA» подключается к той же линии через RC-цепочку R1C1, время заряда которой составляет примерно 20–25 мкС. Вход управления защелкой 12 «LATCH» подключен через RC-цепочку R2C2, которая заряжается примерно за 250 мкС.
Принцип управления достаточно прост. Если на вход дисплея подать очень короткий импульс около 1 мкС, то RC-цепочки не успевают зарядиться, а так как сдвиговый регистр имеет достаточно высокое быстродействие, то данные в регистр вдвинуться успевают. Таким образом, длительностью импульса можно управлять зарядом конденсаторов и устанавливать необходимый уровень напряжения на входе данных и защелки.
Передача логической единицы производится подачей импульса длительностью примерно 25 мкС и короткой паузы не более 1 мкС. Импульс зарядит конденсатор на входе данных до уровня логической единицы, а короткая пауза не успеет его разрядить. Фронт следующего импульса попадет на тактовый вход и запишет единицу в регистр.
Для записи в сдвиговый регистр логического нуля необходимо, наоборот, сперва подать паузу длиной примерно 30 мкС. Это разрядит конденсатор на входе данных до уровня логического нуля, если он был заряжен в предыдущем периоде. А затем подаем короткий импульс около 1 мкС, чтобы его фронт записал ноль в регистр.
В завершение передачи 24 бит данных для заполнения 4 сдвиговых регистров следует подать импульс длительностью 250 мкС для установки сигнала управления защелкой. Записанные данные поступят на выходы сдвиговых регистров и будут удерживаться там до следующего фронта сигнала «LATCH». Теперь конденсатор следует разрядить, для этого устанавливаем на входе схемы низкий уровень минимум на 250 мкС, прежде чем подавать новые данные.
На этом можно было бы и закончить, если бы не те самые нюансы в работе схемы. Меня насторожил тот факт, что RC цепочки имеют разницу по времени заряда всего на один порядок. Отсюда получается, что конденсатор на входе защелки может полностью зарядиться, если подать 10 единиц подряд. А с учетом того, что уровень логической единицы для 74НС595 начинается с напряжения 3,15В, то для полного заряда конденсатора достаточно подряд подать около 6 логических единиц.
В схеме использованы индикаторы с общим анодом, для отключения сегментов которых на выходе регистра необходимо записать единицы. Получается, что при включении на индикаторе цифры 1 необходимо передать двоичный код 11001111, который как раз и содержит 6 единиц.
Если включать на семисегментном дисплее две единицы подряд, то как раз и получим шесть импульсов подряд, передающих логические единицы. Такая комбинация успевает зарядить конденсатор на входе защелки «LATCH». В результате чего, в момент обновления дисплея наблюдается кратковременное «подмигивание».
На графиках видно, что сигнал «LATCH» на входе управления защелкой сдвигового регистра уже после передачи первого семисегментного кода единицы заряжается практически до уровня логической единицы. В итоге, после каждого такого цикла передачи кода для одного регистра, весь дисплей обновляется. Это приводит к кратковременному появлению «мусора» — семисегментный код попадает не на свои позиции.
Если подобная схема управляет индикаторами, то кратковременное «подмигивание» дисплея может быть некритичным. Но вот для управления реле или чем-то подобным это явно не годится.
Как только я не пытался сократить длительности импульсов высокого уровня и увеличить паузы, полностью устранить этот глюк не получилось.
Проблему решил заменой резистора R2 с 33КОм на 100КОм. Время заряда RC-цепочки увеличилось в несколько раз. Индикатор стал работать лучше. Но все равно разницы между временем заряда конденсаторов на входе данных и защелки было недостаточным для вывода на дисплей четырех единиц. Да и включить при такой схеме больше четырех сдвиговых регистров подряд не получится, т.к. конденсатор С2 все равно может успеть зарядиться до уровня логической единицы.
❯ Доработка схемы
Для окончательной доработки схемы нужно добавить один, а лучше два диода Шоттки. Это ускорит разряда конденсатора С2 и заряда С1. Также эта доработка сокращает общее время, необходимое для обновления дисплея. Резисторы R3R4 добавлены для ограничения разрядного тока конденсаторов.
Если сравнить результаты работы схемы с диодами (левый график) и без них (правый график), становиться видно, что фронты импульсов на входе данных «DATA» и срез импульса на входе управления защелкой «LATCH» стали намного круче. И, самое главное, что конденсатор C2 на входе управления защелкой успевает разрядиться за короткий импульс записи. Теперь можно ни в чем себе не отказывать и подключать практически любое количество сдвиговых регистров.
Текст тестовой программы писал в CodeVisionAVR. Это отличный компилятор, хорошо работает даже на очень слабых компьютерах. Умеет напрямую грузить прошивку в Arduino. Имеет удобный мастер для автоматической настройки периферии, а также большое количество готовых библиотек для разных датчиков, дисплеев и прочего. Работа с портами ввода/вывода производилась напрямую через регистры, это в последствии добавило еще немного возни. В железе просто доработал модуль, немного порезал дорожки, добавил недостающие компоненты и перемычки.
❯ Версия для Arduino
Попытка повторить тоже самое в Arduino IDE привела к краху. Функция digitalWrite работает с портами очень медленно, и добиться паузы меньше 1 мкС между импульсами, сохраняя парадигму arduino-программирования, попросту не возможно. Лучшее время составляет 4 мкС. При этом конденсатор С1 успевает разрядиться до самой границы логической единицы, и стабильность схемы все равно теряется.
Итоговую схему собрал в Proteus 8.6. Библиотеку с моделями Arduino нашел на GitHub. Не самая удобная библиотека, контакты виртуальной модели не попадают в сетку. И пришлось по новой подбирать номиналы схемы, при которых работа регистров будет стабильной.
Текст программы получился следующий. Для управления регистрами по одному проводу можно использовать функцию «shiftRegisterEntry», ее входные параметры пояснены в комментариях.
//Выход для управления индикацией
#define SERIAL_DATA_PORT 12
//Массив для хранения семисегментных кодов
#define MAX_DISPL 4
uint8_t displ[MAX_DISPL]=
//семисегментные коды цифр 1,2,3,4
{0b10011001, 0b00001101, 0b00100101, 0b10011111};
//Функция для управления сдвиговым регистром по одному проводу
//uint8_t *data - указатель на массив с данными,
// которые выводятся в регистр
//uint8_t amount - количество байт для записи в регистр
void shiftRegisterEntry(uint8_t *data, uint8_t amount){
//перебираем байты, которые нужно записать в регистры
for(uint8_t num = 0; num < amount; ++num)
//записываем отдельные биты из каждого байта
for(uint8_t bitNum = 0; bitNum < 8; ++bitNum){
//график сигнала для записи логической единицы
if(bit_is_set(data[num],bitNum)){
digitalWrite(SERIAL_DATA_PORT, HIGH);
delayMicroseconds(30);
digitalWrite(SERIAL_DATA_PORT, LOW);
digitalWrite(SERIAL_DATA_PORT, HIGH);
}
//график сигнала для записи логического нуля
else{
digitalWrite(SERIAL_DATA_PORT, HIGH);
digitalWrite(SERIAL_DATA_PORT, LOW);
delayMicroseconds(100);
digitalWrite(SERIAL_DATA_PORT, HIGH);
}
}
//формируем сигнал переключения защелки
delayMicroseconds(500);
digitalWrite(SERIAL_DATA_PORT, LOW);
delayMicroseconds(30);
}
void setup() {
//настройка выхода для управления регистром
pinMode(SERIAL_DATA_PORT, OUTPUT);
}
void loop() {
//обновляем дисплей с интервалом 100мС
shiftRegisterEntry(displ, MAX_DISPL);
delay(100);
}
Заряд конденсатора С1 производится через открытый диод D1 и резистор R3. Это обеспечивает достаточное смещение между тактовым сигналом «SHIFT» и нарастанием уровня на входе «DATA», чтобы произвести запись в регистр логического нуля.
Разряд конденсатора С1 производится через резистор R1. Его сопротивление значительно выше, и конденсатор не успевает разрядиться до границы логической единицы, если импульс записи производить с помощью функции «digitalWrite». Но и время полного разряда тоже значительно возрастает.
❯ Заключение
Среднее время заполнения четырех сдвиговых регистров по одному проводу на Arduino составило 3 мС. Это достаточно большое время по меркам микроконтроллера. Но если не требуется часто обновлять выходы, то данный способ может оказаться вполне приемлемым.
Не могу сказать, что я готов применить данный способ управления в каком-то своем реальном проекте, но для разминки мозгов вполне подойдет. Хотя, если делать что-то на tiny13 или подобном микроконтроллере, писать программу на С и обращаться к регистрам портов напрямую, время записи регистров сократиться примерно в 4 раза…, но что-то сильно много «если».