«Если не знаешь, с чего начать, начни сначала, скорее всего, не ошибешься».
Итак, вначале было (нет не слово, а много слов) тема на одном из сайтов, посвященном любительскому проектированию электроники и программированию (кто сказал «Ардуино» — выйти из класса), называть его не буду, поскольку данная тема могла быть поднята на любом сайте такого профиля и посвящена была медленной работе некоей библиотеки (не будем показывать пальцем, хотя это был Слоненок, а именно LiquidDisplayI2C) на определенной аппаратной платформе. Тема показалось интересной (мои постоянные читатели уже поняли, что быстродействие — это мой пунктик) и, поскольку обсуждение ее (как часто бывает на подобных ресурсах) быстро превратилось в бросание бананами и выяснением, кто на самом деле крутой мачо, а кто так, на минутку зашел, и техническая сторона превратилась в исчезающе малую величину, было предпринято самостоятельное исследование данного вопроса, в процессе которого выяснилось несколько интересных обстоятельств, которые и предлагаются вниманию уважаемой публики.
Уточним исследуемую задачу — требуется наладить взаимодействие с устройством, использующим параллельный интерфейс (в данном случае это был индикатор 1602, хотя это могло бы быть и другое устройство), задействовав как можно меньше ножек МК. Поскольку взаимодействие через 0 (ноль) ножек мне представляется слабо реализуемым подходом, то необходимым минимумом следует признать 1(одну) ножку.
Такие реализации имеют место, достаточно вспомнить WS2181, но, поскольку единственный разумно реализуемый способ кодирования информации представляет собой манипуляцию временем, быстродействие данного подхода будет далеко от идеала. Мы знаем многочисленные успешные реализации данного подхода, среди которых можно особо отметить интерфейсы USB и Ethernet — быстрые однопроводные интерфейсы, но едва ли нам удастся использовать их с минимальными затратами и относительно простой в аппаратном плане, но сильно медленный 1wire.
Ситуация коренным образом меняется, если мы начинаем использовать 2 (две) ножки, что позволяет нам использовать классическую схему с разделением синхросигнала и данных, что позволяет повысить быстродействие без существенных аппаратных затрат. Реальными кандидатами являются SPI в режиме передачи и I2C. К сожалению, в реальной жизни первый не является двухпроводным, и настоятельно требуется еще один или более пинов для выбора конкретного устройства, либо мы должны применять каскадирование устройств с соответствующим снижением пропускной способности канала. Кроме того, SPI не является каналом с гарантированной доставкой на аппаратном уровне, что не всегда (точнее, всегда не) удобно. В этом плане второй кандидат предпочтительнее, поскольку он имеет встроенные средства адресации и контроля передачи. Единственный недостаток — меньшая скорость передачи (100 кГц либо 400 кГц в быстром режиме против единиц и даже десятков МГц ), но за все в этом мире надо платить. К тому же, учитывая особенности подключаемого устройства, для визуализации информации быстродействия даже в 100 кГц ~ 10 килобайт в секунду должно хватить (вполне сравнимо со скоростью 9600, которая вполне обеспечивала достаточно быструю картинку на дисплее 24*80). Оба интерфейса входят в стандартный набор почти всех МК, если по каким-то причинам мы не можем задействовать аппаратные ресурсы, то они легко реализуются на обычных пинах, опять таки при определенном снижении быстродействия либо увеличению нагрузки на процессор. Так что выбор интерфейса I2C можно считать вполне оправданным и разумным для нашей задачи.
Далее встает вопрос выбора микросхемы преобразователя, поскольку с обоих сторон интерфейсы зафиксированы, нам потребуется так называемый расширитель порта I2C, такую микросхему предоставляет NXP и называется она PCF8574. Мсх доступная по цене, широко представлена на рынке и на ее основе уже построена плата для сопряжения именно с интерфейсом дисплея с учетом конструктивных особенностей устройства, так что и этот выбор вполне оправдан, есть одна особенность, но об этом чуть позже.
А пока хотел бы поведать миру о первой неожиданности, связанной именно с выбором микросхемы. Дело в том, что помимо указанной фирмы, аналогичную мсх делает и фирма TI, причем с таким же обозначением. Более того, раскладка ножек по крайней мере для трех типов корпусов (DIP16, SO16, SSOP20) у обоих фирм идентична. Вроде бы, все хорошо, всегда приятно иметь второго поставщика, но дьявол прячется в деталях и обратим внимание на них. Для начала посмотрим документацию на микросхему от NXP, где все ясно и понятно написано, алгоритм работы последовательного интерфейса полностью соответствует стандарту. Обратим внимание на то, что мы можем многократно записывать данные в рамках одной сессии связи, что нам весьма пригодится впоследствии. Обратим еще внимание на страницу 14 описания, где нам указаны способы повышения быстродействия путем миграции на другие типы микросхем (именно эта имеет ограничение в 100 кГц). Резюмируя, спасибо фирме за такую внятную и исчерпывающую документацию.
Ну а теперь о грустном. Открываем документацию на микросхему производства TI и все завертелось… Вообще то я пошел туда для того, чтобы прояснить непонятки с максимально возможной частотой обмена, поскольку сообщения на форуме о том, что можно поднять частоту обмена до 400 кгГц и даже до 800 кГц, с примечанием, что на самом деле она была 640 кГц, а вот 1мГц почему то не заработал, меня несколько насторожили, так как в документации от NXP была четко указана максимальная частота обмена 100 кГц, а я всегда с большим неодобрением отношусь к попыткам выйти за рамки ТУ, ну такая у меня привычка. Поэтому предположил, что TI вариант может быть быстрее. Оказалось, что это не так и его работа также гарантируется только при стандартной скорости в 100 кГц, что явно прописано. Далее меня несколько удивило наличие расчетов нагрузочных резисторов для быстрого режима, помимо стандартного, но, видимо, этот раздел просто кочует по документациям на микросхемы, имеющие отношение к I2C и данный справочный раздел нам ничего не обещает. Несколько напоминает анекдот о рыбе и блохах, но не смертельно.
А вот дальше мой взор упал на страницу 12, где начинается описание особенностей функционирования. И обнаруживаем забавные формулировки «Байт данных сопровождается сигналом подтверждения, выдаваемым прибором. Если другие байты данных передаются мастером, сопровождаемые подтверждением, они игнорируются прибором. Данные выводятся, только если полное количество байт принято и подтверждено.» — если я и ошибся с переводом, то не сильно. Звучит несколько непонятно — сколько байт составят полное количество и что будет, если они не будут выданы мастером? К счастью, на странице 13 есть временная диаграмма, которая призвана внести ясность, поскольку одна хорошая времянка заменит страницы описания. Но не в данном случае — во-первых, видно, что последующий байт не игнорируется, а как минимум подтверждается прибором, на что как бы намекали в тексте. Далее мы видим, что данные во втором байте явно имеют отношение к выходным, поскольку биты в нем имеют то же обозначение, как и в первом, а именно Р0-Р7. В третьих, именно после приема второго байта данные выводятся на параллельный порт, который почему то обозначен как В, а не Р, как было все предыдущее описание. Из непонятных соображений вместе с выходными данными меняется значение на порту А, который предназначен для задания переменной части адреса прибора. Особенно радует сигнал прерывания, который выставляется в конце цикла и видимо, остается высоким навсегда, до снятия питания с устройства. И теперь задаем вопрос — что это за неведомая фигня, другого определения для увиденного у меня нет (впрочем, есть, это — «трэш, угар и содомия»). Зачем нам второй байт, откуда он вообще взялся, если у нас один выходной порт, что в нем должно быть, почему в первом байте 5й разряд обязательно должен быть равен 1, что произойдет, если второй байт не прислать и много других интересных вопросов.
Сразу скажу, что у меня нет ответов на эти вопросы, я нахожусь на зыбкой почве догадок и предположений, и не понимаю, зачем уважаемой фирме потребовалось меня туда загнать. Я вижу ряд противоречивых утверждений, что заставляет вспомнить известный анекдот про трех черепашек (или муравьев), из которых не все придерживаются истины. Попытаемся разгадать данную шараду, хотя мы делать этого вовсе не обязаны, как говорил мой первый начальник после института: «Когда ты в рабочее время делаешь то, за что тебе не платят, ты не делаешь того, за что получаешь зарплату», но я пишу данный пост в нерабочее время, так что простим мне такое отвлечение от работы.
Первый вариант, лично для меня более привлекательный — на самом деле микросхема функционирует ровно так же, как ее подружка из NXP, а все непонятки возникли из за криворуких технических писателей, которые ошибочно оттранслировали на данный прибор времянку с его 16-разрядного собрата pcf8575, тем более что к ней тоже вопросы есть и точно такие же. Тогда приборы разных фирм совместимы и все хорошо, надо только пользоваться правильным описанием а за неправильное скажем фирме TI дружное «ФУ».
Второй вариант хуже, поскольку мы должны поверить, что прибор функционирует в соответствии с времянкой и как бы в соответствии с описанием, если это можно так назвать, и для записи одного байта на выходной порт мы должны подать два, причем второй будет использоваться как маркер конца передачи. Единственным объяснением подобного изысканного решения может быть желание обеспечить совместимость с вышеупомянутым 16-разрядным прибором, но, как говорится, «сказав А, нужно говорить и остальные буквы алфавита», есть же и 24разрядная версия и 104-разрядная и приборы с внутренними регистрами, так давайте делать единый протокол на все случаи жизни и для записи одного байта будем передавать штук 20, а лучше 25, причем 24 из них будут пустыми и предназначены для совместимости с будущими версиями, зачем останавливаться на достигнутом. При этом постулат о криворукости технических писателей по-прежнему остается в силе, поскольку описания функционирования на 8 и 16 разрядные версии очевидно расходятся, причем на 16 оно намного более вменяемое, хотя и не идеальное, а на 8 просто ужасное.
Чем этот вариант хуже первого, кроме того, что разработчики приняли весьма неоптимальное решение ради сомнительных выгод (то есть я такое решение точно не принял бы, но, наверное, я не все знаю) — тем, что тогда существуют два прибора от разных фирм с одинаковым обозначением, одинаковым назначением, одинаковой раскладкой ножек и при этом они функционируют по различным алгоритмам. А я и не знал, что ТАК можно. Чисто теоретически можно написать алгоритм, который будет верно функционировать на обоих вариантах приборов, оставим это в качестве упражнения для пытливого читателя, но костыль, даже сколь угодно крепкий, никогда не заменит живую ногу. Мои рекомендации в этом случае — просто забыть о существовании приборов от TI и пользоваться правильными приборами и правильным описанием, кстати, исходя из того, что на али торгуют исключительно NXP, эта рекомендация несколько запоздала.
Теперь, когда документация от фирмы TI «еще на первом листе было неразличимо смешано с пищей для воробьев и уже было забыто, ибо не этим незначительным объектом могла интересоваться пробужденная мысль академика», можно переходить к разбору собственно библиотеки, постараемся понять, что там можно улучшить с целью ускорения функционирования. Для начала определим главное направление оптимизации — поскольку на любой скорости, что 100, что 400, что 640, что 800, что 1000 кгц для передачи байта данных нам потребуется минимум 9 битовых интервалов, то именно время передачи является определяющим для времени работы функции в целом. Даже при частоте интерфейса в 1мГц за время передачи одной байтовой посылки можно выполнить 8*9=72 команды процессора на частоте 8МГц, поэтому практически любое усложнение программы, приводящее к уменьшению количества передаваемых по интерфейсу байтов, будет вполне оправданно. Тут есть одно соображение, уточняющее подобную позицию, но об этом позже.
Открываем исходный текст, пробираемся через множество функций-оболочек (складывается ощущение, что зарплата программистов зависит от количества сигнатур функций, нег?). Например, публичная функция init вызывает приватную функцию init_private (и больше ничего не делает), а та в свою очередь вызывает публичный метод begin — не то, чтобы я понял, зачем так делают но, наверное, я не все знаю. Запись данных осуществляет функцией write, а команд — command, которые являются оболочками для метода send, который вызывает функцию write4bits, которая вызывает expanderWrite и pulseEnable, который использует expanderWrite, которая вызывает printIIc, которая является подстановкой для метода write класса TwoWire для работы собственно с интерфейсом, исходный код которого лежит (это очевидное решение, не правда ли) в файле Wire. Внимательно рассматриваем функцию expanderWrite и обнаруживаем именно то, что мы ожидали — каждый байт данных передается в сопровождении собственного старта-бита, команды адресации и стоп-бита, то есть для передачи 8 бит информации используется 1+9+9+1=20 бит канального уровня, что явно не является оптимальным решением.
Кстати, одно соображение по стилю программирования — когда я осваивал язык С, в моде было то, что сейчас называют старый стиль (без сигнатур функций), причем вырос такой стиль из Фортрана, и на мой взгляд, такой подход имеет несомненное преимущество, а именно заставлял располагать функции в порядке возрастания сложности, поскольку иначе код просто не компилировался. А в конце файла вполне естественно было найти функцию main, что делало такой подход логически завершенным. Поэтому когда Вы рассматривали реализацию, естественно было искать код функций, используемых в данной, выше по тексту и на самом верху располагались базовые функции (в данном случае это функция вывода), которые и определяли эффективность библиотеки. Я не призываю отказаться от использования сигнатур вообще, вряд ли такой призыв возымеет действие, но даже при наличии сигнатур никто не заставляет Вас располагать реализации функций в произвольном порядке, так зачем делать плохо, когда можно сделать хорошо и причем бесплатно, а именно так (плохо) и сделано в рассматриваемой библиотеке.
И еще одно интересное обстоятельство, связанное с сигнатурами функций в среде Ардуино. Если Вы забудете написать прототип функции, то рискуете увидеть весьма странное сообщение от компилятора об ошибке в строке, в которой совершенно очевидно данной ошибки нет. Дело в том, что среда требует наличия прототипов функций, и если Вы его не задали, препроцессор не выдаст Вам сообщение об ошибке (это было бы слишком просто и это не Ардуино стиль), он сформирует сигнатуру сам, причем расположит ее в самом начале программы, сдвинув нумерацию строк в сообщениях об ошибке. А сообщение об ошибке Вам гарантируется, если в функции используются пользовательские типы, так как сигнатура расположена до их определения. Необычайно удобно, не правда ли, вспоминается старая шутка «Есть три способа что-либо сделать — правильный, неправильный и способ военно-морского флота».
Возвратимся к реализации передачи — мы уже видим способ улучшить быстродействие в 20/9=2 с небольшим раза за счет того, что не будет каждый байт данных сопровождать байтом адресации, конечно это оценка сверху и точный расчет покажет 6*20/(11+6*9)=120/65~1.8 раза, но это только начало.
Для продолжения нам придется обратиться к описанию интерфейса, подключенному к нашему прибору с другой стороны и рассмотреть интерфейс с экраном. Он может содержать 4 либо 8 разрядов данных (в рассматриваемом случае 4) и сигналы сопровождения обмена, из которых важным для нас является сигнал EN, который представляет собой строб подтверждения операции. Поскольку используются только 4 разряда данных, передача байта информации потребует передачи двух нибблов, что можно было бы исключить за счет повышения разрядности, но не с данной микросхемой, поскольку потребовало бы как минимум 8+1=9 выходных портов, а у нас их только 8, но об этом чуть позже. Для учета необходимости формирования строба, в рассматриваемой библиотеке сначала устанавливаются данные а затем формируется передний, а потом и задний фронт импульса записи при стабильных данных, что требует передачи трех байт по интерфейсу. Между тем, внимательное прочтение документации на интерфейс дисплея показывает, что он требует стабильных данных только на переднем фронте, поэтому формирование заднего фронта может быть совмещено с формированием следующего фрагмента данных, единственное обстоятельство, которое следует иметь в виду, это то, что хорошо бы сбросить строб в низкое состояние по окончанию сессии, хотя это и необязательно.
Что мы имеем в результате предложенных, на мой взгляд, очевидных изменений — требуемое время передачи на один байт, пересланный в оконечное устройство, уменьшилось с 120 битовых интервалов до 1+9+4*9+(+9)+1=47(56 в случае сброса строба) битовых интервала, итого рост быстродействия более, чем в два раза при, еще раз подчеркну, совершенно очевидных изменениях. Почему это не было сделано автором рассматриваемой библиотеки — скорее всего потому, что он использовал предыдущее решение с непосредственным подключением к дисплею и не посчитал необходимым его адаптировать под изменившиеся условия, а зря.
Тем не менее, обратим внимание на то, что даже при реализации взаимодействия, как это было в исходном коде, скорость передачи должна была составить 100/120=0.83 килобайт в секунду (1.2 мсек на передаваемый байт), что вполне сравнимо со скоростью передачи данных по последовательному порту 9.600/11=0.87 килобайта в секунду и должна быть вполне достаточной для перерисовки экрана 16*2=32 символа без видимой задержки. Между тем в исходном посте утверждается, что время передачи байта составляло 1.5 мсек, что несколько (на 25%) меньше ожидаемого. Значит, исследования надлежит продолжать.
И точно, рассматриваем еще раз функцию PulseEnable и обнаруживаем в ней задержки после формирования активного уровня строба в 1 мксек и после снятия строба в 50 мксек. Необходимость данных задержек можно объяснить техническими требованиями подключаемого дисплея, и они были вполне уместны в библиотеке с прямым взаимодействием через параллельный интерфейс, но в данном конкретном случае по крайней мере первая из них совершенно избыточна, учитывая, что следующая команда начнется с фазы адресации, которая займет минимум 1+9=10 тактов синхронизации и, соответственно, 10*1=10 мксек при недостижимой частоте в 1МГц, в течение которой подаваемые на дисплей сигналы гарантированно изменяться не будут. А вот со второй задержкой несколько сложнее — при частоте 100кГц гарантированная задержка за счет байта адреса составит 10*10=100мксек, а вот при частоте 400 кГц, которая вполне возможна с другим, более быстродействующим расширителем интерфейса, будет иметь значение только 2.5*10=25 мксек, но нам надлежит учесть, что изменение входных данных и снятие строба на входе экрана состоится только после приема еще одного байта интерфейса и у нас есть гарантированные 50 мксек. Значит, мы можем убрать обе задержки, что позволит сэкономить 2*50=100 мксек на передаче каждого байта данных.
Итак, подведем итоги — мы имели версию библиотеки с требуемым расчетным временем передачи для одного байта данных, которое составляло 2полубайта * 3стадии = 6передаваемых байт * (1+9+9+1)тактов = 120тактов * 10мксек/такт = 1200мксек + 100мксек = 1300мксек, что хорошо коррелирует с наблюдаемыми 1500 микросекундами, а превратили ее в версию с необходимым временем 2полубайта * 2 стадии = 4передаваемых байта * 9тактов/байт = 36тактов+(1+9+1)тактов = 47тактов * 10 мксек/такт = 470мксек, что в 2.6 раза быстрее.
Теоретическая часть закончилась (все, что можно было сделать анализом исходного текста, завершено), пора проверить наши изыскания практикой (я, в определенной степени, марксист и согласен, что «Практика — критерий истины», и, хотя к математике данная фраза не относится, к программированию она вполне применима). Настало время загрузить исходный и модифицированный код в плату Ардуино и осциллографом посмотреть на получившиеся временные параметры сигналов взаимодействия (недавно в документации на микросхему от фирмы, к которой я отношусь, наверное, слишком требовательно, было предложено очаровательное определение «временные параметры переменного тока», я несколько старомоден для принятия подобной терминологии).
Поскольку оригинальной платы у меня нет, надо искать выход, и им стало применение совершенно очаровательной программы онлайн моделирования circuit.io, настоятельно ее рекомендую, даже тем, у кого есть платы Ардуино. Такой вариант лучше, чем отладка на живой плате, поскольку данная программа намного быстрее делает прошивку из исходного кода, в среде отладки можно ставить точки останова, осуществлять пошаговую отладку и даже просматривать значения переменных программы. Те, кто работал в оригинальной среде Ардуино, поймут, чем я тут восхищаюсь, остальные могут недоуменно пожать плечами. Конечно, и к этой программе у меня есть претензии (в частности, к осциллографу в ее составе), но данный пост посвящен описанию недостатков совсем других вещей.
И вот тут нас подстерегает непонятная засада (не первая на пути нашего исследования, но вдвойне неприятная в силу своей неожиданности), а именно — быстродействие откорректированного модуля оказывается несколько меньше ожидаемого, поскольку имеют место задержки между фрагментами сообщения, то есть после старт-бита, после байта команды и после каждого байта данных, величиной около 40 мксек, что увеличивает общее время передачи почти на 40%. Прогон на эмуляторе исходной программы показывает такие же дополнительные задержки.
Начинаем разбираться с происходящим, для начала просматриваем документацию на МК, применяемый в плате, а именно ATMega32, в части, касающейся интерфейса TWI. Должен сразу же заявить, что в общем к документации у меня нет претензий, все описано ясно, четко и подробно, не то, что у некоторых. Но, тем не менее, на странице 290 в таблице 119 при определении динамически характеристик интерфейса мы наблюдаем несколько странное явление, а именно — для времени установления и удержания сигналов при формировании стартового импульса указаны только минимальные значения, а для максимальных поставлен прочерк. Такое сочетание требований совершенно естественно при описании входных сигналов, но в данном случае у нас есть и выходной формирователь, а для него оставить максимальные значения неопределенными несколько странно. Более того, требование к минимальному времени установления задано раздельно для частоты интерфейса, не превышающей 100кГц (составляет 4мксек, что хорошо коррелирует с половиной длительности тактового интервала), и для частоты, превышающей 100 кГц (составляет 0.6мксек, что явно представляет собой магическое число и от частоты вообще не зависит). Если бы мне дали задание спроектировать интерфейс I2C (не будем делать вид, будто бы аббревиатура TWI ввела нас в заблуждение), отвечающий подобным требованиям к временным характеристикам, то я был бы весьма озадачен двумя вопросами — какого черта делать именно так и, самое главное, как сделать именно так, задачка нетривиальная.
Документация явно нам ничего не подсказала, раз прочитать не удалось, придется экспериментировать дальше. Выдвигаю гипотезу, что флаг готовности к приему очередной команды взводится с задержкой относительно реальной готовности, но маленькая тестовая программа опровергает ее — выдача с опросом готовности производится без добавочной задержки сколь-нибудь различимого значения.
Тогда выдвигается новая гипотеза — флаг прерывания взводится с задержкой относительно реальной готовности интерфейса либо обработка прерывания требует значительного времени. Для проверки пишу тестовую программу, которая в процессе ожидания завершения работы функции передачи по прерываниям выводит на дополнительный пин значение флага готовности интерфейса, а в прерывании этот пин сразу же сбрасывается. Ожидаю увидеть на этом пине импульсы длительностью 40мксек, но не вижу вообще никаких импульсов, чистый ноль в процессе передачи данных. В общем то, так и должно быть, если прерывания обрабатываются в момент возникновения, но откуда тогда задержка?
И неожиданно обнаруживаю, что задержка исчезла и байт команды передается сразу же по окончанию старт-бита и все стало хорошо. Убираю трансляцию бита готовности на дополнительный пин и задержка возникает снова. Выясняется, если в процессе ожидания завершения передачи провести чтение регистра состояний интерфейса, то задержка исчезает полностью, обращение к другим регистрам интерфейса действия не имеет. На редкость непонятное явление, но разнообразные эксперименты подтверждают, что это действительно так. В принципе, нетрудно спроектировать схему, которая обеспечит такое поведение флага прерывания, но загадкой остается смысл подобных экзерсисов.
Возможных объяснений данному факту лично я вижу два: первый вариант состоит в том, что имеет место ошибка в программе эмуляции, которая и приводит к подобному поведению интерфейса. Если это так, то я готов простить данную ошибку, поскольку адекватное повторение в программе реальных микросхем (и их фрагментов, как в данном случае) есть задача весьма сложная и часто принципиально не решаемая, иначе мы бы давно отказались от макетных плат. Но лично я к данной версии не склоняюсь, поскольку не вижу, как такую ошибку можно легко сделать в тексте программы модели, разве что использовалось поведенческое описание на некоем языке высокого уровня, но и там сложно случайно создать подобное.
Вышеизложенные соображения позволяют выдвинуть и вторую гипотезу — программа адекватно описывает прибор и он действительно так функционирует. В этом случае возможная ошибка уходит в недра микросхемы, а там намного больше возможностей, и помимо описания функционирования с дефектами появляются и тонкие физические эффекты, которые могли привести к данному результату. В этом случае становится непонятным, как подобный дефект был обнаружен и исследован до уровня модели и почему о нем не говорится в списке ошибок.
Ну и остается третья гипотеза — такое поведение было запроектировано и должным образом описано во внутренней документации, которой пользовались разработчики программы моделирования. Тогда все становится на свои места, и остаются только два вопроса — зачем так сделано (какие преимущества подобная реализация предоставляет, недостаток ее очевиден — снижение реального быстродействия) и почему данная особенность не отражена во внешней документации.
Для определения верности первой гипотезы достаточно прогнать исходную (либо модифицированную) программу на реальной плате и посмотреть осциллографом задержки, на модели эффект имел место в варианте плат Uno и Micro. Поскольку, как я уже заявлял ранее, у меня подобных плат нет, проведение натурных экспериментов оставляю на долю пытливого читателя, надеюсь, результатами поделитесь в комментариях. Для определения верности второй либо третьей гипотезы необходим доступ к внутренней документации фирмы который лично мне (видимо, по недоразумению) предоставлен не был. Имеющиеся (в моем распоряжении) описания ошибок относительно TWI утверждают о наличии ошибки слишком быстрой выдачи первого бита в подчиненном режиме и необходимости введения задержки между записью в регистр данных и запуском передачи, может быть, в попытке пофиксить этот баг в железе и внесли наблюдаемую задержку.
Далее здесь должно быть дальнейшее рассмотрение правильной реализации данной библиотеки конктретно и библиотек с изменяемым поведением вообще, и я начал эту часть писать, но и так получилось несколько многовато для пятничного прочтения, так что это будет попозже.
Ну и в заключение о том, о чем собственно пост — о тенденции, а она угрожающая. Итак:
— первые) проектируют непонятно какие микросхемы (надеюсь, что это ложное утверждение),
— вторые) пишут непонятно какие описания к ним (и это однозначно),
— третьи) пишут непонятно какие библиотеки с видимым пренебрежением к читателю и здравому смыслу,
— четвертые) неспособны понять описания микросхем и пишут непонятно какие программы поверх библиотек,
— пятые) непонятно как реализуют интерфейсы в МК,
— шестые) непонятно как описывают данные интерфейсы в документации на МК,
…
— хх-надцатые) непонятно зачем в очередной раз меняют тэги в редакторе Хабра и тем самым вбивают гвоздь в крышку гроба современной электроники,
и лишь я один) на белом коне среди разрухи и хаоса несу свет просвещения, но понимаю всю тщетность своих усилий ввиду превосходящих сил противника.
Если откинуть в сторону ерничество, конечно, «раньше солнце светило ярче и трава была заленее», но я не припомню подобных расследований, которые мне приходилось бы проводить 20 лет назад, неужели все стало настолько хуже?
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Действительно ли упал уровень проработки решений в области электроники
17.39% Нет, не упал, он по прежнему высок, это просто синдром «стареющего инженера»4
17.39% Да, упал, но незначительно и это неизбежно в силу возросшей сложности изделий4
30.43% Да, упал, в силу того, что стало больше малоквалифицированных специалистов7
21.74% Да, упал, и существенно, это следствие отмены руководящей роли КПСС5
13.04% А он никогда и не был высоким, это просто сидром «стареющего инженера»3
Проголосовали 23 пользователя. Воздержались 16 пользователей.