Micro-UART для МикроКонтроллера (ATtiny13A)

    Рад приветствовать вновь всех посетителей этого славного места в интернете, чтобы привнести еще один скромный вклад.

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


    Речь пойдет о программной реализации UART, для микроконтроллеров AVR компании Atmel, интеллектуальной собственностью которой с некоторых пор владеет компания Microchip.

    Итак, следуя традиции, присказка


    Мысль в голове на тему этой статьи закралась давно, благодаря одному спору в комментариях, что, мол: программный UART для микроконтроллера, работающего от внутреннего генератора, очень нестабильный, и на нем нельзя получить высокой точности на скоростях выше 9600 бод, ввиду сильного дрейфа задающей частоты этого самого внутреннего генератора.
    И вот однажды, мне вновь захотелось проверить этот тезис (к сожалению я не смог найти ни статьи, где это обсуждалось, ни обсуждения. Хотя я уверен, что это происходило здесь, на Хабре), узнать:

    • На какую величину я смогу сократить погрешность;
    • На какой максимальной скорости передачи устройство сможет общаться;
    • Смогу ли я сжать код, используя все свои знания.

    Таким образом, начальный смысл этой работы был скорее — спортивным интересом.
    Но когда были получены первые результаты, я решил продемонстрировать начальный размер кода на канале Анархическая электроника мессенжера Telegram, и неожиданно тема начала резонировать. К обсуждению начали подключаться новые люди, и скоро спор вернулся в привычное русло, пользователь канала @RT_1_98 — очень яростно настаивал на несостоятельности этой работы (за что ему отдельное спасибо, он меня только распалил сильнее — доказать обратное ;) ) Тем временем, один из администраторов канала — @Byte_kgd, предложил не останавливаться на достигнутом, а довести работу до логического завершения: к передатчику написать еще и приёмник!

    И вызов был принят…

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

    Конечно же я сел писать в своей любимой IDE — Atmel Studio. В первоначальном варианте код тела функции (на инлайновом ассемблере) занял около 90 байт (45 команд), благодаря которой я выставил константы задержек, протестировав выполнение в симуляторе Proteus.

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

    Немного поразмыслив, я переписал функцию для отправки строк, оканчивающихся нулём, что позволило отправлять заранее подготовленный буфер определенного размера директивой #define, а кроме того, удалось сократить размер кода до 76 байт (38 команд)! В этот момент я и решился показать результаты тестов в Telegram, после чего было принято решение написать и приёмник.

    Собирался с мыслями я долго...


    Не давала покоя одна главная мысль: раз уж я решил отказаться от прерываний таймера в передатчике, было бы неплохо построить на этом же принципе и приёмник… но как?!
    И я решился на отчаянный шаг: написать обработчик прерывания таким образом, чтобы весь приём происходил в один вызов.

    Да, это плохая практика, и да — это непристойное поведение! Но во-первых, объемы памяти микроконтроллера не позволяют хранить большой буфер, это накладно для основного кода, а во-вторых, я решил написать обработчик так, чтобы он умел обрабатывать:

    • Переполнение буфера (реализовано выходом из прерывания при достижении границы);
    • Разрыв линии приема (тестовая эмуляция в Proteus, показала полное зависание, в результате чего было принято решение о контроле такого состояния);
    • И, собственно, штатное поведение (с заполнением флага успешного приёма данных);

    Когда написание кода прерывания было завершено, я провел тесты на стабильность приёма/передачи, построив такую модель в Proteus:


    Один микроконтроллер выполнял передачу заранее заданного буфера с тестовым содержимым:

    char Rx_Buf[Buf_Size]={'T','e','s','t','0','1','2','3','4','5',10,13,0};

    А второй тем временем, приняв его, сравнивал с эталонным, и в случае несоответствия, отправлял по линии данных во второй терминал сообщение:"Error!", после чего запустил симуляцию и лег спать…



    … На утро


    когда я проснулся меня ждало разочарование: терминал был усыпан нежелательными сообщениями об ошибках… Но когда я их посчитал, оказалось все не так плохо: за 9 часов работы, на скорости 57600 бод было 11 сообщений. Это значит что для 3888000 байт, произошло не более 143 ошибок (в случае полного несоответствия буфера).

    … Так это же успех!...


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

    Пересмотрев код приёмника и передатчика еще раз, я вновь сократил передатчик до 60 байт (30 команд), и оптимизировал приёмник.

    После чего передал код @Byte_kgd, который пламенно захотел поучаствовать в тестировании, с небольшим отступлением: в наличие имеется только ATtiny2313.

    И он мне злорадно и пафосно сообщил: "… сафсем ниработаит! ваапще!..."


    Вот так фиаско, братан...


    … Настал новый год, съеден весь оливье, выпито все шампанское, вот уже горят ёлки за окном.
    А код все не работает… не поддается, ни в какую!

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

    И тут Остапа понесло! (с) И. Ильф Е. Петров.

    Во-первых код передатчика был сокращен до 54-ёх байт (26 команд), во-вторых была устранена фатальная ошибка прерывания: несброшенный флаг (при этом Proteus переварил его, даже не сообщив в лог. И это уже вторая неточность в симуляции, которую я нашёл для МК ATtiny13A), в третьих и четвёртых — были устранены ошибки архитектурного характера, такие как: неправильный возврат флага, джитер, несоответствие длительностей передачи и приёма и т.д. Код приёмника был переписан с самосинхронизацией по STOP-биту, что несомненно повысило качество приёма.

    Кроме того, я наконец достал логический анализатор, расчехлил программатор, UART-преобразователь, и достав драгоценную ATtiny13A из своих закромов, решил ВПЕРВЫЕ за всё это время, проанализировать работоспособность в железе!

    Забегая вперед скажу: ЧУДО ПРОИЗОШЛО!

    Прошитая «тинька» показала в терминале «пинг», и ответила с улыбкой: Test012345.
    Использование логического анализатора показало расхождение в задержках в Proteus и на реальном железе. Благодаря чему, удалось унифицировать задержку одной константой для приёмника и для передатчика:



    А так же, позволило найти теоретический предел для максимальной скорости передачи в нормальных условиях:



    Я умышленно не стал выбирать другой скрин, показав ошибки приемника, который не смог работать на таких скоростях, ввиду того, что выполнение пролога и эпилога в заклинании прерывании, успевают пропустить стартовый бит. Однако в несколько измененной версии, я смог добиться уверенного приема на скорости 512 кбод, но ввиду объективных причин, решил оставить код таким как есть, с ограничением на приём/передачу в 250 кбод.

    Итог


    В результате проделанной, весьма изнурительной работы,

    • Получился самый компактный код в мире!
    • Не использующий немаскируемые (INT0 и INT1) прерывания
    • Не использующий прерывания таймера, освобождая все доступные для использования каналы ШИМ
    • Позволяющий гибко оперировать настройкой порта, выбирая нужные пины для линий отправки и приёма данных
    • Позволяющий стабильно передавать и принимать данные на скоростях от 9600 до 250000 бод
    • Передавать строки из флэш-памяти, благодаря соответствующей функции
    • Конвертировать и передавать данные в двоичном, hex-, и десятичном виде (для последнего реализованы две функции для передачи байт и слов данных), благодаря соответствующим функциям

    На этом изображении хорошо видно, что в демонстрационном примере использован весь доступный набор функций, при общем размере демонстрационного проекта — 920 байт занятой флэш-памяти. При правильной организации, можно использовать только нужные функции, что позволит сильно минимизировать затраты для основного проекта, позволяя при этом производить отладку микроконтроллера через UART. Код легко модифицируем под другие контроллеры (для примера — ATtiny85A), при этом позволяя увеличить размер буфера и количество используемых функций, не в ущерб производительности и работоспособности основной задачи. Код не выносился в библиотеку, для наглядности и удобства анализа и обучения. Контроль переполнения буфера было решено не реализовывать полноценно. Это значит что при переполнении флаг переполнения не устанавливается, вместо этого происходит выход из прерывания, а дальнейшее поведение определяет входящий поток.

    Таким образом микроконтроллер вновь уходит в прерывание и затирает принятый буфер новыми данными. Чтобы этого избежать, не нужно его кормить порциями данных, большей длины, чем определенный размер буфера. На этом было решено остановиться, после нескольких обсуждений, чтобы не раздувать код обработчика. Главное условие выполнено — микроконтроллер не зависает и продолжает работу в штатном режиме.

    Еще одним отступлением от традиций в этот раз, стало отсутствие выдержек кода в виде скриншотов. В прошлой статье в первом же комментарии был на эту тему гнев :)
    А раз так, то пусть код лежит на Githab'е, и как следствие, отображает всю красоту в не форматированном им виде:

    Проект версии 2.0, для Atmel Studio 7.0

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

    Желаю скорейшего наступления тепла, в ваши дома, на ваши улицы и в ваши сердца!
    Всем успехов, творений, свершений!

    Использование в коммерческих целях, перепродажа исходного кода, использование с целью наживы и любых корыстных целях, запрещено. Исходные тексты распространяются бесплатно как есть, в случае использования на других сайтах, либо в других источниках, указание автора и уведомление о размещении — обязательно!
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 33
    • +4

      А теперь нужно взять паяльный фен и жидкий азот с системой подачи и посмотреть куда всё уплывёт при нагреве до максимальной (по datasheet’у) и охлаждении до минимальной температур, а также при разных номиналах питания. Судя по datasheet’у, мало того, что на заводе калибруют с точностью до 10 % (пункт 18.4.1) — до 2 % можете закалиброваться сами. Так ещё и частота сильно плывёт в зависимости от температуры, на те же 10 % примерно (пункт 19.12). И, к тому же, datasheet может и врать, иногда сильно.

      • +1
        Да, этот предмет споров и не дает спокойствия противникам. Могу Вас заверить, такие тесты проводились мной в сторону нагрева. И действительно частота плывет, и правда, что обмен «сыпется», но не на 19200, и даже не на 57600, а там, где стартовый бит почти совпадает с окончанием пролога. У таких скоростей, при такой частоте чипа просто нет шансов остаться в окне передачи. При этом на низких скоростях передачи, отклонение даже на 30% не вызовет серьезных проблем, ввиду допусков самого UART. И скорей всего, Вы не заметили слово — самосинхронизация. Последняя написанная мной версия кода (не входящая в планы), делает этот процесс прямо во время передачи, на границах бит 0->1 и 1->0.
        • 0

          Так про тестирование при температуре хорошо бы в статье написать. И там слово «самосинхронизация», хоть и есть, но не поясняется. Где она в коде я не вижу.

          • +1
            Согласен, объективное замечание. Я много чего хотел написать, в том числе и про испытания. Но 3 часа ночи сыграли против меня в этот раз :)
            Поэтому не буду раздувать статью дальше, пусть она останется стихотворением, написанным водой на горячем асфальте, а Вам отвечу здесь:
            Грел строительным феном прямо на мини-борде почти до 100 градусов. Больше не рискнул, побоявшись оплавления ABS (температура стеклования 120-140).
            При такой температуре передача на 57600 работает без ошибок, 250к — сыпется. Промежуточные не измерял из-за затрудненного процесса прошивки (MiniPro 866A), нужно передергивать чип из борды в программатор и обратно. В перспективе если откопаю USBAsp, который забросил в дальний угол, организую стендик и выполню все покрытие диапазона. Отпишусь.
            На счет самосинхронизации: при задержке в стоповом бите, тестируется линия на появление низкого уровня. Если событие произошло — безусловный переход на приём следующего символа, в результате чего, программа оказывается на старте уже через два такта после начала низкого уровня. Дальнейший приём идет с запасом по дрейфу, почти двух-кратным.
            • 0

              Понятно. Я эту часть видел, но от явления, названного «самосинхронизации», я ожидал либо чего‐то вроде изменения UART_DELAY (которая у вас сейчас константа) в зависимости от значения счётчика или же чего‐то вроде изменения счётчика задержки на половину от UART_DELAY — оба варианта когда значение на RX меняется во время задержки.

              • 0
                Ну так эффект в точности такой, только корректируется не длительность а фаза.
      • 0
        Спасибо за статью. Хотелось бы, как минимум, видеть наречие 'насколько' слитно. Спасибо ещё раз.
        • –1
          Спасибо, что вы заметили только опечатку, и не обратили внимание на ошибки. Исправил.
          • 0
            Да ладно. Он «чтобы» правильно пишет!!! За такое всё можно простить.
          • +1
            Я правильно понимаю, что вы написали бессмысленный и беспощадный софтовый UART, который сжирает 100% ядра при приёме или передаче? Я, если что, не осуждаю, принцип «хотел, мог, сделал» — мощная движущая сила в области DIY.
            • 0
              Нет, Вы всё поняли неправильно. Я сделал инструмент, а каким образом им пользоваться решает каждый для себя сам. Менеджер отправки может написать любой, кому эта тема интересна и нужна.
              • 0

                Да, там busy-loop как в коде отправки, так и приёма. А приём сделан в прерывании, так что когда оно сработает, отправка будет приостановлена. В результате полного дуплекса не получается, и во время отправки или получения данных полезную работу можно делать разве что в обработчиках других прерываний. Обычный программный уарт, в общем. Разве что объём кода минимальный.

                • 0
                  Приём во время отправки запрещен, в остальном все верно, полнодуплекса нет, да и не нужен он тиньке. Ну и не стоит так категорично — «разве что...», минимальный объем кода, меньше мешает при отладке, например. Понятное дело, что это не для «продакшена», лично мне такая либа сэкономила бы уйму времени однажды. А чего уж говорить о новичках, которые только-только слезли с ардуинки, с целью минимизировать свои проекты. В общем я не претендую на роль первопроходца, я это сказал вначале. Повторюсь: это удобный инструмент, не более того. А цель разработки — спортивная.
                  • +1

                    Ну вообще да, для atTiny вполне. Там каждая нога и каждый байт памяти на счету. А тут можно на любую ногу повесить отправку и приём, удобно.

              • +2
                Проблема дрейфа обычно решается калибровкой по известным данным. Получаете байт вроде 0x55 или 0xAA, измеряете длину бита в тактах и пользуетесь. Уплыла частота, получаем бред? На уровне протокола приостанавливаем обмен, ждем пакета калибровочных данных, продолжаем обмен.
                Писал когда-то программный UART с таким функционалом, получилось около 140 байт флеша, 3 регистра и 1 байт памяти. Функции инициализации, отправки\получения байта и автокалибровки. Без таймеров\прерываний, само собой, на задержках. Отправка\получение побайтовые, но для строк приспособить будет несложно:
                github.com/sirgal/AVR-ASM-Software-UART/blob/master/software_uart.asm (использование в корыстных целях приветствуется, лицензия MIT)
                • 0
                  Вы лишний раз подтвердили, что мой код меньше, почти в три раза. Спасибо.
                  • 0

                    Когда я предложил использовать у себя «калибровку по известным данным» вроде 0x55 мне в ответ предложили гораздо более простую вещь: просто смотрите какова минимальная длина нуля. Учитывая, что UART передаёт нулевой бит, затем наименее значимый, вероятность поймать правильный результат уже на первом бите первого байта произвольной посылки примерно 50 %.

                    • 0
                      Это неверно! Я акцентировал проблему пролога прерывания: пока обрабатывается вход, мы уже в середине стартового бита. А если следом за ним передается нулевой бит, то мы никогда не поймем где граница. HurrTheDurr в этом отношении прав, однако подобные синхронизации требуют дополнительного кода как для клиента, так и для сервера. Выше я так же акцентировал, что синхронизацию можно производить по переходным состояниям линии на границах следования бит (0->1 или 1->0), выходя из цикла задержки заранее. И тогда, даже на высоких скоростях дрейф частоты не окажет влияния (в допустимых нормах).
                      • 0

                        В вашем случае для получение длины нуля нужна будет любая последовательность «101» в середине или «10stop» в конце посылки, что тоже не является невероятным, но конкретно у меня FPGA и тактируемый цикл (в LabVIEW — цикл, гарантированно исполняющияся за один такт), поэтому вашей проблемы с началом посылки нет.


                        В любом случае, по переходным состояниям можно только компенсировать дрейф, но во время приёма и даже без потери посылки. По калибровочному байту можно получить значение скорости, имея смутное представление о том, каким оно должно быть, но требуется как‐то этот байт послать вместо чего‐то более полезного (я когда предлагал вариант с калибровочным байтом просто предполагал просто начинать с него каждую посылку). А по длине нуля можно калиброваться на произвольных посылках, опять же, имея только смутное представление о скорости — но что‐то получать в это время можно только если вы именно компенсируете так дрейф.

                        • 0
                          Да, так и делают. В Ethernet каждый фрейм начинается с 7 байтов 10101010 подряд. Код это не особо раздувает, надо просто 7 раз подряд вызвать функцию и посчитать среднее.
                          • 0
                            Какое-то отклонение от темы, и сравнение «теплого» с «мягким».
                            Понятное дело, что ПЛИС позволяет многие вещи делать совсем иначе, но это никак не связанно с циклами (не понял что Вы под этим подразумеваете, если — такт, то в AVR они так же — гарантированы), как и вход в прерывание клиента, с началом посылки — сервера. Начало посылки в моём случае выполняется сразу (не считая нескольких тактов на инициализацию).
                            Касаемо калибровочных байт Вы, вероятно видите задачу связи между микроконтроллерами, хотя я настойчиво утверждаю и в статье, и в комментариях к ней, что функции приема и передачи написаны с учетом стандарта.
                            Моя задача: читать и принимать данные любым терминалом. А это уже не позволяет использовать калибровочные данные.
                            Если бы я реализовывал связь между микроконтроллерами, я бы точно отказался от UART, в пользу параллельных шин, на нестандартных частотах, с потоковым сжатием, коррекцией и проверкой валидности.
                            Но это другая задача, и для других контроллеров.
                            • +1

                              Тактируемые циклы — это особенность программирования ПЛИС на LabVIEW, их тело запускается в начале такта и один запуск тела цикла гарантированно длится не более одного такта. На VHDL я не пишу, но знаю, что первая часть (запуск в начале такта) будет выглядеть примерно как process (clk) begin if rising_edge(clk) then {body} end if; end process;, а вторая часть вроде выглядит как TIMESPEC TS_CLK = PERIOD "clk" 100 MHz HIGH 50%; и идёт в отдельный файл (не VHDL), что в совокупности совершенно не выглядит как цикл (и потому я и написал уточнение).


                              По поводу калибровочных байт/длины нуля: я вижу их применимость в любом случае, когда осуществляется связь между двумя устройствами, частота приёма/передачи хотя бы одного из которых может изменяться во время работы. Вариант с длиной нуля специально придуман именно для случая, когда нельзя использовать калибровочные данные. У меня там действительно связь между ПЛИС и микроконтроллером, причём шлёт данные в основном второй, а подстраивается под него первая. В вашей ситуации приём с нулём будет выглядеть примерно так:


                              1. Микроконтроллер определил, что ему нужно закалиброваться — например, по тому что он получает какой‐то бред. Или его только что включили, а создатель устройства решил не указывать конкретную частоту при создании прошивки. Важно, чтобы команды ему продолжали/начали слать, даже если другая сторона не понимает ответы микроконтроллера так же, как микроконтроллер не понимает команды.
                              2. Он входит в калибровочный режим и начинает подсчитывать, насколько длинные нули ему приходят, запоминая только минимальное значение.
                              3. Через некоторое время он выходит из калибровочного режима, вычисляет UART_DELAY на основе полученных данных (т.е. умножает длину нуля на коэффициент, предварительно проверив данные на адекватность) и далее использует его.

                              Требования к посылкам при этом минимальные: если во время «калибровочного режима» нужно, чтобы работал какой‐то ещё код, то «наличие последовательности 101 в посылке, в т.ч. подойдёт 10 и стоповый бит». Если не нужно, то ещё подойдёт посылка с единицей в наименее значимом бите: тогда можно не использовать прерывания. (Кстати, строка «стандарт» на момент написания комментария на всей странице встречается ровно два раза — все в том комментарии, на который я сейчас отвечаю.)


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


                              Про подстройку на основе длины нуля во время приёма: я подумал, как это будет выглядеть с программным UART и понял, что иметь такое в цикле задержки мне бы не понравилось. В целом примерно то же самое, что и с калибровочным режимом, только длина считается не во время калибровочного режима, а во время нормальной работы, а задержка вычисляется между приёмами.


                              PS: Чтобы исключить возможное недопонимание, привожу как я видел компенсацию дрейфа во время приёма по переходным состояниям: вместо nop’ов получение значения с порта, сравнение с сохранённым предыдущим, brne на команду вперёд и установка счётчика в UART_DELAY/2 − C (поправка на дополнительные затраты времени на установку счётчика).

                              • +1
                                Спасибо за столь развернутый ответ, это многое прояснило.
                                Стандартный протокол UART, не подразумевает автокалибровку по дополнительным входным последовательностям и работает на заданных частотах, поэтому я неправильно интерпретировал Ваши «нули».
                                Касаемо же этой подстройки по длительности периода бод на неизвестной частоте, идея хорошая и нужная, но опять же — жирно использовать ее для микроконтроллера с 1кб флеша и 64 байтами SRAM. В случае же режима корректировки фазы, код не сильно увеличивается, теряется возможность автоопределения скорости, но повышается точность приёма при дрейфе частоты. И этот вариант вполне самодостаточен для программной реализации UART, используемого для «ОТЛАДКИ» микроконтроллера.
                                Ситуация с подсчетами еще усложняется тем, что разные команды имеют разное количество циклов, в этой ситуации требуется унификация задержки, которую я в свою очередь развернул, чтобы сократить размер кода и получить возможность более точной подстройки.
                                Поэтому любая команда, будь то условный переход, или чтение линии порта, будет выполняться дольше NOP, и в случае получения значения с порта, сравнения, и условного перехода, это уже в ПЯТЬ раз больше, и получение неустранимого джитера в приёмнике.
                                Спасибо за Ваш интерес к теме и описанные варианты обработки, любые решения, даже неудачные позволяют приблизиться к одному — идеальному.
                                Эксперименты не заканчиваются, и как я сказал выше — я написал другой приемник, с автокоррекцией фазы, но меня не устраивают некоторые нюансы в нём. Если я устраню эти нюансы и удастся оптимизировать код, я форкну проект на гитхабе и отпишусь об изменениях.
                                А пока, будем считать это решение законченным и самодостаточным.
                                Спасибо еще раз!
                                • +1

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


                                  Спасибо за беседу, интересно пообщаться с человеком с иным взглядом на проблему: я на работе (пока?) не пишу программы для микроконтроллеров с жёсткой экономией памяти (и отсутствием отдельного UART блока), но специфика работы (тестирование микросхем на радиационную стойкость) предполагает, что внутренняя частота во время испытаний уплывёт с немалой вероятностью, иногда даже намного сильнее, чем по datasheet’у. Поэтому автокорректировка на одной из сторон видится довольно полезной вещью — и если использовать строго стандартные скорости, то этой стороной будет менее ограниченный микроконтроллер. (Правда мы, если частота действительно уплывёт за границы, указанные в datasheet’е, можем написать, что схема не прошла испытания. Впрочем, часто внутреннего генератора нет или его не предполагается использовать.)

                    • 0
                      Похвально. А чего код не написали просто чисто на ассемблере, а ещё прикрутили си? Там же проще было всё чистоганом сделать на асме.

                      У вас код под гну лицензией, она не запрещает использование кода в коммерческих проектах. И более того, я могу использовать ваш код в моём проекте и если кому-то понадобиться, то я могу предоставить ваш исходный код.
                      • 0
                        Для себя я так и делаю, после того как библиотека прошла тесты и показала полную работоспособность, а пишу так, потому что привычка с тех времен, когда еще не было окон и вкладок. Мне удобно перемещаться по листингу не переключаясь между окнами в маленьких проектах, и считаю, что новичкам так проще не запутаться.
                        По поводу коммерции — бесспорно, ведь не я придумал протокол обмена, здесь я имел ввиду сам код и его «несвободность». То есть продажу кода как своего личного, любого измененного приложения — как своего, либо в составе собственного продукта в качестве библиотеки, с нарушением авторских прав. Во всех остальных случаях, с соблюдением этикета, вы можете пользоваться так, как считаете нужным, с указанием источника, автора, и исходного кода.
                      • +1
                        Огромное спасибочки. Заказал тиней десяток и неплохо бы их использовать как мозг какого-нибудь простенького сетевого датчика, только без уарта его в «классическую» проводную шину втыкать накладно. А так задача сильно упростилась
                        • 0
                          Только не стоит забывать, что расстояния там не очень большие, возможно лучше использовать другие интерфейсы. Есть планы на счет реализации i2c и twi, но пока это размазано и туманно. Хотя есть заказ. В общем — думаю.
                        • +1
                          Только что искал какую-нибудь реализацию именно для tiny13. Синхронизм удивительный.
                          • +1
                            А как у данной программной реализации с помехоустойчивостью? Хорошие аппаратные реализации делают несколько проверок уровня входного сигнала в ожидаемой середине битового интервала, и затем мажоритарно делают вывод о наиболее вероятном значении бита. Начальная же синхронизация посылки по старт-биту в хороших аппаратных моделях вообще делается следующим образом: в состоянии idle каждый переход 1->0 инициирует свой таймер, вызывающий серию проверок уровня сигнала в середине ожидаемого стартового бита — то есть, каждый переход по сути порождает гипотезу об истинном времени начала передачи. Подтверждается или опровергается гипотеза мажоритарным выбором через половину интервала — при этом точкой синхронизации начала посылки считается время перехода 1->0, породившего наиболее раннюю гипотезу. При подтверждении гипотезы конечный автомат переходит из idle в busy, а более поздние гипотезы отбрасываются; при неподтверждении автомат остается в состоянии idle, и отбрасываются все гипотезы, устаревшие более чем на один битовый интервал.

                            Сравнить помехоустойчивость программных и аппаратных реализаций можно, добавив искусственные положительные и отрицательные иголки (кратковременные переходы 1->0->1 и 0->1->0) в программный генератор UART, и подключив на прием одновременно обе реализации
                            • 0
                              Вы говорите про помехоустойчивость передачи, или же приёмника? :)
                              Очевидно, что сэмплирование осуществляется в любой точке временного диапазона бита на конкретной частоте. Стартом (и началом отсчета) служит стартовый бит, в результате чего происходит вызов функции прерывания которая, в свою очередь выполняя пролог, учитывает текущее смещение к нулевому биту таким образом, чтобы обеспечить попадание в окно.
                              Не менее очевидно, что если в момент сэмплирования будет попадание в помеху, то результат будет записан безусловно. Однако для таких целей существует контроль по сумме и четности, которые в данной реализации не предусмотрены, но могут быть организованы с минимальными усилиями, силами разработчика.
                              Любые другие алгоритмы помехоустойчивости на столь низкой частоте МК, гарантированно ведут к снижению рабочего диапазона частот, раздувают код и количество задействованных ресурсов (речь о регистрах).
                              • 0
                                Я говорю про помехоустойчивость программной реализации приемника относительно более-менее стандартной аппаратной реализации (тактирование автомата с периодом в 16 раз меньше периода битового интервала, проверка и мажоритарный выбор в 7-8-9 периоде внутреннего генератора).
                                Обычно программные реализации достаточно наивны именно потому, что поддержка сложного конечного автомата в реальном времени сильно снижает скорость, и увеличивают долю ресурсов МК, а жесткие условия по помехам на столе или в макете практически не встречаются. Вот я и хотел узнать, у вас так же, или нет
                                • +1
                                  250000*(1+8+1)*16=40000000 Hz (40MHz)
                                  Как Вы себе представляете тактирование автомата на такой частоте, в чипе с частотой 9.6МГц?
                                  На мой взгляд, такая программная реализация — более чем наивна.

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

                            Самое читаемое