Pull to refresh

Comments 37

"выбор еще больше ограничен."

- а если так - "выбор еще меньше"

Огромное спасибо автору за приятные ностальгические воспоминания о конце 80-х — начале 90-х, когда приходилось решать подобные проблемы. Да и вообще подача материала и стиль изложения заслуживает уважения.
Но… Вы меня простите, но вынужден выступить с резкой критикой исходника обработчика прерывания таймера в примере программы для динамической индикации. Использование в обработчике прерывания функции digitalWrite(), да еще и многократное — это самое настоящее преступление. То же касается и shiftOut(), которая внутри использует digitalWrite(). Посмотрите на исходник digitalWrite() чтобы раз и навсегда понять, что ей не место в обработчике прерывания, особенно для AVR с его примитивной подсистемой прерываний.

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

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

Эта функция - совместимая оболочка в ардуине для записи бита в порт. Ее имплементации очень различны для разных архитектур. Посмотрите для интереса на avr, stm8, stm32( тут тоже разные для m0,m4, m7) esp xtensa, esp risc-v и т.д.

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

Бессмысленная оптимизация . Программа справляется с поставленной задачей? Если да - то больше ничего делать не надо. Если нет то можно и пооптимизировать. В данном случае выигрыш будет копеечный. И я думаю что автор статьи тоже умеет писать в порт напрямую на AVR -ке. Просто не посчитал нужным и я бы не стал придираться

Автор, безусловно, умеет работать напрямую с регистрами, об этом говорит хотя бы операция инициализации таймера. Да и другие статьи автора. И именно поэтому у меня этот кусок кода и вызвал резкий диссонанс. Если бы статью написал типичный «ардуинщик», то претензий бы не было (я бы и читать ее не стал дальше первого абзаца).
Кстати, насчет «справляется с поставленной задачей» — у меня нет уверенности, что при такой реализации полностью отсутствует паразитная подсветка погашенных сегментов. Я бы сделал так: сначала снимается напряжение с анодов индикаторов, затем выполняются все манипуляции с сегментами, и только потом подается питание на анод активного разряда.
Согласен, но уж больно понравилась сама статья, чтобы обратить внимание даже на мелочи. К сожалению, это сейчас случается не часто.

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

Удивился странной претензии. Вы времена выполнения операций считать умеете? Во-первых, выполнение сдвига через ShiftOut(), специально мной промеренное, занимает 120 мкс, там написано. Что примерно 3% от времени горения разряда. Во-вторых, разряд зажигается ПОСЛЕ сдвига и переписывания в защелку. Разница в задержках порядка времени выполнения одной-двух digitaWrite(), т.е. где-то 13-25 мкс, максимум 0,6% от времени горения. Это скажется хоть как-то на яркостях? Зачем напрямую писать в порты, когда все это нас устраивает выше крыши? Я вообще всегда по возможности привык применять штатные средства, если к этому нет прямых противопоказаний.

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

Вы времена выполнения операций считать умеете?
Умею, и считаю, когда пишу критические по времени выполнения участки кода, к которым относятся обработчики прерываний.
Во-первых, выполнение сдвига через ShiftOut(), специально мной промеренное, занимает 120 мкс, там написано. Что примерно 3% от времени горения разряда.
Да бог с ним, с временем горения. Этот интервал входит в общее время обработки прерывания, когда процессор не реагирует на другие прерывания. Это же AVR.
Во-вторых, разряд зажигается ПОСЛЕ сдвига и переписывания в защелку.
Но и предыдущий разряд гасится ПОСЛЕ сдвига, а надо бы его погасить ДО, во избежание паразитных засветок.
Разница в задержках порядка времени выполнения одной-двух digitaWrite(), т.е. где-то 13-25 мкс, максимум 0,6% от времени горения. Это скажется хоть как-то на яркостях?
На яркости нет, собственный разброс яркости разных сегментов индикатора намного больше. А вот на потере данных с какого-нибудь быстрого интерфейса из-за несвоевременной обработки его прерываний — вполне.
Я вообще всегда по возможности привык применять штатные средства, если к этому нет прямых противопоказаний.
Смотря что считать «штатными средствами». Если это стандартная библиотека или библиотека от производителя — то да. Библиотеку Ардуино я бы к ним относить не стал. А противопоказания в данном случае — затягивание времени обработки прерывания.
Напрямую в порты я пишу в ассемблерных версиях тех же программ. Там все просчитать, включая и не показанный здесь обмен данными, можно намного точнее и, конечно, никакие digitaWrite() там не уместны.
Прямая запись в регистр на Ассемблере и на C не особо отличается, если конечно адресоваться напрямую к порту, а не как это принято в Ардуино к некоей лишней сущности в виде «номера пина». Что касается digitaWrite(), то на мой взгляд она нигде не уместна, ну а уж в обработчике прерывания — безусловно.

Замучали, честно.

Этот интервал входит в общее время обработки прерывания, когда процессор не реагирует на другие прерывания.

И что из того, если мы тут больше ничего не делаем, а время это занимает доли процента от промежутка между прерываниями? Чему мы можем навредить? Я вообще-то между прерываниями еще собираюсь данные получать по уарту. Уарт в сотню раз дольше, чем все эти переключения, как они могут на него повлиять, если он даже и отложится на пару десятков микросекунд? Народ вообще переоценивает влияние этой фичи AVR, обычно то, что процессор затормаживается, приносит одни только удобства. Тут просто надо все внимательно считать, и никаких проблем не будет, хоть с ардуиной, хоть напрямую.

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

Но и предыдущий разряд гасится ПОСЛЕ сдвига, а надо бы его погасить ДО, во избежание паразитных засветок.

Откуда паразитные засветки, если все хранится в защелке? И до этого горел предыдущий разряд с предыдущим содержимым защелки?

Ну а это уже чисто ваши личные принципы:

если конечно адресоваться напрямую к порту, а не как это принято в Ардуино к некоей лишней сущности в виде «номера пина». Что касается digitaWrite(), то на мой взгляд она нигде не уместна, ну а уж в обработчике прерывания — безусловно.

Я тоже в чем-то перфекционист, но все-таки по делу, а принципы оставляю фанатикам. Есть конкретная задача, под нее и принимаем конкретные решения.

Замучали, честно.
Будьте великодушны, простите меня.
И что из того, если мы тут больше ничего не делаем ...
Это так, пока это демонстрационная программа. В реальной программе придется параллельно решать и другие задачи. Вы же в статье пишете:
Далее я предлагаю универсальное решение для любых типоразмеров индикаторов.
Я это трактую, как претензии на некое универсальное решение, которое в дальнейшем будет использоваться в реальных задачах. Хотя, возможно, я неправильно понял и Вы предлагаете только аппаратное решение, а код пишете только для демонстрации его работоспособности. В таком случае примите мои извинения за ненужную дискуссию, на которую все мы потратили время. Но боюсь, что кто-то еще, но имеющий меньший опыт и потому менее критический подход, может воспринять информацию в статье как прямое руководство к действию (а аппаратное решение Вы действительно подали и разжевали прекрасно и это имеет полное право быть руководством к действию).
«Потеря данных с какого-нибудь быстрого интерфейса» при применении прерываний — этот миф...
Это не миф, а вполне реальная проблема. И чем дольше мы находимся в обработчиках прерываний, тем больше шансов с ней столкнуться. Да и помимо обслуживания интерфейсов есть другие критичные ко времени задачи, хотя бы тот же «ногодрыг».
Откуда паразитные засветки, если все хранится в защелке? И до этого горел предыдущий разряд с предыдущим содержимым защелки?
Засветка будет как раз если наоборот до этого предыдущий разряд не горел, а следующий горит. Давайте для понимания все предельно упростим: будем рассматривать только два разряда и всего один сегмент в ситуации, когда в первом разряде он не горит, а во втором горит. При переключении от первого ко второму разряду у Вас, при запитаном аноде первого разряда, сначала подается состояние для второго разряда на катод (включенное), и только потом происходит коммутация анодов. Да, это достаточно короткий импульс, чтобы перепутать погашенное и включенное состояние сегмента, но все же достаточный, чтобы погашенный сегмент слегка подсвечивался. И главное, чтобы этого избежать не нужно никакого усложнения программы, достаточно всего лишь соблюсти временную последовательность:
  1. отключение анода первого разряда
  2. изменения состояния сигналов сегментов (катодов) от состояния для первого разряда на состояние для второго разряда
  3. включение анода второго разряда

Ну а это уже чисто ваши личные принципы:

Я тоже в чем-то перфекционист, но все-таки по делу, а принципы оставляю фанатикам. Есть конкретная задача, под нее и принимаем конкретные решения.
Здесь предлагаю остаться каждому со своим мнением и уважением чужого, иначе похоже дискуссия может затянуться до бесконечности.

Serge78rus, я вас понял, спасибо. Что касается последовательности включения-выключения, теоретически вы правы, необходимо было сделать так, как вы говорите. Но практике перебой в доли процента от длительности периода совершенно не приведет к какой-либо засветке, это проверено многолетним опытом. И еще вы правы в том, что это демо-программа для иллюстрации аппаратной части. Макетирование, если угодно, не более того. Но пусть кто-то, читающий эти комментарии, учтет момент, на который вы указали.

Но вот в чем я попрежнему останусть на своих позициях, это в вопросе работы по прерываниям. Я имел дело с AVR с момента их появления, около 1997 года. И мы задолго до появления всяких навороченных студий старались писать код так, чтобы он выполнялся именно в прерываниях. Основной цикл по возможности оставляли пустым или выполняли в нем какие-то второстпенные действия, которые можно отложить. Тот самый diHalt назвал это в разговоре со мной бесплатной многозадачностью. На мой вкус, уж извините, выполнение основной задачи в главном цикле - моветон, надо все делать в прерываниях (и считать времена удобнее, и вклиниться ничего не может). Благо именно AVR отлично приспособлены к такому порядку работы. Про Arduino тут речи нет, это просто удобное средство для быстрой проверки принципов работы, как в данном случае с семисегментниками. Хотя простые проекты можно выполнять и на Arduino, удобно, быстро и дешево.

Но чего я не пытался делать на AVR - работать с большим потоком данных, идущих подряд (звук, например). Вот в таких задачах да, можно что-то потерять, как вы говорите. Но к этому AVR совершенно не приспособлены, они банально медленные, и попытки, скажем, воспроизведения звука через ШИМ дают крайне убогий результат. А в большинстве задач автоматики, управления или обработки данных с датчиков совершенно неважно, отложится прием пакета данных по UART на десяток микросекунд или нет. И если все-таки надо дрыгнуть ногой в точно определенный момент (вот как вы там описывали про поорядок переключения разрядов - на семисегментнике это не скажется, но может сказаться, например, при каком-то синхронном обмене), то всегда можно организовать временной поток так, чтобы первостепенные задачи выполнялись вовремя. В конце концов, там можно организовать и вложенные прерывания, никто не запрещает. Ну а если все-таки нельзя, то AVR попросту не годятся, повторяю, сейчас есть уже много чего более быстрого и производительного.

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

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

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

Не выйдет. Догадываетесь, почему? Потому что у красных-желтых LED при токах в десятки мА падение до 2 с лишним вольт, у синих - до 3. А у обычных диодов максимум 0,9-1 вольт, даже у мощных выпрямительных - до полутора. Это все никак не скажется при питании 12-15 вольт (да и сама разница между точкой и сегментом тоже будет незаметна), а вот при питании 6-7 В будет причиной разнобоя в яркостях.

Можно воткнуть светодиод.

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

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

Записать в порт сначала только точку (если ее нужно засвечивать) (или не точку в противоположность , если она наоборот слишком яркая, т.е остальные нужные сегменты) для включения "дополнительного" света на "регулируемое" время, а потом полностью все нужные (до конца времени засветки индикатора).

А можно не заниматься изобретанием велосипедов, а просто поставить копеечную PCA9745

И использовать аппаратный SPI в МК, может останавливать цена 200 р/шт и отсутствие в России, но она вполне доставаема.

В ТМ1637 интерфейс не І2С а похожий на него безадресный интерфейс.

Ага, налетел однажды на эту особенность.

MBI5167

Так какой китайский аналог то смотреть?
А то все эти преимущества всё равно разбиваются ценой 595 с резисторами

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

Поэтому пока остаюсь на полубестолковой 1637, и все руки не доходят до переделки на OneWire.

мы вроде бы выходим за пределы максимально допустимого тока 30 мА. Но все станет на свои места, если вспомнить, что это максимально допустимый средний ток, а усредненная величина в зависимости от коэффициента заполнения не меняется. Но и пиковый ток, разумеется, ограничен,

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

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

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

0 1 2 3 4 5 6 7 8 9 A b C d E F. Ну и да, ещё минус, градус

А если всё таки поизощряться, то ещё больше: 0 1 2 3 4 5 6 7 8 9 A b C d E F G h I J L n o P q r S t u Y

Спасибо за статью! Есть идеи как управлять установкой тока нескольких драйверов в статическом режиме при помощи одного потенциометра?

А как вы это себе представляете? Там входы Rext между собой нельзя соединять, т.е. переменники должны быть раздельными. Объединять можно только линии управления, но и это не так просто. Можно, например, взять цифровые потенциометры и управлять ими через один интерфейс. Теоретически еще, наверное, можно что-то замутить с токовыми зеркалами, но не думаю, что получится хорошо и с полпинка. Если придумаете еще что-то, пишите.

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

А чем Вас не устраивает динамическая регулировка ( PWM ) ?

У меня "аллергия" на "видимый" ШИМ (освещение и диспеи, но не двигатели и нагревали). Для щитовых показометров, на которые нужно сомтреть 30 секунд в месяц это не критично, но у меня тут завалялся проект настольных часов, которые почти все время в области видимости. Вот в таких часах или ШИМ на несколько кГц или статика, причем статика лучше, как минимум психологически. Обычно семисегментные индикаторы я делаю на max7219, он не плох, но не идеален - для крупных семисегментников без доп. обвяза не подходит, частота в пару-тройку раз ниже, чем мне бы хотелось. Вот и заинтересовался таким подходом, но нужна регулировка яркости, как минимум дневной и ночной режимы.

Можно регулировать общее, относительно высокое напряжение с большим постоянным резистором (источник тока).

Можно использовать TFT LCD с добавочным светофильтром

Распять из дискретных ледов

Sign up to leave a comment.

Articles